Implemented object caching on client side
authorVictor Kirhenshtein <victor@netxms.org>
Sat, 26 Mar 2005 07:50:57 +0000 (07:50 +0000)
committerVictor Kirhenshtein <victor@netxms.org>
Sat, 26 Mar 2005 07:50:57 +0000 (07:50 +0000)
14 files changed:
include/nxclapi.h
src/console/win32/comm.cpp
src/console/win32/globals.h
src/console/win32/nxcon.cpp
src/libnxcl/comm.cpp
src/libnxcl/libnxcl.h
src/libnxcl/objects.cpp
src/libnxcl/session.cpp
src/server/core/client.cpp
src/server/core/netobj.cpp
src/server/core/node.cpp
src/server/core/session.cpp
src/server/include/nms_core.h
src/server/include/nms_objects.h

index 0b32040..2f4b7bf 100644 (file)
@@ -973,6 +973,7 @@ void LIBNXCL_EXPORTABLE NXCSetEventHandler(NXC_SESSION hSession, NXC_EVENT_HANDL
 void LIBNXCL_EXPORTABLE NXCSetCommandTimeout(NXC_SESSION hSession, DWORD dwTimeout);
 
 DWORD LIBNXCL_EXPORTABLE NXCSyncObjects(NXC_SESSION hSession);
+DWORD LIBNXCL_EXPORTABLE NXCSyncObjectsEx(NXC_SESSION hSession, TCHAR *pszCacheFile);
 NXC_OBJECT LIBNXCL_EXPORTABLE *NXCFindObjectById(NXC_SESSION hSession, DWORD dwId);
 NXC_OBJECT LIBNXCL_EXPORTABLE *NXCFindObjectByIdNoLock(NXC_SESSION hSession, DWORD dwId);
 NXC_OBJECT LIBNXCL_EXPORTABLE *NXCFindObjectByName(NXC_SESSION hSession, TCHAR *pszName);
@@ -997,6 +998,7 @@ DWORD LIBNXCL_EXPORTABLE NXCGetSupportedParameters(NXC_SESSION hSession, DWORD d
                                                    NXC_AGENT_PARAM **ppParamList);
 void LIBNXCL_EXPORTABLE NXCGetComparableObjectName(NXC_SESSION hSession, DWORD dwObjectId,
                                                    TCHAR *pszName);
+DWORD LIBNXCL_EXPORTABLE NXCSaveObjectCache(NXC_SESSION hSession, TCHAR *pszFile);
 
 DWORD LIBNXCL_EXPORTABLE NXCLoadCCList(NXC_SESSION hSession, NXC_CC_LIST **ppList);
 void LIBNXCL_EXPORTABLE NXCDestroyCCList(NXC_CC_LIST *pList);
index 2720fdc..ba13b3d 100644 (file)
@@ -109,8 +109,12 @@ static DWORD WINAPI LoginThread(void *pArg)
    // Synchronize objects
    if (dwResult == RCC_SUCCESS)
    {
+      TCHAR szCacheFile[MAX_PATH];
+
       SetInfoText(hWnd, "Synchronizing objects...");
-      dwResult = NXCSyncObjects(g_hSession);
+      _tcscpy(szCacheFile, g_szWorkDir);
+      _tcscat(szCacheFile, WORKFILE_OBJECTCACHE);
+      dwResult = NXCSyncObjectsEx(g_hSession, szCacheFile);
    }
 
    if (dwResult == RCC_SUCCESS)
index 2d7e342..c64dc2e 100644 (file)
@@ -89,11 +89,12 @@ extern CConsoleApp theApp;
 
 
 //
-// Subdirectories in working directory
+// Subdirectories and files in working directory
 //
 
-#define WORKDIR_MIBCACHE   "\\MIBCache"
-#define WORKDIR_IMAGECACHE "\\ImageCache"
+#define WORKDIR_MIBCACHE      _T("\\MIBCache")
+#define WORKDIR_IMAGECACHE    _T("\\ImageCache")
+#define WORKFILE_OBJECTCACHE  _T("\\objects.cache")
 
 
 //
index 7eb2f41..8aeed24 100644 (file)
@@ -327,6 +327,12 @@ BOOL CConsoleApp::InitInstance()
 
 int CConsoleApp::ExitInstance() 
 {
+   TCHAR szBuffer[MAX_PATH];
+
+   _tcscpy(szBuffer, g_szWorkDir);
+   _tcscat(szBuffer, WORKFILE_OBJECTCACHE);
+   NXCSaveObjectCache(g_hSession, szBuffer);
+
    NXCSetDebugCallback(NULL);
    NXCDisconnect(g_hSession);
    NXCShutdown();
index 15cec8c..72c5336 100644 (file)
@@ -104,6 +104,7 @@ THREAD_RESULT THREAD_CALL NetReceiver(NXCL_Session *pSession)
          switch(pMsg->GetCode())
          {
             case CMD_KEEPALIVE:     // Keepalive message, ignore it
+               pSession->SetTimeStamp(pMsg->GetVariableLong(VID_TIMESTAMP));
                break;
             case CMD_OBJECT:        // Object information
             case CMD_OBJECT_UPDATE:
index c5ea420..9698b42 100644 (file)
@@ -38,7 +38,9 @@
 // Constants
 //
 
-#define LIBNXCL_VERSION    1
+#define OBJECT_CACHE_MAGIC 0x7080ABCD
+
+#define LIBNXCL_VERSION    2
 
 #define MAX_SERVER_NAME    64
 #define MAX_LOGIN_NAME     64
@@ -53,6 +55,7 @@
 
 #define NXC_SF_USERDB_LOADED     0x0001
 #define NXC_SF_SYNC_FINISHED     0x0002
+#define NXC_SF_HAS_OBJECT_CACHE  0x0004
 
 
 //
@@ -66,6 +69,19 @@ typedef struct
 } INDEX;
 
 
+//
+// Object cache file header structure
+//
+
+typedef struct
+{
+   DWORD dwMagic;
+   DWORD dwStructSize;  // sizeof(NXC_OBJECT)
+   DWORD dwTimeStamp;
+   DWORD dwNumObjects;
+} OBJECT_CACHE_HEADER;
+
+
 //
 // Session class
 //
@@ -79,6 +95,7 @@ private:
    DWORD m_dwFlags;
    DWORD m_dwMsgId;
    DWORD m_dwState;
+   DWORD m_dwTimeStamp;    // Last known server's timestamp
    DWORD m_dwNumObjects;
    INDEX *m_pIndexById;
    MUTEX m_mutexIndexAccess;
@@ -118,6 +135,7 @@ private:
 
    void ProcessObjectUpdate(CSCPMessage *pMsg);
    void AddObject(NXC_OBJECT *pObject, BOOL bSortIndex);
+   void LoadObjectsFromCache(TCHAR *pszCacheFile);
 
 public:
    NXCL_Session();
@@ -156,7 +174,7 @@ public:
    BOOL GetUserDB(NXC_USER **ppUserList, DWORD *pdwNumUsers);
    NXC_USER *FindUserById(DWORD dwId);
 
-   DWORD SyncObjects(void);
+   DWORD SyncObjects(TCHAR *pszCacheFile);
    void LockObjectIndex(void) { MutexLock(m_mutexIndexAccess, INFINITE); }
    void UnlockObjectIndex(void) { MutexUnlock(m_mutexIndexAccess); }
    NXC_OBJECT *FindObjectById(DWORD dwId, BOOL bLock);
@@ -164,6 +182,9 @@ public:
    void EnumerateObjects(BOOL (* pHandler)(NXC_OBJECT *));
    NXC_OBJECT *GetRootObject(DWORD dwId, DWORD dwIndex);
    void *GetObjectIndex(DWORD *pdwNumObjects);
+
+   void SetTimeStamp(DWORD dwTimeStamp) { m_dwTimeStamp = dwTimeStamp; }
+   DWORD GetTimeStamp(void) { return m_dwTimeStamp; }
 };
 
 inline void NXCL_Session::CallEventHandler(DWORD dwEvent, DWORD dwCode, void *pArg)
index 8dab536..bac65e1 100644 (file)
@@ -258,9 +258,12 @@ void NXCL_Session::ProcessObjectUpdate(CSCPMessage *pMsg)
    switch(pMsg->GetCode())
    {
       case CMD_OBJECT_LIST_END:
-         LockObjectIndex();
-         qsort(m_pIndexById, m_dwNumObjects, sizeof(INDEX), IndexCompare);
-         UnlockObjectIndex();
+         if (!(m_dwFlags & NXC_SF_HAS_OBJECT_CACHE))
+         {
+            LockObjectIndex();
+            qsort(m_pIndexById, m_dwNumObjects, sizeof(INDEX), IndexCompare);
+            UnlockObjectIndex();
+         }
          CompleteSync(RCC_SUCCESS);
          break;
       case CMD_OBJECT:
@@ -268,8 +271,25 @@ void NXCL_Session::ProcessObjectUpdate(CSCPMessage *pMsg)
                      pMsg->GetVariableStr(VID_OBJECT_NAME), pMsg->GetVariableShort(VID_OBJECT_CLASS));
       
          // Create new object from message and add it to list
-         pObject = NewObjectFromMsg(pMsg);
-         AddObject(pObject, FALSE);
+         pNewObject = NewObjectFromMsg(pMsg);
+         if (m_dwFlags & NXC_SF_HAS_OBJECT_CACHE)
+         {
+            // We already have some objects loaded from cache file
+            pObject = FindObjectById(pNewObject->dwId, TRUE);
+            if (pObject == NULL)
+            {
+               AddObject(pNewObject, TRUE);
+            }
+            else
+            {
+               ReplaceObject(pObject, pNewObject);
+            }
+         }
+         else
+         {
+            // No cache file, all objects are new
+            AddObject(pNewObject, FALSE);
+         }
          break;
       case CMD_OBJECT_UPDATE:
          pNewObject = NewObjectFromMsg(pMsg);
@@ -296,7 +316,7 @@ void NXCL_Session::ProcessObjectUpdate(CSCPMessage *pMsg)
 // This function is NOT REENTRANT
 //
 
-DWORD NXCL_Session::SyncObjects(void)
+DWORD NXCL_Session::SyncObjects(TCHAR *pszCacheFile)
 {
    CSCPMessage msg;
    DWORD dwRetCode, dwRqId;
@@ -306,8 +326,13 @@ DWORD NXCL_Session::SyncObjects(void)
 
    DestroyAllObjects();
 
+   m_dwFlags &= ~NXC_SF_HAS_OBJECT_CACHE;
+   if (pszCacheFile != NULL)
+      LoadObjectsFromCache(pszCacheFile);
+
    msg.SetCode(CMD_GET_OBJECTS);
    msg.SetId(dwRqId);
+   msg.SetVariable(VID_TIMESTAMP, m_dwTimeStamp);
    SendMsg(&msg);
 
    dwRetCode = WaitForRCC(dwRqId);
@@ -321,12 +346,17 @@ DWORD NXCL_Session::SyncObjects(void)
 
 
 //
-// Wrapper for NXCL_Session::SyncObjects()
+// Wrappers for NXCL_Session::SyncObjects()
 //
 
 DWORD LIBNXCL_EXPORTABLE NXCSyncObjects(NXC_SESSION hSession)
 {
-   return ((NXCL_Session *)hSession)->SyncObjects();
+   return ((NXCL_Session *)hSession)->SyncObjects(NULL);
+}
+
+DWORD LIBNXCL_EXPORTABLE NXCSyncObjectsEx(NXC_SESSION hSession, TCHAR *pszCacheFile)
+{
+   return ((NXCL_Session *)hSession)->SyncObjects(pszCacheFile);
 }
 
 
@@ -970,3 +1000,165 @@ void LIBNXCL_EXPORTABLE NXCGetComparableObjectName(NXC_SESSION hSession, DWORD d
       *pszName = 0;
    }
 }
+
+
+//
+// Save object's cache to file
+//
+
+DWORD LIBNXCL_EXPORTABLE NXCSaveObjectCache(NXC_SESSION hSession, TCHAR *pszFile)
+{
+   FILE *hFile;
+   OBJECT_CACHE_HEADER hdr;
+   DWORD i, dwResult, dwNumObjects, dwSize;
+   INDEX *pList;
+
+   hFile = _tfopen(pszFile, _T("wb"));
+   if (hFile != NULL)
+   {
+      ((NXCL_Session *)hSession)->LockObjectIndex();
+      pList = (INDEX *)((NXCL_Session *)hSession)->GetObjectIndex(&dwNumObjects);
+
+      // Write cache file header
+      hdr.dwMagic = OBJECT_CACHE_MAGIC;
+      hdr.dwStructSize = sizeof(NXC_OBJECT);
+      hdr.dwTimeStamp = ((NXCL_Session *)hSession)->GetTimeStamp();
+      hdr.dwNumObjects = dwNumObjects;
+      fwrite(&hdr, 1, sizeof(OBJECT_CACHE_HEADER), hFile);
+
+      // Write all objects
+      for(i = 0; i < dwNumObjects; i++)
+      {
+         fwrite(pList[i].pObject, 1, sizeof(NXC_OBJECT), hFile);
+         fwrite(pList[i].pObject->pdwChildList, 1, 
+                sizeof(DWORD) * pList[i].pObject->dwNumChilds, hFile);
+         fwrite(pList[i].pObject->pdwParentList, 1, 
+                sizeof(DWORD) * pList[i].pObject->dwNumParents, hFile);
+         fwrite(pList[i].pObject->pAccessList, 1, 
+                sizeof(NXC_ACL_ENTRY) * pList[i].pObject->dwAclSize, hFile);
+         switch(pList[i].pObject->iClass)
+         {
+            case OBJECT_NODE:
+               dwSize = _tcslen(pList[i].pObject->node.pszDescription) * sizeof(TCHAR);
+               fwrite(&dwSize, 1, sizeof(DWORD), hFile);
+               fwrite(pList[i].pObject->node.pszDescription, 1, dwSize, hFile);
+               break;
+            case OBJECT_CONTAINER:
+               dwSize = _tcslen(pList[i].pObject->container.pszDescription) * sizeof(TCHAR);
+               fwrite(&dwSize, 1, sizeof(DWORD), hFile);
+               fwrite(pList[i].pObject->container.pszDescription, 1, dwSize, hFile);
+               break;
+            case OBJECT_TEMPLATE:
+               dwSize = _tcslen(pList[i].pObject->dct.pszDescription) * sizeof(TCHAR);
+               fwrite(&dwSize, 1, sizeof(DWORD), hFile);
+               fwrite(pList[i].pObject->dct.pszDescription, 1, dwSize, hFile);
+               break;
+            case OBJECT_NETWORKSERVICE:
+               dwSize = _tcslen(pList[i].pObject->netsrv.pszRequest) * sizeof(TCHAR);
+               fwrite(&dwSize, 1, sizeof(DWORD), hFile);
+               fwrite(pList[i].pObject->netsrv.pszRequest, 1, dwSize, hFile);
+
+               dwSize = _tcslen(pList[i].pObject->netsrv.pszResponce) * sizeof(TCHAR);
+               fwrite(&dwSize, 1, sizeof(DWORD), hFile);
+               fwrite(pList[i].pObject->netsrv.pszResponce, 1, dwSize, hFile);
+               break;
+            default:
+               break;
+         }
+      }
+
+      ((NXCL_Session *)hSession)->UnlockObjectIndex();
+      fclose(hFile);
+      dwResult = RCC_SUCCESS;
+   }
+   else
+   {
+      dwResult = RCC_IO_ERROR;
+   }
+
+   return dwResult;
+}
+
+
+//
+// Load objects from cache file
+//
+
+void NXCL_Session::LoadObjectsFromCache(TCHAR *pszFile)
+{
+   FILE *hFile;
+   OBJECT_CACHE_HEADER hdr;
+   NXC_OBJECT object;
+   DWORD i, dwSize;
+
+   hFile = _tfopen(pszFile, _T("rb"));
+   if (hFile != NULL)
+   {
+      // Read header
+      if (fread(&hdr, 1, sizeof(OBJECT_CACHE_HEADER), hFile) == sizeof(OBJECT_CACHE_HEADER))
+      {
+         if ((hdr.dwMagic == OBJECT_CACHE_MAGIC) &&
+             (hdr.dwStructSize == sizeof(NXC_OBJECT)))
+         {
+            m_dwTimeStamp = hdr.dwTimeStamp;
+            for(i = 0; i < hdr.dwNumObjects; i++)
+            {
+               if (fread(&object, 1, sizeof(NXC_OBJECT), hFile) == sizeof(NXC_OBJECT))
+               {
+                  object.pdwChildList = (DWORD *)malloc(sizeof(DWORD) * object.dwNumChilds);
+                  fread(object.pdwChildList, 1, sizeof(DWORD) * object.dwNumChilds, hFile);
+                  
+                  object.pdwParentList = (DWORD *)malloc(sizeof(DWORD) * object.dwNumParents);
+                  fread(object.pdwParentList, 1, sizeof(DWORD) * object.dwNumParents, hFile);
+                  
+                  object.pAccessList = (NXC_ACL_ENTRY *)malloc(sizeof(NXC_ACL_ENTRY) * object.dwAclSize);
+                  fread(object.pAccessList, 1, sizeof(NXC_ACL_ENTRY) * object.dwAclSize, hFile);
+
+                  switch(object.iClass)
+                  {
+                     case OBJECT_NODE:
+                        fread(&dwSize, 1, sizeof(DWORD), hFile);
+                        object.node.pszDescription = (TCHAR *)malloc(dwSize + sizeof(TCHAR));
+                        fread(object.node.pszDescription, 1, dwSize, hFile);
+                        object.node.pszDescription[dwSize / sizeof(TCHAR)] = 0;
+                        break;
+                     case OBJECT_CONTAINER:
+                        fread(&dwSize, 1, sizeof(DWORD), hFile);
+                        object.container.pszDescription = (TCHAR *)malloc(dwSize + sizeof(TCHAR));
+                        fread(object.container.pszDescription, 1, dwSize, hFile);
+                        object.container.pszDescription[dwSize / sizeof(TCHAR)] = 0;
+                        break;
+                     case OBJECT_TEMPLATE:
+                        fread(&dwSize, 1, sizeof(DWORD), hFile);
+                        object.dct.pszDescription = (TCHAR *)malloc(dwSize + sizeof(TCHAR));
+                        fread(object.dct.pszDescription, 1, dwSize, hFile);
+                        object.dct.pszDescription[dwSize / sizeof(TCHAR)] = 0;
+                        break;
+                     case OBJECT_NETWORKSERVICE:
+                        fread(&dwSize, 1, sizeof(DWORD), hFile);
+                        object.netsrv.pszRequest = (TCHAR *)malloc(dwSize + sizeof(TCHAR));
+                        fread(object.netsrv.pszRequest, 1, dwSize, hFile);
+                        object.netsrv.pszRequest[dwSize / sizeof(TCHAR)] = 0;
+
+                        fread(&dwSize, 1, sizeof(DWORD), hFile);
+                        object.netsrv.pszResponce = (TCHAR *)malloc(dwSize + sizeof(TCHAR));
+                        fread(object.netsrv.pszResponce, 1, dwSize, hFile);
+                        object.netsrv.pszResponce[dwSize / sizeof(TCHAR)] = 0;
+                        break;
+                     default:
+                        break;
+                  }
+
+                  AddObject((NXC_OBJECT *)nx_memdup(&object, sizeof(NXC_OBJECT)), FALSE);
+               }
+            }
+            LockObjectIndex();
+            qsort(m_pIndexById, m_dwNumObjects, sizeof(INDEX), IndexCompare);
+            UnlockObjectIndex();
+            m_dwFlags |= NXC_SF_HAS_OBJECT_CACHE;
+         }
+      }
+
+      fclose(hFile);
+   }
+}
index a9ed010..9fbbd48 100644 (file)
@@ -32,6 +32,7 @@ NXCL_Session::NXCL_Session()
 {
    m_dwFlags = 0;
    m_dwMsgId = 0;
+   m_dwTimeStamp = 0;
    m_pEventHandler = NULL;
    m_dwState = STATE_DISCONNECTED;
    m_dwCommandTimeout = 10000;    // Default timeout is 10 seconds
index ac013c3..c352ce3 100644 (file)
@@ -74,6 +74,41 @@ void UnregisterSession(DWORD dwIndex)
 }
 
 
+//
+// Keep-alive thread
+//
+
+static THREAD_RESULT THREAD_CALL ClientKeepAliveThread(void *)
+{
+   int i, iSleepTime;
+   CSCPMessage msg;
+
+   // Read configuration
+   iSleepTime = ConfigReadInt("KeepAliveInterval", 60);
+
+   // Prepare keepalive message
+   msg.SetCode(CMD_KEEPALIVE);
+   msg.SetId(0);
+
+   while(1)
+   {
+      if (SleepAndCheckForShutdown(iSleepTime))
+         break;
+
+      msg.SetVariable(VID_TIMESTAMP, (DWORD)time(NULL));
+      MutexLock(m_hSessionListAccess, INFINITE);
+      for(i = 0; i < MAX_CLIENT_SESSIONS; i++)
+         if (m_pSessionList[i] != NULL)
+            if (m_pSessionList[i]->IsAuthenticated())
+               m_pSessionList[i]->SendMessage(&msg);
+      MutexUnlock(m_hSessionListAccess);
+   }
+
+   DbgPrintf(AF_DEBUG_MISC, _T("Client keep-alive thread terminated"));
+   return THREAD_OK;
+}
+
+
 //
 // Listener thread
 //
@@ -120,6 +155,9 @@ THREAD_RESULT THREAD_CALL ClientListener(void *)
    // Set up queue
    listen(sock, SOMAXCONN);
 
+   // Start client keep-alive thread
+   ThreadCreate(ClientKeepAliveThread, 0, NULL);
+
    // Wait for connection requests
    while(!ShutdownInProgress())
    {
index c2e050e..9670700 100644 (file)
@@ -153,12 +153,12 @@ BOOL NetObj::SaveCommonProperties(void)
          _sntprintf(szQuery, 512, 
                     _T("UPDATE object_properties SET name='%s',status=%d,"
                        "is_deleted=%d,image_id=%ld,inherit_access_rights=%d,"
-                       "last_modified WHERE object_id=%ld"),
+                       "last_modified=%ld WHERE object_id=%ld"),
                     m_szName, m_iStatus, m_bIsDeleted, m_dwImageId,
                     m_bInheritAccessRights, m_dwTimeStamp, m_dwId);
       else
          _sntprintf(szQuery, 512, 
-                    _T("INSERT INTO access_options (object_id,name,status,is_deleted,"
+                    _T("INSERT INTO object_properties (object_id,name,status,is_deleted,"
                        "image_id,inherit_access_rights,last_modified) "
                        "VALUES (%ld,'%s',%d,%d,%ld,%d,%ld)"),
                     m_dwId, m_szName, m_iStatus, m_bIsDeleted, m_dwImageId,
index e9ec207..8ce55ca 100644 (file)
@@ -741,7 +741,8 @@ void Node::StatusPoll(ClientSession *pSession, DWORD dwRqId)
    SendPollerMsg(dwRqId, "Finished status poll for node %s\r\n"
                          "Node status after poll is %s\r\n", m_szName, g_pszStatusName[m_iStatus]);
    m_pPollRequestor = NULL;
-   m_dwDynamicFlags &= ~NDF_QUEUED_FOR_STATUS_POLL;
+   if (dwRqId == 0)
+      m_dwDynamicFlags &= ~NDF_QUEUED_FOR_STATUS_POLL;
    PollerUnlock();
 }
 
@@ -1013,7 +1014,8 @@ void Node::ConfigurationPoll(ClientSession *pSession, DWORD dwRqId)
                  m_szName, bHasChanges ? _T(" ") : _T(" not "));
 
    // Finish configuration poll
-   m_dwDynamicFlags &= ~NDF_QUEUED_FOR_CONFIG_POLL;
+   if (dwRqId == 0)
+      m_dwDynamicFlags &= ~NDF_QUEUED_FOR_CONFIG_POLL;
    PollerUnlock();
    DbgPrintf(AF_DEBUG_DISCOVERY, "Finished configuration poll for node %s (ID: %d)", m_szName, m_dwId);
 
index 9652b16..769e8f3 100644 (file)
@@ -542,7 +542,7 @@ void ClientSession::ProcessingThread(void)
             SendServerInfo(pMsg->GetId());
             break;
          case CMD_GET_OBJECTS:
-            SendAllObjects(pMsg->GetId());
+            SendAllObjects(pMsg);
             break;
          case CMD_GET_EVENTS:
             SendAllEvents(pMsg->GetId());
@@ -1174,18 +1174,21 @@ void ClientSession::GenerateEventCode(DWORD dwRqId)
 // Send all objects to client
 //
 
-void ClientSession::SendAllObjects(DWORD dwRqId)
+void ClientSession::SendAllObjects(CSCPMessage *pRequest)
 {
-   DWORD i;
+   DWORD i, dwTimeStamp;
    CSCPMessage msg;
 
    // Send confirmation message
    msg.SetCode(CMD_REQUEST_COMPLETED);
-   msg.SetId(dwRqId);
+   msg.SetId(pRequest->GetId());
    msg.SetVariable(VID_RCC, RCC_SUCCESS);
    SendMessage(&msg);
    msg.DeleteAllVariables();
 
+   // Get client's last known time stamp
+   dwTimeStamp = pRequest->GetVariableLong(VID_TIMESTAMP);
+
    MutexLock(m_mutexSendObjects, INFINITE);
 
    // Prepare message
@@ -1194,7 +1197,8 @@ void ClientSession::SendAllObjects(DWORD dwRqId)
    // Send objects, one per message
    RWLockReadLock(g_rwlockIdIndex, INFINITE);
    for(i = 0; i < g_dwIdIndexSize; i++)
-      if (g_pIndexById[i].pObject->CheckAccessRights(m_dwUserId, OBJECT_ACCESS_READ))
+      if ((g_pIndexById[i].pObject->CheckAccessRights(m_dwUserId, OBJECT_ACCESS_READ)) &&
+          (g_pIndexById[i].pObject->TimeStamp() > dwTimeStamp))
       {
          g_pIndexById[i].pObject->CreateMessage(&msg);
          SendMessage(&msg);
index 040ccd5..6aa3adc 100644 (file)
@@ -293,7 +293,7 @@ private:
    void DebugPrintf(char *szFormat, ...);
    void SendServerInfo(DWORD dwRqId);
    void Login(CSCPMessage *pRequest);
-   void SendAllObjects(DWORD dwRqId);
+   void SendAllObjects(CSCPMessage *pRequest);
    void SendAllEvents(DWORD dwRqId);
    void SendAllConfigVars(void);
    void SendUserDB(DWORD dwRqId);
index 2ee9f86..ca90c3a 100644 (file)
@@ -144,6 +144,7 @@ public:
    DWORD Id(void) { return m_dwId; }
    const char *Name(void) { return m_szName; }
    int Status(void) { return m_iStatus; }
+   DWORD TimeStamp(void) { return m_dwTimeStamp; }
 
    BOOL IsModified(void) { return m_bIsModified; }
    BOOL IsDeleted(void) { return m_bIsDeleted; }