[mapguide-commits] r7689 - in sandbox/jng/http304: Common/MapGuideCommon Common/MapGuideCommon/Services Common/MapGuideCommon/System Server/src/Services/Tile Server/src/UnitTesting

svn_mapguide at osgeo.org svn_mapguide at osgeo.org
Mon Jul 15 07:41:16 PDT 2013


Author: jng
Date: 2013-07-15 07:41:16 -0700 (Mon, 15 Jul 2013)
New Revision: 7689

Added:
   sandbox/jng/http304/Common/MapGuideCommon/Services/Tile.cpp
   sandbox/jng/http304/Common/MapGuideCommon/Services/Tile.h
Modified:
   sandbox/jng/http304/Common/MapGuideCommon/MapGuideCommon.h
   sandbox/jng/http304/Common/MapGuideCommon/MapGuideCommon.vcxproj
   sandbox/jng/http304/Common/MapGuideCommon/MapGuideCommon.vcxproj.filters
   sandbox/jng/http304/Common/MapGuideCommon/MapGuideCommonBuild.cpp
   sandbox/jng/http304/Common/MapGuideCommon/Services/ProxyTileService.cpp
   sandbox/jng/http304/Common/MapGuideCommon/Services/ProxyTileService.h
   sandbox/jng/http304/Common/MapGuideCommon/Services/TileDefs.h
   sandbox/jng/http304/Common/MapGuideCommon/Services/TileService.h
   sandbox/jng/http304/Common/MapGuideCommon/System/MapGuideCommonClassId.h
   sandbox/jng/http304/Common/MapGuideCommon/System/MapGuideCommonFactory.cpp
   sandbox/jng/http304/Server/src/Services/Tile/ServerTileService.cpp
   sandbox/jng/http304/Server/src/Services/Tile/ServerTileService.h
   sandbox/jng/http304/Server/src/Services/Tile/ServerTileService.vcxproj
   sandbox/jng/http304/Server/src/Services/Tile/ServerTileService.vcxproj.filters
   sandbox/jng/http304/Server/src/Services/Tile/ServerTileServiceBuild.cpp
   sandbox/jng/http304/Server/src/Services/Tile/TileCache.cpp
   sandbox/jng/http304/Server/src/Services/Tile/TileCache.h
   sandbox/jng/http304/Server/src/Services/Tile/TileOperationFactory.cpp
   sandbox/jng/http304/Server/src/UnitTesting/TestTileService.cpp
   sandbox/jng/http304/Server/src/UnitTesting/TestTileService.h
Log:
First cut of APIs to support HTTP 304 cacheability:
 - A new class MgTile which contains the image (MgByteReader) and its creation date (MgDateTime)
 - New APIs to MgTileService
    - GetTileWithCreationDate to return MgTile that contains both image (MgByteReader) and creation date (MgDateTime). Existing GetTile implementation MgServerTileService re-routes to GetTileWithCreationDate and just returns the image component of the MgTile that is returned.
    - GetTileCreationDate to return the creation date of a given tile, or NULL if the tile does not exist.
 - Add new unit tests to exercise GetTileWithCreationDate and GetTileCreationDate

Modified: sandbox/jng/http304/Common/MapGuideCommon/MapGuideCommon.h
===================================================================
--- sandbox/jng/http304/Common/MapGuideCommon/MapGuideCommon.h	2013-07-15 09:54:15 UTC (rev 7688)
+++ sandbox/jng/http304/Common/MapGuideCommon/MapGuideCommon.h	2013-07-15 14:41:16 UTC (rev 7689)
@@ -144,6 +144,7 @@
 #include "Services/RenderingDefs.h"
 #include "Services/RenderingOptions.h"
 #include "Services/RenderingService.h"
+#include "Services/Tile.h"
 #include "Services/TileDefs.h"
 #include "Services/TileService.h"
 #include "Services/KmlService.h"

Modified: sandbox/jng/http304/Common/MapGuideCommon/MapGuideCommon.vcxproj
===================================================================
--- sandbox/jng/http304/Common/MapGuideCommon/MapGuideCommon.vcxproj	2013-07-15 09:54:15 UTC (rev 7688)
+++ sandbox/jng/http304/Common/MapGuideCommon/MapGuideCommon.vcxproj	2013-07-15 14:41:16 UTC (rev 7689)
@@ -876,6 +876,12 @@
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
     </ClCompile>
+    <ClCompile Include="Services\Tile.cpp">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+    </ClCompile>
     <ClCompile Include="Services\TileDefs.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
@@ -1053,6 +1059,7 @@
     <ClInclude Include="Services\SiteInfo.h" />
     <ClInclude Include="Services\SiteManager.h" />
     <ClInclude Include="Services\SqlResult.h" />
+    <ClInclude Include="Services\Tile.h" />
     <ClInclude Include="Services\TileDefs.h" />
     <ClInclude Include="Services\TileService.h" />
     <ClInclude Include="Services\UnitType.h" />

Modified: sandbox/jng/http304/Common/MapGuideCommon/MapGuideCommon.vcxproj.filters
===================================================================
--- sandbox/jng/http304/Common/MapGuideCommon/MapGuideCommon.vcxproj.filters	2013-07-15 09:54:15 UTC (rev 7688)
+++ sandbox/jng/http304/Common/MapGuideCommon/MapGuideCommon.vcxproj.filters	2013-07-15 14:41:16 UTC (rev 7689)
@@ -394,6 +394,9 @@
     <ClCompile Include="Util\TimerUtil.cpp">
       <Filter>Util</Filter>
     </ClCompile>
+    <ClCompile Include="Services\Tile.cpp">
+      <Filter>Services</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="MapLayer\Layer.h">
@@ -769,6 +772,9 @@
     <ClInclude Include="Util\TimerUtil.h">
       <Filter>Util</Filter>
     </ClInclude>
+    <ClInclude Include="Services\Tile.h">
+      <Filter>Services</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="MapGuideCommon.rc" />

Modified: sandbox/jng/http304/Common/MapGuideCommon/MapGuideCommonBuild.cpp
===================================================================
--- sandbox/jng/http304/Common/MapGuideCommon/MapGuideCommonBuild.cpp	2013-07-15 09:54:15 UTC (rev 7688)
+++ sandbox/jng/http304/Common/MapGuideCommon/MapGuideCommonBuild.cpp	2013-07-15 14:41:16 UTC (rev 7689)
@@ -125,6 +125,7 @@
 #include  "Services/SiteManager.cpp"
 #include  "Services/SiteInfo.cpp"
 #include  "Services/SiteConnection.cpp"
+#include  "Services/Tile.cpp"
 #include  "Services/TileDefs.cpp"
 #include  "Services/TileService.cpp"
 #include  "Services/ProfilingDefs.cpp"

Modified: sandbox/jng/http304/Common/MapGuideCommon/Services/ProxyTileService.cpp
===================================================================
--- sandbox/jng/http304/Common/MapGuideCommon/Services/ProxyTileService.cpp	2013-07-15 09:54:15 UTC (rev 7688)
+++ sandbox/jng/http304/Common/MapGuideCommon/Services/ProxyTileService.cpp	2013-07-15 14:41:16 UTC (rev 7689)
@@ -136,8 +136,134 @@
     return (MgByteReader*)cmd.GetReturnValue().val.m_obj;
 }
 
+//////////////////////////////////////////////////////////////////
+/// <summary>
+/// Returns the specified base map tile for the given map.  If a cached tile
+/// image exists it will return it, otherwise the tile is rendered and added
+/// to the cache.
+/// </summary>
+/// <param name="map">Input
+/// Map object containing current state of map.
+/// </param>
+/// <param name="baseMapLayerGroupName">Input
+/// Specifies the name of the baseMapLayerGroup for which to render the tile.
+/// <param name="tileColumn">Input
+/// Specifies the column index of the tile to return.
+/// </param>
+/// <param name="tileRow">Input
+/// Specifies the row index of the tile to return.
+/// </param>
+/// <returns>
+/// A byte reader containing the rendered tile image.
+/// </returns>
+MgTile* MgProxyTileService::GetTileWithCreationDate(
+    MgMap* map,
+    CREFSTRING baseMapLayerGroupName,
+    INT32 tileColumn,
+    INT32 tileRow)
+{
+    MgCommand cmd;
+    cmd.ExecuteCommand(m_connProp,                                      // Connection
+                        MgCommand::knObject,                            // Return type expected
+                        MgTileServiceOpId::GetTileWithCreationDate,                     // Command Code
+                        4,                                              // No of arguments
+                        Tile_Service,                                   // Service Id
+                        BUILD_VERSION(2,6,0),                           // Operation version
+                        MgCommand::knObject, map,                       // Argument#1
+                        MgCommand::knString, &baseMapLayerGroupName,    // Argument#2
+                        MgCommand::knInt32, tileColumn,                 // Argument#3
+                        MgCommand::knInt32, tileRow,                    // Argument#4
+                        MgCommand::knNone);                             // End of arguments
 
+    SetWarning(cmd.GetWarningObject());
+
+    return (MgTile*)cmd.GetReturnValue().val.m_obj;
+}
+
 /////////////////////////////////////////////////////////////////
+/// \brief
+/// Returns the specified base map tile for the given map.  If a cached tile
+/// image exists it will return it, otherwise the tile is rendered and added
+/// to the cache.
+MgTile* MgProxyTileService::GetTileWithCreationDate(
+    MgResourceIdentifier* mapDefinition,
+    CREFSTRING baseMapLayerGroupName,
+    INT32 tileColumn,
+    INT32 tileRow,
+    INT32 scaleIndex)
+{
+    MgCommand cmd;
+    cmd.ExecuteCommand(m_connProp,                                      // Connection
+                        MgCommand::knObject,                            // Return type expected
+                        MgTileServiceOpId::GetTileWithCreationDate,                     // Command Code
+                        5,                                              // No of arguments
+                        Tile_Service,                                   // Service Id
+                        BUILD_VERSION(2,6,0),                           // Operation version
+                        MgCommand::knObject, mapDefinition,             // Argument#1
+                        MgCommand::knString, &baseMapLayerGroupName,    // Argument#2
+                        MgCommand::knInt32, tileColumn,                 // Argument#3
+                        MgCommand::knInt32, tileRow,                    // Argument#4
+                        MgCommand::knInt32, scaleIndex,                 // Argument#5
+                        MgCommand::knNone);                             // End of arguments
+
+    SetWarning(cmd.GetWarningObject());
+
+    return (MgTile*)cmd.GetReturnValue().val.m_obj;
+}
+
+/// Returns the timestamp of when the tile for the specified map/group/row/col was generated. Returns NULL if no such tile exists
+///
+MgDateTime* MgProxyTileService::GetTileCreationDate(MgMap* map, 
+                                                    CREFSTRING baseMapLayerGroupName, 
+                                                    INT32 tileColumn, 
+                                                    INT32 tileRow)
+{
+    MgCommand cmd;
+    cmd.ExecuteCommand(m_connProp,                                      // Connection
+                        MgCommand::knObject,                            // Return type expected
+                        MgTileServiceOpId::GetTileCreationDate,                     // Command Code
+                        4,                                              // No of arguments
+                        Tile_Service,                                   // Service Id
+                        BUILD_VERSION(2,6,0),                           // Operation version
+                        MgCommand::knObject, map,                       // Argument#1
+                        MgCommand::knString, &baseMapLayerGroupName,    // Argument#2
+                        MgCommand::knInt32, tileColumn,                 // Argument#3
+                        MgCommand::knInt32, tileRow,                    // Argument#4
+                        MgCommand::knNone);                             // End of arguments
+
+    SetWarning(cmd.GetWarningObject());
+
+    return (MgDateTime*)cmd.GetReturnValue().val.m_obj;
+}
+
+/// Returns the timestamp of when the tile for the specified map/group/row/col/scale was generated. Returns NULL if no such tile exists
+///
+MgDateTime* MgProxyTileService::GetTileCreationDate(MgResourceIdentifier* mapDefinition, 
+                                                    CREFSTRING baseMapLayerGroupName, 
+                                                    INT32 tileColumn, 
+                                                    INT32 tileRow, 
+                                                    INT32 scaleIndex)
+{
+    MgCommand cmd;
+    cmd.ExecuteCommand(m_connProp,                                      // Connection
+                        MgCommand::knObject,                            // Return type expected
+                        MgTileServiceOpId::GetTileCreationDate,                     // Command Code
+                        5,                                              // No of arguments
+                        Tile_Service,                                   // Service Id
+                        BUILD_VERSION(2,6,0),                           // Operation version
+                        MgCommand::knObject, mapDefinition,             // Argument#1
+                        MgCommand::knString, &baseMapLayerGroupName,    // Argument#2
+                        MgCommand::knInt32, tileColumn,                 // Argument#3
+                        MgCommand::knInt32, tileRow,                    // Argument#4
+                        MgCommand::knInt32, scaleIndex,                 // Argument#5
+                        MgCommand::knNone);                             // End of arguments
+
+    SetWarning(cmd.GetWarningObject());
+
+    return (MgDateTime*)cmd.GetReturnValue().val.m_obj;
+}
+
+/////////////////////////////////////////////////////////////////
 /// <summary>
 /// Adds the specified base map tile to the cache.  If a tile image already
 /// exists at the specified location then it will be repleced.

Modified: sandbox/jng/http304/Common/MapGuideCommon/Services/ProxyTileService.h
===================================================================
--- sandbox/jng/http304/Common/MapGuideCommon/Services/ProxyTileService.h	2013-07-15 09:54:15 UTC (rev 7688)
+++ sandbox/jng/http304/Common/MapGuideCommon/Services/ProxyTileService.h	2013-07-15 14:41:16 UTC (rev 7689)
@@ -88,6 +88,74 @@
         INT32 tileRow,
         INT32 scaleIndex);
 
+    /////////////////////////////////////////////////////////////////
+    /// \brief
+    /// Returns the specified base map tile for the given map.  If a cached tile
+    /// image exists it will return it, otherwise the tile is rendered and added
+    /// to the cache.
+    ///
+    /// \param map
+    /// Input
+    /// Map object containing current state of map.
+    /// \param baseMapLayerGroupName
+    /// Input
+    /// Specifies the name of the baseMapLayerGroup for which to render the tile.
+    /// \param tileColumn
+    /// Input
+    /// Specifies the column index of the tile to return.
+    /// \param tileRow
+    /// Input
+    /// Specifies the row index of the tile to return.
+    ///
+    /// \return
+    /// A byte reader containing the rendered tile image.
+    ///
+    virtual MgTile* GetTileWithCreationDate(
+        MgMap* map,
+        CREFSTRING baseMapLayerGroupName,
+        INT32 tileColumn,
+        INT32 tileRow);
+
+    /////////////////////////////////////////////////////////////////
+    /// \brief
+    /// Returns the specified base map tile for the given map.  If a cached tile
+    /// image exists it will return it, otherwise the tile is rendered and added
+    /// to the cache.
+    ///
+    /// \param mapDefinition
+    /// Input
+    /// Resource identifier for the map definition
+    /// \param baseMapLayerGroupName
+    /// Input
+    /// Specifies the name of the baseMapLayerGroup for which to render the tile.
+    /// \param tileColumn
+    /// Input
+    /// Specifies the column index of the tile to return.
+    /// \param tileRow
+    /// Input
+    /// Specifies the row index of the tile to return.
+    /// \param scaleIndex
+    /// Input
+    /// Scale index to render
+    ///
+    /// \return
+    /// A byte reader containing the rendered tile image.
+    ///
+    virtual MgTile* GetTileWithCreationDate(
+        MgResourceIdentifier* mapDefinition,
+        CREFSTRING baseMapLayerGroupName,
+        INT32 tileColumn,
+        INT32 tileRow,
+        INT32 scaleIndex);
+
+    /// Returns the timestamp of when the tile for the specified map/group/row/col was generated. Returns NULL if no such tile exists
+    ///
+    virtual MgDateTime* GetTileCreationDate(MgMap* map, CREFSTRING baseMapLayerGroupName, INT32 tileColumn, INT32 tileRow);
+
+    /// Returns the timestamp of when the tile for the specified map/group/row/col/scale was generated. Returns NULL if no such tile exists
+    ///
+    virtual MgDateTime* GetTileCreationDate(MgResourceIdentifier* mapDefinition, CREFSTRING baseMapLayerGroupName, INT32 tileColumn, INT32 tileRow, INT32 scaleIndex);
+
     //////////////////////////////////////////////////////////////////
     /// \brief
     /// Clears the entire tile cache for the given map.  Tiles for all base

Added: sandbox/jng/http304/Common/MapGuideCommon/Services/Tile.cpp
===================================================================
--- sandbox/jng/http304/Common/MapGuideCommon/Services/Tile.cpp	                        (rev 0)
+++ sandbox/jng/http304/Common/MapGuideCommon/Services/Tile.cpp	2013-07-15 14:41:16 UTC (rev 7689)
@@ -0,0 +1,75 @@
+//
+//  Copyright (C) 2004-2013 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  This library is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+//  Lesser General Public License for more details.
+//
+//  You should have received a copy of the GNU Lesser General Public
+//  License along with this library; if not, write to the Free Software
+//  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+#include "Foundation.h"
+#include "MapGuideCommon.h"
+
+MG_IMPL_DYNCREATE(MgTile)
+
+MgTile::MgTile()
+{
+    m_image = NULL;
+    m_creationDate = NULL;
+}
+
+MgTile::MgTile(MgByteReader* image, MgDateTime* creationDate)
+{
+    m_image = SAFE_ADDREF(image);
+    m_creationDate = SAFE_ADDREF(creationDate);
+}
+
+MgTile::~MgTile()
+{
+    SAFE_RELEASE(m_image);
+    SAFE_RELEASE(m_creationDate);
+}
+
+void MgTile::SetImage(MgByteReader* image)
+{
+    m_image = SAFE_ADDREF(image);
+}
+
+void MgTile::SetCreationDate(MgDateTime* creationDate)
+{
+    m_creationDate = SAFE_ADDREF(creationDate);
+}
+
+INT32 MgTile::GetClassId()
+{
+    return m_cls_id;
+}
+
+MgByteReader* MgTile::GetImage()
+{
+    return SAFE_ADDREF(m_image);
+}
+
+MgDateTime* MgTile::GetCreationDate()
+{
+    return SAFE_ADDREF(m_creationDate);
+}
+
+void MgTile::Deserialize(MgStream* stream)
+{
+    m_image = (MgByteReader*)stream->GetObject();
+    m_creationDate = (MgDateTime*)stream->GetObject();
+}
+
+void MgTile::Serialize(MgStream* stream)
+{
+    stream->WriteObject(m_image);
+    stream->WriteObject(m_creationDate);
+}
\ No newline at end of file

Added: sandbox/jng/http304/Common/MapGuideCommon/Services/Tile.h
===================================================================
--- sandbox/jng/http304/Common/MapGuideCommon/Services/Tile.h	                        (rev 0)
+++ sandbox/jng/http304/Common/MapGuideCommon/Services/Tile.h	2013-07-15 14:41:16 UTC (rev 7689)
@@ -0,0 +1,64 @@
+//
+//  Copyright (C) 2004-2013 by Autodesk, Inc.
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of version 2.1 of the GNU Lesser
+//  General Public License as published by the Free Software Foundation.
+//
+//  This library is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+//  Lesser General Public License for more details.
+//
+//  You should have received a copy of the GNU Lesser General Public
+//  License along with this library; if not, write to the Free Software
+//  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+#ifndef MG_TILE_H
+#define MG_TILE_H
+
+/// \defgroup MgTileService MgTileService
+/// \ingroup Tile_Service_Module
+/// \{
+
+class MG_MAPGUIDE_API MgTile : public MgSerializable
+{
+    MG_DECL_DYNCREATE();
+    DECLARE_CLASSNAME(MgTile)
+
+INTERNAL_API:
+    MgTile();
+    MgTile(MgByteReader* image, MgDateTime* creationDate);
+    ~MgTile();
+
+    void SetImage(MgByteReader* image);
+    void SetCreationDate(MgDateTime* creationDate);
+
+    virtual INT32 GetClassId();
+
+PUBLISHED_API:
+    MgByteReader* GetImage();
+    MgDateTime* GetCreationDate();
+
+protected:
+
+    virtual void Dispose()
+    {
+        delete this;
+    }
+
+    void Deserialize(MgStream* stream);
+    void Serialize(MgStream* stream);
+
+private:
+    MgByteReader* m_image;
+    MgDateTime* m_creationDate;
+
+CLASS_ID:
+    static const INT32 m_cls_id = MapGuide_TileService_Tile;
+};
+
+/// \}
+
+#endif
\ No newline at end of file

Modified: sandbox/jng/http304/Common/MapGuideCommon/Services/TileDefs.h
===================================================================
--- sandbox/jng/http304/Common/MapGuideCommon/Services/TileDefs.h	2013-07-15 09:54:15 UTC (rev 7688)
+++ sandbox/jng/http304/Common/MapGuideCommon/Services/TileDefs.h	2013-07-15 14:41:16 UTC (rev 7689)
@@ -32,6 +32,8 @@
     static const int ClearCache = 0x1111E803;
     static const int GetDefaultTileSizeX  = 0x1111E804;
     static const int GetDefaultTileSizeY  = 0x1111E805;
+    static const int GetTileWithCreationDate = 0x1111E806;
+    static const int GetTileCreationDate = 0x1111E807;
 };
 /// \endcond
 

Modified: sandbox/jng/http304/Common/MapGuideCommon/Services/TileService.h
===================================================================
--- sandbox/jng/http304/Common/MapGuideCommon/Services/TileService.h	2013-07-15 09:54:15 UTC (rev 7688)
+++ sandbox/jng/http304/Common/MapGuideCommon/Services/TileService.h	2013-07-15 14:41:16 UTC (rev 7689)
@@ -91,6 +91,74 @@
         INT32 tileRow,
         INT32 scaleIndex) = 0;
 
+    /////////////////////////////////////////////////////////////////
+    /// \brief
+    /// Returns the specified base map tile for the given map.  If a cached tile
+    /// image exists it will return it, otherwise the tile is rendered and added
+    /// to the cache.
+    ///
+    /// \param map
+    /// Input
+    /// Map object containing current state of map.
+    /// \param baseMapLayerGroupName
+    /// Input
+    /// Specifies the name of the baseMapLayerGroup for which to render the tile.
+    /// \param tileColumn
+    /// Input
+    /// Specifies the column index of the tile to return.
+    /// \param tileRow
+    /// Input
+    /// Specifies the row index of the tile to return.
+    ///
+    /// \return
+    /// A byte reader containing the rendered tile image.
+    ///
+    virtual MgTile* GetTileWithCreationDate(
+        MgMap* map,
+        CREFSTRING baseMapLayerGroupName,
+        INT32 tileColumn,
+        INT32 tileRow) = 0;
+
+    /////////////////////////////////////////////////////////////////
+    /// \brief
+    /// Returns the specified base map tile for the given map.  If a cached tile
+    /// image exists it will return it, otherwise the tile is rendered and added
+    /// to the cache.
+    ///
+    /// \param mapDefinition
+    /// Input
+    /// Resource identifier for the map definition
+    /// \param baseMapLayerGroupName
+    /// Input
+    /// Specifies the name of the baseMapLayerGroup for which to render the tile.
+    /// \param tileColumn
+    /// Input
+    /// Specifies the column index of the tile to return.
+    /// \param tileRow
+    /// Input
+    /// Specifies the row index of the tile to return.
+    /// \param scaleIndex
+    /// Input
+    /// Scale index to render.  Most detailed scale is index 0.
+    ///
+    /// \return
+    /// A byte reader containing the rendered tile image.
+    ///
+    virtual MgTile* GetTileWithCreationDate(
+        MgResourceIdentifier* mapDefinition,
+        CREFSTRING baseMapLayerGroupName,
+        INT32 tileColumn,
+        INT32 tileRow,
+        INT32 scaleIndex) = 0;
+
+    /// Returns the timestamp of when the tile for the specified map/group/row/col was generated. Returns NULL if no such tile exists
+    ///
+    virtual MgDateTime* GetTileCreationDate(MgMap* map, CREFSTRING baseMapLayerGroupName, INT32 tileColumn, INT32 tileRow) = 0;
+
+    /// Returns the timestamp of when the tile for the specified map/group/row/col/scale was generated. Returns NULL if no such tile exists
+    ///
+    virtual MgDateTime* GetTileCreationDate(MgResourceIdentifier* mapDefinition, CREFSTRING baseMapLayerGroupName, INT32 tileColumn, INT32 tileRow, INT32 scaleIndex) = 0;
+
     //////////////////////////////////////////////////////////////////
     /// \brief
     /// Clears the entire tile cache for the given map.  Tiles for all base

Modified: sandbox/jng/http304/Common/MapGuideCommon/System/MapGuideCommonClassId.h
===================================================================
--- sandbox/jng/http304/Common/MapGuideCommon/System/MapGuideCommonClassId.h	2013-07-15 09:54:15 UTC (rev 7688)
+++ sandbox/jng/http304/Common/MapGuideCommon/System/MapGuideCommonClassId.h	2013-07-15 14:41:16 UTC (rev 7689)
@@ -135,6 +135,7 @@
 
 // Tile Service
 #define MapGuide_TileService_TileService                            MAPGUIDE_TILESERVICE_ID+0
+#define MapGuide_TileService_Tile                                   MAPGUIDE_TILESERVICE_ID+1
 
 // KML Service
 #define MapGuide_KmlService_KmlService                              MAPGUIDE_KMLSERVICE_ID+0

Modified: sandbox/jng/http304/Common/MapGuideCommon/System/MapGuideCommonFactory.cpp
===================================================================
--- sandbox/jng/http304/Common/MapGuideCommon/System/MapGuideCommonFactory.cpp	2013-07-15 09:54:15 UTC (rev 7688)
+++ sandbox/jng/http304/Common/MapGuideCommon/System/MapGuideCommonFactory.cpp	2013-07-15 14:41:16 UTC (rev 7689)
@@ -111,8 +111,8 @@
     fact->Register(MapGuide_RenderingService_RenderingOptions, MgRenderingOptions::CreateObject);
     fact->Register(PlatformBase_FeatureService_FeatureTransaction, MgProxyFeatureTransaction::CreateObject);
     fact->Register(MapGuide_Service_SqlResult, MgSqlResult::CreateObject);
+    fact->Register(MapGuide_TileService_Tile, MgTile::CreateObject);
 
-
     MgServiceRegistry* registry = MgServiceRegistry::GetInstance();
     registry->RegisterService(MgServiceType::DrawingService, MgProxyDrawingService::CreateService, sctRemoteServerToServer);
     registry->RegisterService(MgServiceType::MappingService, MgProxyMappingService::CreateService, sctRemoteServerToServer);

Modified: sandbox/jng/http304/Server/src/Services/Tile/ServerTileService.cpp
===================================================================
--- sandbox/jng/http304/Server/src/Services/Tile/ServerTileService.cpp	2013-07-15 09:54:15 UTC (rev 7688)
+++ sandbox/jng/http304/Server/src/Services/Tile/ServerTileService.cpp	2013-07-15 14:41:16 UTC (rev 7689)
@@ -117,11 +117,21 @@
     return found;
 }
 
+MgByteReader* MgServerTileService::GetTile(MgMap* map,
+                                           CREFSTRING baseMapLayerGroupName,
+                                           INT32 tileColumn,
+                                           INT32 tileRow)
+{
+    Ptr<MgByteReader> ret;
+    MG_TRY()
 
-///////////////////////////////////////////////////////////////////////////////
-// Create tilename from mapDefinition, scaleIndex, row, and column.
-// Remove lockfile, look for the tile in the cache, if not in cache create
-// lockfile and look for map in mapcache.
+    Ptr<MgTile> tile = GetTileWithCreationDate(map, baseMapLayerGroupName, tileColumn, tileRow);
+    ret = tile->GetImage();
+
+    MG_CATCH_AND_THROW(L"MgServerTileService.GetTile")
+    return ret.Detach();
+}
+
 MgByteReader* MgServerTileService::GetTile(MgResourceIdentifier* mapDefinition,
                                            CREFSTRING baseMapLayerGroupName,
                                            INT32 tileColumn,
@@ -129,6 +139,116 @@
                                            INT32 scaleIndex)
 {
     Ptr<MgByteReader> ret;
+    MG_TRY()
+
+    Ptr<MgTile> tile = GetTileWithCreationDate(mapDefinition, baseMapLayerGroupName, tileColumn, tileRow, scaleIndex);
+    ret = tile->GetImage();
+
+    MG_CATCH_AND_THROW(L"MgServerTileService.GetTile")
+    return ret.Detach();
+}
+
+/// Returns the timestamp of when the tile for the specified map/group/row/col was generated. Returns NULL if no such tile exists
+///
+MgDateTime* MgServerTileService::GetTileCreationDate(MgMap* map, 
+                                                     CREFSTRING baseMapLayerGroupName, 
+                                                     INT32 tileColumn, 
+                                                     INT32 tileRow)
+{
+    Ptr<MgDateTime> ret;
+    STRING tilePathname, lockPathname;
+    MG_TRY()
+
+    if (NULL == map || baseMapLayerGroupName.empty())
+    {
+        throw new MgNullArgumentException(L"MgServerTileService.GetTile",
+            __LINE__, __WFILE__, NULL, L"", NULL);
+    }
+
+    // find the finite display scale closest to the requested map scale
+    double scale = map->GetViewScale();
+    INT32 scaleIndex = map->FindNearestFiniteDisplayScaleIndex(scale);
+
+    // if we don't find a nearest scale then something is wrong with the map
+    if (scaleIndex < 0)
+    {
+        throw new MgInvalidMapDefinitionException(L"MgServerTileService.GetTile",
+            __LINE__, __WFILE__, NULL, L"", NULL);
+    }
+
+    // Generate expected tile and lock paths.
+    m_tileCache->GeneratePathnames(map, scaleIndex, baseMapLayerGroupName,
+        tileColumn, tileRow, tilePathname, lockPathname, false);
+
+    // If tile path exists, get its creation date and return it
+    if (MgFileUtil::IsFile(tilePathname))
+    {
+        MgDateTime cdate = MgFileUtil::GetFileCreationTime(tilePathname);
+        ret = new MgDateTime(cdate.ToNumber());
+    }
+
+    MG_CATCH_AND_THROW(L"MgServerTileService.GetTileCreationDate")
+    return ret.Detach();
+}
+
+/// Returns the timestamp of when the tile for the specified map/group/row/col/scale was generated. Returns NULL if no such tile exists
+///
+MgDateTime* MgServerTileService::GetTileCreationDate(MgResourceIdentifier* mapDefinition, 
+                                                     CREFSTRING baseMapLayerGroupName, 
+                                                     INT32 tileColumn, 
+                                                     INT32 tileRow, 
+                                                     INT32 scaleIndex)
+{
+    Ptr<MgDateTime> ret;
+    STRING tilePathname, lockPathname;
+
+    MG_TRY()
+
+    if (NULL == mapDefinition || baseMapLayerGroupName.empty())
+    {
+        throw new MgNullArgumentException(L"MgServerTileService.GetTile",
+            __LINE__, __WFILE__, NULL, L"", NULL);
+    }
+
+    if (scaleIndex < 0)
+    {
+        STRING buffer;
+        MgUtil::Int32ToString(scaleIndex, buffer);
+
+        MgStringCollection arguments;
+        arguments.Add(L"5");
+        arguments.Add(buffer);
+
+        throw new MgInvalidArgumentException(L"MgServerTileService.GetTile",
+            __LINE__, __WFILE__, &arguments, L"MgInvalidScaleIndex", NULL);
+    }
+
+    // Generate expected tile and lock paths.
+    m_tileCache->GeneratePathnames(mapDefinition, scaleIndex, baseMapLayerGroupName,
+        tileColumn, tileRow, tilePathname, lockPathname, false);
+
+    // If tile path exists, get its creation date and return it
+    if (MgFileUtil::IsFile(tilePathname))
+    {
+        MgDateTime cdate = MgFileUtil::GetFileCreationTime(tilePathname);
+        ret = new MgDateTime(cdate.ToNumber());
+    }
+
+    MG_CATCH_AND_THROW(L"MgServerTileService.GetTileCreationDate")
+    return ret.Detach();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Create tilename from mapDefinition, scaleIndex, row, and column.
+// Remove lockfile, look for the tile in the cache, if not in cache create
+// lockfile and look for map in mapcache.
+MgTile* MgServerTileService::GetTileWithCreationDate(MgResourceIdentifier* mapDefinition,
+                                                     CREFSTRING baseMapLayerGroupName,
+                                                     INT32 tileColumn,
+                                                     INT32 tileRow,
+                                                     INT32 scaleIndex)
+{
+    Ptr<MgTile> ret;
     FILE* lockFile = NULL;
     STRING tilePathname, lockPathname;
 
@@ -272,12 +392,12 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 // look for the tile in the tilecache first
-MgByteReader* MgServerTileService::GetTile(MgMap* map,
-                                           CREFSTRING baseMapLayerGroupName,
-                                           INT32 tileColumn,
-                                           INT32 tileRow)
+MgTile* MgServerTileService::GetTileWithCreationDate(MgMap* map,
+                                                     CREFSTRING baseMapLayerGroupName,
+                                                     INT32 tileColumn,
+                                                     INT32 tileRow)
 {
-    Ptr<MgByteReader> ret;
+    Ptr<MgTile> ret;
     FILE* lockFile = NULL;
     STRING tilePathname, lockPathname;
 
@@ -378,10 +498,10 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 // render a tile and store it in the cache
-MgByteReader* MgServerTileService::GetTile(CREFSTRING tilePathname, MgMap* map, INT32 scaleIndex,
+MgTile* MgServerTileService::GetTile(CREFSTRING tilePathname, MgMap* map, INT32 scaleIndex,
     CREFSTRING baseMapLayerGroupName, INT32 tileColumn, INT32 tileRow)
 {
-    Ptr<MgByteReader> img;
+    Ptr<MgTile> tile;
 
     // get a rendering service instance
     MgServiceManager* serviceMan = MgServiceManager::GetInstance();
@@ -393,22 +513,26 @@
     if (svcRendering != NULL)
     {
         // generate the tile
-        img = svcRendering->RenderTile(map, baseMapLayerGroupName, tileColumn, tileRow);
+        Ptr<MgByteReader> img = svcRendering->RenderTile(map, baseMapLayerGroupName, tileColumn, tileRow);
 
         // cache the tile
         if (!sm_renderOnly)
         {
             m_tileCache->Set(img, tilePathname);
+            MgDateTime cdate = MgFileUtil::GetFileCreationTime(tilePathname);
 
             // rewind the reader since setting the tile advances it to the end
             if (img)
             {
                 img->Rewind();
             }
+            Ptr<MgDateTime> creationDate = new MgDateTime(cdate.ToNumber());
+
+            tile = new MgTile(img, creationDate);
         }
     }
 
-    return img.Detach();
+    return tile.Detach();
 }
 
 

Modified: sandbox/jng/http304/Server/src/Services/Tile/ServerTileService.h
===================================================================
--- sandbox/jng/http304/Server/src/Services/Tile/ServerTileService.h	2013-07-15 09:54:15 UTC (rev 7688)
+++ sandbox/jng/http304/Server/src/Services/Tile/ServerTileService.h	2013-07-15 14:41:16 UTC (rev 7689)
@@ -41,6 +41,25 @@
                                   INT32 tileRow,
                                   INT32 scaleIndex);
 
+    virtual MgTile* GetTileWithCreationDate(MgMap* map,
+                                            CREFSTRING baseMapLayerGroupName,
+                                            INT32 tileColumn,
+                                            INT32 tileRow);
+
+    virtual MgTile* GetTileWithCreationDate(MgResourceIdentifier* mapDefinition,
+                                            CREFSTRING baseMapLayerGroupName,
+                                            INT32 tileColumn,
+                                            INT32 tileRow,
+                                            INT32 scaleIndex);
+    
+    /// Returns the timestamp of when the tile for the specified map/group/row/col was generated. Returns NULL if no such tile exists
+    ///
+    virtual MgDateTime* GetTileCreationDate(MgMap* map, CREFSTRING baseMapLayerGroupName, INT32 tileColumn, INT32 tileRow);
+
+    /// Returns the timestamp of when the tile for the specified map/group/row/col/scale was generated. Returns NULL if no such tile exists
+    ///
+    virtual MgDateTime* GetTileCreationDate(MgResourceIdentifier* mapDefinition, CREFSTRING baseMapLayerGroupName, INT32 tileColumn, INT32 tileRow, INT32 scaleIndex);
+
     virtual void SetTile(MgByteReader* img,
                          MgMap* map,
                          INT32 scaleIndex,
@@ -64,7 +83,7 @@
 
     bool DetectTileLockFile(CREFSTRING lockPathname);
 
-    MgByteReader* GetTile(CREFSTRING tilePathname, MgMap* map, INT32 scaleIndex,
+    MgTile* GetTile(CREFSTRING tilePathname, MgMap* map, INT32 scaleIndex,
         CREFSTRING baseMapLayerGroupName, INT32 tileColumn, INT32 tileRow);
 
     void ClearMapCache(CREFSTRING mapName);

Modified: sandbox/jng/http304/Server/src/Services/Tile/ServerTileService.vcxproj
===================================================================
--- sandbox/jng/http304/Server/src/Services/Tile/ServerTileService.vcxproj	2013-07-15 09:54:15 UTC (rev 7688)
+++ sandbox/jng/http304/Server/src/Services/Tile/ServerTileService.vcxproj	2013-07-15 14:41:16 UTC (rev 7689)
@@ -198,6 +198,18 @@
     </Link>
   </ItemDefinitionGroup>
   <ItemGroup>
+    <ClCompile Include="OpGetTileCreationDate.cpp">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+    </ClCompile>
+    <ClCompile Include="OpGetTileWithCreationDate.cpp">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+    </ClCompile>
     <ClCompile Include="OpClearCache.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
@@ -267,6 +279,8 @@
     </ClCompile>
   </ItemGroup>
   <ItemGroup>
+    <ClInclude Include="OpGetTileCreationDate.h" />
+    <ClInclude Include="OpGetTileWithCreationDate.h" />
     <ClInclude Include="OpClearCache.h" />
     <ClInclude Include="OpGetDefaultTileSizeX.h" />
     <ClInclude Include="OpGetDefaultTileSizeY.h" />

Modified: sandbox/jng/http304/Server/src/Services/Tile/ServerTileService.vcxproj.filters
===================================================================
--- sandbox/jng/http304/Server/src/Services/Tile/ServerTileService.vcxproj.filters	2013-07-15 09:54:15 UTC (rev 7688)
+++ sandbox/jng/http304/Server/src/Services/Tile/ServerTileService.vcxproj.filters	2013-07-15 14:41:16 UTC (rev 7689)
@@ -32,6 +32,12 @@
     <ClCompile Include="ServerTileServiceBuild.cpp" />
     <ClCompile Include="TileCache.cpp" />
     <ClCompile Include="TileServiceHandler.cpp" />
+    <ClCompile Include="OpGetTileCreationDate.cpp">
+      <Filter>Ops</Filter>
+    </ClCompile>
+    <ClCompile Include="OpGetTileWithCreationDate.cpp">
+      <Filter>Ops</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="OpClearCache.h">
@@ -60,6 +66,12 @@
     <ClInclude Include="..\..\Common\stdafx.h" />
     <ClInclude Include="TileCache.h" />
     <ClInclude Include="TileServiceHandler.h" />
+    <ClInclude Include="OpGetTileCreationDate.h">
+      <Filter>Ops</Filter>
+    </ClInclude>
+    <ClInclude Include="OpGetTileWithCreationDate.h">
+      <Filter>Ops</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="ServerTileService.rc" />

Modified: sandbox/jng/http304/Server/src/Services/Tile/ServerTileServiceBuild.cpp
===================================================================
--- sandbox/jng/http304/Server/src/Services/Tile/ServerTileServiceBuild.cpp	2013-07-15 09:54:15 UTC (rev 7688)
+++ sandbox/jng/http304/Server/src/Services/Tile/ServerTileServiceBuild.cpp	2013-07-15 14:41:16 UTC (rev 7689)
@@ -20,6 +20,8 @@
 #endif
 #include "OpClearCache.cpp"
 #include "OpGetTile.cpp"
+#include "OpGetTileCreationDate.cpp"
+#include "OpGetTileWithCreationDate.cpp"
 #include "OpSetTile.cpp"
 #include "OpGetDefaultTileSizeX.cpp"
 #include "OpGetDefaultTileSizeY.cpp"

Modified: sandbox/jng/http304/Server/src/Services/Tile/TileCache.cpp
===================================================================
--- sandbox/jng/http304/Server/src/Services/Tile/TileCache.cpp	2013-07-15 09:54:15 UTC (rev 7688)
+++ sandbox/jng/http304/Server/src/Services/Tile/TileCache.cpp	2013-07-15 14:41:16 UTC (rev 7689)
@@ -145,9 +145,9 @@
 }
 
 // returns any cached tile for the given pathname
-MgByteReader* MgTileCache::Get(CREFSTRING tilePathname)
+MgTile* MgTileCache::Get(CREFSTRING tilePathname)
 {
-    Ptr<MgByteReader> ret;
+    Ptr<MgTile> ret;
 
     MG_TRY()
 
@@ -168,7 +168,11 @@
             byteSource->SetMimeType(MgMimeType::Png);
         }
 
-        ret = byteSource->GetReader();
+        Ptr<MgByteReader> image = byteSource->GetReader();
+        MgDateTime cdate = MgFileUtil::GetFileCreationTime(tilePathname);
+        Ptr<MgDateTime> createDate = new MgDateTime(cdate.ToNumber());
+        
+        ret = new MgTile(image, createDate);
     }
 
     MG_CATCH_AND_RELEASE()

Modified: sandbox/jng/http304/Server/src/Services/Tile/TileCache.h
===================================================================
--- sandbox/jng/http304/Server/src/Services/Tile/TileCache.h	2013-07-15 09:54:15 UTC (rev 7688)
+++ sandbox/jng/http304/Server/src/Services/Tile/TileCache.h	2013-07-15 14:41:16 UTC (rev 7689)
@@ -38,7 +38,7 @@
     STRING CreateFullPath(MgResourceIdentifier* mapDef, int scaleIndex, CREFSTRING group, int tileColumn, int tileRow);
     STRING CreateFullPath(MgMap* map, int scaleIndex, CREFSTRING group, int tileColumn, int tileRow);
 
-    MgByteReader* Get(CREFSTRING tilePathname);
+    MgTile* Get(CREFSTRING tilePathname);
     void Set(MgByteReader* img, CREFSTRING tilePathname);
 
     void Clear(MgMap* map);

Modified: sandbox/jng/http304/Server/src/Services/Tile/TileOperationFactory.cpp
===================================================================
--- sandbox/jng/http304/Server/src/Services/Tile/TileOperationFactory.cpp	2013-07-15 09:54:15 UTC (rev 7688)
+++ sandbox/jng/http304/Server/src/Services/Tile/TileOperationFactory.cpp	2013-07-15 14:41:16 UTC (rev 7689)
@@ -74,6 +74,30 @@
         }
         break;
 
+    case MgTileServiceOpId::GetTileWithCreationDate:
+        switch (VERSION_NO_PHASE(operationVersion))
+        {
+        case VERSION_SUPPORTED(2,6):
+            handler.reset(new MgOpGetTileWithCreationDate());
+            break;
+        default:
+            throw new MgInvalidOperationVersionException(
+                L"MgTileOperationFactory.GetOperation", __LINE__, __WFILE__, NULL, L"", NULL);
+        }
+        break;
+
+    case MgTileServiceOpId::GetTileCreationDate:
+        switch (VERSION_NO_PHASE(operationVersion))
+        {
+        case VERSION_SUPPORTED(2,6):
+            handler.reset(new MgOpGetTileCreationDate());
+            break;
+        default:
+            throw new MgInvalidOperationVersionException(
+                L"MgTileOperationFactory.GetOperation", __LINE__, __WFILE__, NULL, L"", NULL);
+        }
+        break;
+
     case MgTileServiceOpId::SetTile:
         switch (VERSION_NO_PHASE(operationVersion))
         {

Modified: sandbox/jng/http304/Server/src/UnitTesting/TestTileService.cpp
===================================================================
--- sandbox/jng/http304/Server/src/UnitTesting/TestTileService.cpp	2013-07-15 09:54:15 UTC (rev 7688)
+++ sandbox/jng/http304/Server/src/UnitTesting/TestTileService.cpp	2013-07-15 14:41:16 UTC (rev 7689)
@@ -165,6 +165,7 @@
 
         // set up temporary directory for tile images
         MgFileUtil::CreateDirectory(L"./temp_tiles", false);
+        MgFileUtil::CreateDirectory(L"./temp_tiles_mod_date", false);
     }
     catch (MgException* e)
     {
@@ -218,6 +219,7 @@
 
         // remove temporary directory for tile images
         MgFileUtil::DeleteDirectory(L"./temp_tiles", true, false);
+        MgFileUtil::DeleteDirectory(L"./temp_tiles_mod_date", true, false);
 
         #ifdef _DEBUG
         MgFdoConnectionManager* pFdoConnectionManager = MgFdoConnectionManager::GetInstance();
@@ -253,12 +255,121 @@
     INT32 tileCol;
 };
 
+// data structure which is passed to each thread
+struct TileWithCreationDateThreadData
+{
+    INT32 threadId;
+    bool done;
+    bool saveTile;
+    Ptr<MgMap> map;
+    INT32 tileRow;
+    INT32 tileCol;
+    Ptr<MgDateTime> checkDate;
+    bool nDateFailCount;
+    bool nCacheDateFailCount;
+};
 
+
 ////////////////////////////////////////////////////////////////
 /// GetTile methods
 ////////////////////////////////////////////////////////////////
 
+// the method which gets executed by the ACE worker thread
+ACE_THR_FUNC_RETURN GetTileWithCreationDateWorker(void* param)
+{
+    // get the data for this thread
+    TileWithCreationDateThreadData* threadData = (TileWithCreationDateThreadData*)param;
+    INT32 threadId = threadData->threadId;
+    INT32 tileRow  = threadData->tileRow;
+    INT32 tileCol  = threadData->tileCol;
+    bool saveTile  = threadData->saveTile;
+    Ptr<MgMap> map = threadData->map;
+    #ifdef _DEBUG
+    printf("> thread %d started, tile %d,%d\n", threadId, tileRow, tileCol);
+    #endif
 
+    try
+    {
+        // set user info
+        Ptr<MgUserInformation> userInfo = new MgUserInformation(L"Administrator", L"admin");
+        userInfo->SetLocale(TEST_LOCALE);
+        MgUserInformation::SetCurrentUserInfo(userInfo);
+
+        // get the tile service instance
+        MgServiceManager* serviceManager = MgServiceManager::GetInstance();
+        Ptr<MgTileService> svcTile = dynamic_cast<MgTileService*>(
+            serviceManager->RequestService(MgServiceType::TileService));
+        assert(svcTile != NULL);
+
+        wchar_t imgName[PATH_LEN] = { 0 };
+        swprintf(imgName, PATH_LEN, L"./temp_tiles_mod_date/tile%d_%d.png", tileRow, tileCol);
+        
+        // get the tile
+        Ptr<MgTile> tile = svcTile->GetTileWithCreationDate(map, L"BaseLayers", tileCol, tileRow);
+        Ptr<MgDateTime> createDate = tile->GetCreationDate();
+        if (NULL == createDate.p)
+        {
+            //We should've gotten a date back from this
+            threadData->nDateFailCount++;
+        }
+        else
+        {
+            MgDateTime chkDate = (*(threadData->checkDate.p)); 
+            MgDateTime cdate = (*(createDate.p));
+
+            //Date created should be newer than our check date
+            if (cdate <= chkDate)
+                threadData->nDateFailCount++;
+        }
+
+        // save the image to a file if necessary
+        if (saveTile)
+        {
+            Ptr<MgByteReader> img = tile->GetImage();
+            (MgByteSink(img)).ToFile(imgName);
+        }
+
+        // Verify dates are same
+        Ptr<MgDateTime> createDate2 = svcTile->GetTileCreationDate(map, L"BaseLayers", tileCol, tileRow);
+        if (NULL == createDate2.p)
+        {
+            // Tile was rendered, so we should've gotten a date
+            threadData->nCacheDateFailCount++;
+        }
+        else
+        {
+            if (NULL != createDate.p)
+            {
+                MgDateTime date1 = (*(createDate.p));
+                MgDateTime date2 = (*(createDate2.p));
+                // Dates should've been equal
+                if (date1 != date2)
+                    threadData->nCacheDateFailCount++;
+            }
+        }
+
+        // clear the user info to prevent leaks
+        MgUserInformation::SetCurrentUserInfo(NULL);
+    }
+    catch (MgException* e)
+    {
+        STRING message = e->GetDetails(TEST_LOCALE);
+        SAFE_RELEASE(e);
+        CPPUNIT_FAIL(MG_WCHAR_TO_CHAR(message.c_str()));
+    }
+    catch (...)
+    {
+        throw;
+    }
+
+    #ifdef _DEBUG
+//  printf("> thread %d done\n", threadId);
+    #endif
+
+    threadData->done = true;
+    return 0;
+}
+
 // the method which gets executed by the ACE worker thread
 ACE_THR_FUNC_RETURN GetTileWorker(void* param)
 {
@@ -451,7 +562,238 @@
     }
 }
 
+void TestTileService::TestCase_GetTileCreationDate()
+{
+    // define the range of tiles to get
+    INT32 tileRowMin =  0;
+    INT32 tileRowMax = 12;
+    INT32 tileColMin =  3;
+    INT32 tileColMax = 11;
 
+    try
+    {
+        // get the tile service instance
+        MgServiceManager* serviceManager = MgServiceManager::GetInstance();
+        Ptr<MgTileService> svcTile = dynamic_cast<MgTileService*>(
+            serviceManager->RequestService(MgServiceType::TileService));
+        assert(svcTile != NULL);
+
+        // make the runtime map
+        Ptr<MgMap> map = CreateMap();
+
+        // Clear any tiles for this map if they exist
+        svcTile->ClearCache(map);
+
+        for (INT32 tileRow = tileRowMin; tileRow <= tileRowMax; ++tileRow)
+        {
+            for (INT32 tileCol = tileColMin; tileCol <= tileColMax; ++tileCol)
+            {
+                //None of these tiles should exist, thus their create dates should all be NULL
+                Ptr<MgDateTime> createDate = svcTile->GetTileCreationDate(map, L"BaseLayers", tileCol, tileRow);
+                CPPUNIT_ASSERT(NULL == createDate.p);
+            }
+        }
+    }
+    catch (MgException* e)
+    {
+        STRING message = e->GetDetails(TEST_LOCALE);
+        SAFE_RELEASE(e);
+        CPPUNIT_FAIL(MG_WCHAR_TO_CHAR(message.c_str()));
+    }
+    catch (...)
+    {
+        throw;
+    }
+}
+
+void TestTileService::TestCase_GetTileWithCreationDate()
+{
+    // specify the number of threads to use
+    const INT32 numThreads = MG_TEST_THREADS;
+    TileWithCreationDateThreadData threadData[numThreads];
+
+    // define the range of tiles to get
+    INT32 tileRowMin =  0;
+    INT32 tileRowMax = 12;
+    INT32 tileColMin =  3;
+    INT32 tileColMax = 11;
+
+    Ptr<MgDateTime> now = new MgDateTime();
+    
+    // "Breathe" a bit to give the check and expected creation dates some room
+    ACE_OS::sleep(1);
+
+    try
+    {
+        // need a thread manager
+        ACE_Thread_Manager* manager = ACE_Thread_Manager::instance();
+        // get the tile service instance
+        MgServiceManager* serviceManager = MgServiceManager::GetInstance();
+        Ptr<MgTileService> svcTile = dynamic_cast<MgTileService*>(
+            serviceManager->RequestService(MgServiceType::TileService));
+        assert(svcTile != NULL);
+
+        // make the runtime map
+        Ptr<MgMap> map = CreateMap();
+
+        // Make sure date checks aren't tainted by previous tests on the same map
+        svcTile->ClearCache(map);
+
+        // set up the tile indices
+        INT32 numTileRows = tileRowMax - tileRowMin + 1;
+        INT32 numTileCols = tileColMax - tileColMin + 1;
+        INT32 numTiles    = numTileRows * numTileCols;
+
+        INT32* tileRows = new INT32[numTiles];
+        INT32* tileCols = new INT32[numTiles];
+
+        INT32 nRequest = 0;
+        for (INT32 tileRow = tileRowMin; tileRow <= tileRowMax; ++tileRow)
+        {
+            for (INT32 tileCol = tileColMin; tileCol <= tileColMax; ++tileCol)
+            {
+                tileRows[nRequest] = tileRow;
+                tileCols[nRequest] = tileCol;
+                nRequest++;
+            }
+        }
+
+        // initialize the thread data
+        for (INT32 i=0; i<numThreads; i++)
+        {
+            // each thread works with its own instance of the map
+            Ptr<MgMemoryStreamHelper> helper = new MgMemoryStreamHelper();
+            Ptr<MgStream> stream = new MgStream(helper);
+            Ptr<MgMap> newMap = new MgMap(m_siteConnection);
+            map->Serialize(stream);
+            newMap->Deserialize(stream);
+
+            threadData[i].threadId  = i;
+            threadData[i].done      = true;
+            threadData[i].saveTile  = false;
+            threadData[i].map       = newMap;
+            threadData[i].tileRow   = 0;
+            threadData[i].tileCol   = 0;
+            threadData[i].checkDate = now;
+            threadData[i].nDateFailCount = 0;
+            threadData[i].nCacheDateFailCount = 0;
+        }
+
+        // execute the requests to randomly access the tiles
+        #ifdef _DEBUG
+        printf("\n");
+        #endif
+        nRequest = 0;
+        for (;;)
+        {
+            INT32 dc = 0;
+            for (INT32 i=0; i<numThreads; i++)
+            {
+                // check if the thread is available
+                if (threadData[i].done)
+                {
+                    // if we're not yet done then give the thread a new request
+                    if (nRequest < REQUEST_FACTOR*numTiles)
+                    {
+                        // pick a random request to execute...
+                        INT32 nTile = Rand(numTiles);
+
+                        // ... but make every REQUEST_FACTOR-th tile non-random to
+                        // ensure we get each tile at least once
+                        if (nRequest % REQUEST_FACTOR == 0)
+                            nTile = nRequest / REQUEST_FACTOR;
+
+                        threadData[i].done     = false;
+                        threadData[i].saveTile = (nRequest % REQUEST_FACTOR == 0);
+                        threadData[i].tileRow  = tileRows[nTile];
+                        threadData[i].tileCol  = tileCols[nTile];
+
+                        // spawn a new thread using a specific group id
+                        int thid = manager->spawn(ACE_THR_FUNC(GetTileWithCreationDateWorker), &threadData[i], 0, NULL, NULL, 0, THREAD_GROUP);
+                        nRequest++;
+                    }
+
+                    // keep a tally of all the done threads
+                    if (threadData[i].done)
+                        ++dc;
+                }
+            }
+
+            // move on if all threads are done
+            if (dc == numThreads)
+                break;
+
+            // under Linux we get a deadlock if we don't call this every once in a while
+            if (nRequest % 25 == 0)
+                manager->wait_grp(THREAD_GROUP);
+            else
+            {
+                // pause briefly (10ms) before checking again
+                ACE_Time_Value t(0, 10000);
+                ACE_OS::sleep(t);
+            }
+        }
+
+        // make sure all threads in the group have completed
+        manager->wait_grp(THREAD_GROUP);
+
+        // Check our failure count. Should all be zero
+        STRING message;
+        for (INT32 i=0; i<numThreads; i++)
+        {
+            if (threadData[i].nDateFailCount > 0)
+            {
+                STRING msg = L"Thread (";
+                STRING sCount;
+                STRING sThreadId;
+                MgUtil::Int32ToString(threadData[i].threadId, sThreadId);
+                MgUtil::Int32ToString(threadData[i].nDateFailCount, sCount);
+                msg += sThreadId;
+                msg += L") failed creation date check ";
+                msg += sCount;
+                msg += L" times\n";
+
+                message += msg;
+            }
+
+            if (threadData[i].nCacheDateFailCount > 0)
+            {
+                STRING msg = L"Thread (";
+                STRING sCount;
+                STRING sThreadId;
+                MgUtil::Int32ToString(threadData[i].threadId, sThreadId);
+                MgUtil::Int32ToString(threadData[i].nCacheDateFailCount, sCount);
+                msg += sThreadId;
+                msg += L") failed cache date check ";
+                msg += sCount;
+                msg += L" times\n";
+
+                message += msg;
+            }
+        }
+
+        // done with the tile indices
+        delete [] tileRows;
+        delete [] tileCols;
+
+        if (!message.empty())
+        {
+            CPPUNIT_FAIL(MG_WCHAR_TO_CHAR(message.c_str()));
+        }
+    }
+    catch (MgException* e)
+    {
+        STRING message = e->GetDetails(TEST_LOCALE);
+        SAFE_RELEASE(e);
+        CPPUNIT_FAIL(MG_WCHAR_TO_CHAR(message.c_str()));
+    }
+    catch (...)
+    {
+        throw;
+    }
+}
+
+
 ////////////////////////////////////////////////////////////////
 /// SetTile methods
 ////////////////////////////////////////////////////////////////

Modified: sandbox/jng/http304/Server/src/UnitTesting/TestTileService.h
===================================================================
--- sandbox/jng/http304/Server/src/UnitTesting/TestTileService.h	2013-07-15 09:54:15 UTC (rev 7688)
+++ sandbox/jng/http304/Server/src/UnitTesting/TestTileService.h	2013-07-15 14:41:16 UTC (rev 7689)
@@ -26,6 +26,8 @@
     CPPUNIT_TEST(TestStart); // This must be the very first unit test
 
     CPPUNIT_TEST(TestCase_GetTile);
+    CPPUNIT_TEST(TestCase_GetTileCreationDate);
+    CPPUNIT_TEST(TestCase_GetTileWithCreationDate);
     CPPUNIT_TEST(TestCase_SetTile);
     CPPUNIT_TEST(TestCase_GetSetTile);
     CPPUNIT_TEST(TestCase_ClearCache);
@@ -43,6 +45,8 @@
     void TestEnd();
 
     void TestCase_GetTile();
+    void TestCase_GetTileWithCreationDate();
+    void TestCase_GetTileCreationDate();
     void TestCase_SetTile();
     void TestCase_GetSetTile();
     void TestCase_ClearCache();



More information about the mapguide-commits mailing list