diff --git a/.gitignore b/.gitignore index 54b685e..fb4f905 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,7 @@ target/ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* + +/.classpath +/.project +/.settings/ diff --git a/pom.xml b/pom.xml index 098b2a7..59c9eb2 100644 --- a/pom.xml +++ b/pom.xml @@ -5,9 +5,9 @@ 4.0.0 - io.activestack + org.activestack syncengine - 1.1.16-SNAPSHOT + 1.1.17-updatetable-SNAPSHOT 3.2.4.RELEASE @@ -15,6 +15,7 @@ 1.6.1 1.19.0 1.19.0 + 1.9.5 @@ -34,6 +35,12 @@ 4.8.2 test + + org.mockito + mockito-core + ${mockito-core.version} + test + org.codehaus.jackson @@ -244,7 +251,10 @@ com.google.apis google-api-services-oauth2 - v2-rev78-1.19.0 + + v2-rev78-1.19.0 com.google.http-client @@ -293,6 +303,20 @@ + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.7 + 1.7 + + + + + com.springsource.repository.bundles.release @@ -315,4 +339,6 @@ http://maven.springframework.org/release + + \ No newline at end of file diff --git a/src/main/java/com/convergys/pulse/service/PulseHttpAuthProvider.java b/src/main/java/com/convergys/pulse/service/PulseHttpAuthProvider.java deleted file mode 100644 index a61e0a1..0000000 --- a/src/main/java/com/convergys/pulse/service/PulseHttpAuthProvider.java +++ /dev/null @@ -1,167 +0,0 @@ -package com.convergys.pulse.service; - -import com.percero.agents.auth.services.IAuthProvider; -import com.percero.agents.auth.vo.BasicAuthCredential; -import com.percero.agents.auth.vo.ServiceIdentifier; -import com.percero.agents.auth.vo.ServiceUser; -import com.percero.serial.map.SafeObjectMapper; -import org.apache.commons.io.IOUtils; -import org.apache.http.HttpResponse; -import org.apache.http.client.ClientProtocolException; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.conn.ClientConnectionManager; -import org.apache.http.conn.scheme.Scheme; -import org.apache.http.conn.scheme.SchemeRegistry; -import org.apache.http.conn.ssl.SSLSocketFactory; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.impl.conn.SingleClientConnManager; -import org.apache.log4j.Logger; -import org.codehaus.jackson.map.ObjectMapper; - -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; -import java.io.IOException; -import java.security.SecureRandom; -import java.security.cert.X509Certificate; -import java.util.HashMap; -import java.util.Map; - -/** - * AuthProvider implementation for Pulse to their http rest endpoint - * Created by Jonathan Samples on 8/27/15. - */ -public class PulseHttpAuthProvider implements IAuthProvider { - - private static Logger logger = Logger.getLogger(PulseHttpAuthProvider.class); - private static final String ID = "pulse_http"; - - public String getID() { - return ID; - } - - /** - * @param credential - String in : format - * @return - */ - public ServiceUser authenticate(String credential) { - ServiceUser result = null; - BasicAuthCredential cred = BasicAuthCredential.fromString(credential); - - String endpoint = hostPortAndContext +"/Authenticate"; - Map params = new HashMap(); - params.put("userDomainAndLogin", cred.getUsername()); - params.put("userPassword", cred.getPassword()); - - String body = makeRequest(endpoint, params); - - /** - * Result will be something like - * true - */ - if(body.contains("true")){ - result = new ServiceUser(); - result.getIdentifiers().add(new ServiceIdentifier("email", cred.getUsername())); - } - - return result; - } - - /** - * @param url - * @param params - * @return - */ - private String makeRequest(String url, Map params){ - String body = ""; - try { - HttpClient client = getHttpClient(); - String query = "?"; - for(String key : params.keySet()){ - query += key+"="+params.get(key)+"&"; - } - url += query; - - HttpGet request = new HttpGet(url); - HttpResponse response = client.execute(request); - logger.debug("Got response from auth hostPortAndContext: (" + response.getStatusLine().getStatusCode() + ")" + response.getStatusLine().getReasonPhrase()); - body = IOUtils.toString(response.getEntity().getContent(), "UTF8"); - } catch(ClientProtocolException e){ - logger.warn(e.getMessage(), e); - } catch(IOException ioe){ - logger.warn(ioe.getMessage(), ioe); - } - - return body; - } - - private HttpClient getHttpClient(){ - HttpClient httpClient = null; - if(trustAllCerts) { - try { - SSLContext sslContext = SSLContext.getInstance("SSL"); - - // set up a TrustManager that trusts everything - sslContext.init(null, new TrustManager[]{new X509TrustManager() { - public X509Certificate[] getAcceptedIssuers() { - return null; - } - - public void checkClientTrusted(X509Certificate[] certs, - String authType) { - } - - public void checkServerTrusted(X509Certificate[] certs, - String authType) { - } - }}, new SecureRandom()); - - SSLSocketFactory sf = new SSLSocketFactory(sslContext); - Scheme httpsScheme = new Scheme("https", sf, 443); - SchemeRegistry schemeRegistry = new SchemeRegistry(); - schemeRegistry.register(httpsScheme); - - ClientConnectionManager cm = new SingleClientConnManager(null, schemeRegistry); - httpClient = new DefaultHttpClient(cm, null); - } catch (Exception e) { - logger.error(e.getMessage(), e); - } - } - - // If don't trustAllCerts or an exception thrown then use the default one. - if(httpClient == null){ - httpClient = new DefaultHttpClient(); - } - - return httpClient; - } - - private String hostPortAndContext; - private ObjectMapper objectMapper; - - /** - * Only make this true for development when dealing with a self-signed certificate - */ - private boolean trustAllCerts = false; - - /** - * @param hostPortAndContext - e.g. https://some_host:5400/auth - * @param objectMapper - */ - public PulseHttpAuthProvider(String hostPortAndContext, ObjectMapper objectMapper, boolean trustAllCerts){ - this.hostPortAndContext = hostPortAndContext; - this.objectMapper = objectMapper; - this.trustAllCerts = trustAllCerts; - } - - /** - * For Testing - * @param args - */ - public static void main(String[] args){ - PulseHttpAuthProvider provider = new PulseHttpAuthProvider("https://localhost:8900/auth", new SafeObjectMapper(), true); - ServiceUser su = provider.authenticate("hoo:ha"); - System.out.println(su.toString()); - } -} diff --git a/src/main/java/com/convergys/pulse/service/PulseHttpAuthProviderFactory.java b/src/main/java/com/convergys/pulse/service/PulseHttpAuthProviderFactory.java deleted file mode 100644 index fbbffcc..0000000 --- a/src/main/java/com/convergys/pulse/service/PulseHttpAuthProviderFactory.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.convergys.pulse.service; - -import com.percero.agents.auth.services.AuthProviderRegistry; -import org.apache.log4j.Logger; -import org.codehaus.jackson.map.ObjectMapper; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -import javax.annotation.PostConstruct; - -/** - * Created by jonnysamps on 8/27/15. - */ -@Component -public class PulseHttpAuthProviderFactory { - - private static Logger logger = Logger.getLogger(PulseHttpAuthProviderFactory.class); - - @Autowired @Value("$pf{pulseHttpAuth.hostPortAndContext}") - String hostPortAndContext = null; - - @Autowired @Value("$pf{pulseHttpAuth.trustAllCerts:false}") - Boolean trustAllCerts = false; - - @Autowired - AuthProviderRegistry authProviderRegistry; - - @Autowired - ObjectMapper objectMapper; - - @PostConstruct - public void init(){ - if(hostPortAndContext != null){ - logger.info("Using PulseHttpAuthProvider with endpoint: "+hostPortAndContext); - PulseHttpAuthProvider provider = new PulseHttpAuthProvider(hostPortAndContext, objectMapper, trustAllCerts); - authProviderRegistry.addProvider(provider); - } - } -} diff --git a/src/main/java/com/convergys/pulse/vo/PulseUserInfo.java b/src/main/java/com/convergys/pulse/vo/PulseUserInfo.java deleted file mode 100644 index 13e6629..0000000 --- a/src/main/java/com/convergys/pulse/vo/PulseUserInfo.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.convergys.pulse.vo; - -/** - * Class to represent the response from the Pulse /retrieve_user endpoint - * Created by Jonathan Samples on 8/27/15. - */ -public class PulseUserInfo { - private String userLogin; - private String employeeId; - - public String getEmployeeId() { - return employeeId; - } - - public void setEmployeeId(String employeeId) { - this.employeeId = employeeId; - } - - public String getUserLogin() { - return userLogin; - } - - public void setUserLogin(String userLogin) { - this.userLogin = userLogin; - } -} diff --git a/src/main/java/com/percero/agents/auth/services/AuthProviderRegistry.java b/src/main/java/com/percero/agents/auth/services/AuthProviderRegistry.java index b2e43a1..baaecb0 100644 --- a/src/main/java/com/percero/agents/auth/services/AuthProviderRegistry.java +++ b/src/main/java/com/percero/agents/auth/services/AuthProviderRegistry.java @@ -35,7 +35,7 @@ public void addProvider(IAuthProvider provider){ if(providerMap.containsKey(provider.getID())) logger.warn("Non-unique auth provider ID: "+provider.getID()); - providerMap.put(provider.getID(), provider); + providerMap.put(provider.getID().toLowerCase(), provider); } /** @@ -44,7 +44,7 @@ public void addProvider(IAuthProvider provider){ * @return */ public IAuthProvider getProvider(String ID){ - return providerMap.get(ID); + return providerMap.get(ID.toLowerCase()); } /** @@ -53,6 +53,6 @@ public IAuthProvider getProvider(String ID){ * @return */ public boolean hasProvider(String ID){ - return providerMap.containsKey(ID); + return providerMap.containsKey(ID.toLowerCase()); } } diff --git a/src/main/java/com/percero/agents/sync/access/AccessManager.java b/src/main/java/com/percero/agents/sync/access/AccessManager.java index 33b1fae..b1dc5a4 100644 --- a/src/main/java/com/percero/agents/sync/access/AccessManager.java +++ b/src/main/java/com/percero/agents/sync/access/AccessManager.java @@ -870,6 +870,16 @@ public List getObjectAccessJournals(String className, String classId) th return result; } + @Override + public Set getClassAccessJournalIDs(String className) { + return null; + } + + @Override + public long getNumClientsInterestedInWholeClass(String className) { + return 0; + } + @SuppressWarnings("unchecked") public Map> getClientAccessess(Collection classIdPairs) throws Exception { Map> result = new HashMap>(); @@ -938,7 +948,7 @@ public List checkUserListAccessRights(List userIdList, String cl boolean hasAccess = true; // If the Read Query/Filter uses the ID, then we need to check against each ID here. if (isValidReadQuery) { - mappedClass.getReadQuery().setQueryParameters(query, classId, nextUserId); + mappedClass.getReadQuery().setQueryParameters(query.getQueryString(), classId, nextUserId); Number readFilterResult = (Number) query.uniqueResult(); if (readFilterResult == null || readFilterResult.intValue() <= 0) hasAccess = false; @@ -1257,6 +1267,34 @@ public String validateAndRetrieveCurrentClientId(String clientId, return null; } + @Override + public void updateWatcherFields(String category, String subCategory, + String fieldName, Collection fieldsToWatch, String[] params) { + // TODO Auto-generated method stub + + } + + @Override + public void addWatcherField(String category, String subCategory, + String fieldName, Collection collection, String[] params) { + // TODO Auto-generated method stub + + } + + @Override + public void addWatcherField(String category, String subCategory, + String fieldName, Collection collection) { + // TODO Auto-generated method stub + + } + + @Override + public void updateWatcherFields(String category, String subCategory, + String fieldName, Collection fieldsToWatch) { + // TODO Auto-generated method stub + + } + /* (non-Javadoc) * @see com.com.percero.agents.sync.services.IAccessManager#removeDeleteJournals(java.util.List) * diff --git a/src/main/java/com/percero/agents/sync/access/IAccessManager.java b/src/main/java/com/percero/agents/sync/access/IAccessManager.java index 7875993..0d3dc27 100644 --- a/src/main/java/com/percero/agents/sync/access/IAccessManager.java +++ b/src/main/java/com/percero/agents/sync/access/IAccessManager.java @@ -153,6 +153,8 @@ public Set findClientByUserIdDeviceId(String deviceId, String userId) * @throws Exception */ public List getObjectAccessJournals(String className, String classId) throws Exception; + Set getClassAccessJournalIDs(String className); + long getNumClientsInterestedInWholeClass(String className); public void removeAccessJournalsByObject(ClassIDPair classIdPair); @@ -196,10 +198,14 @@ public Set findClientByUserIdDeviceId(String deviceId, String userId) //////////////////////////////////////////////////////// public void addWatcherField(ClassIDPair classIdPair, String fieldName, Collection collection); public void addWatcherField(ClassIDPair classIdPair, String fieldName, Collection collection, String[] params); + public void addWatcherField(String category, String subCategory, String fieldName, Collection collection); + public void addWatcherField(String category, String subCategory, String fieldName, Collection collection, String[] params); public void addWatcherClient(ClassIDPair classIdPair, String fieldName, String clientId); public void addWatcherClient(ClassIDPair classIdPair, String fieldName, String clientId, String[] params); public void updateWatcherFields(ClassIDPair classIdPair, String fieldName, Collection fieldsToWatch); public void updateWatcherFields(ClassIDPair classIdPair, String fieldName, Collection fieldsToWatch, String[] params); + public void updateWatcherFields(String category, String subCategory, String fieldName, Collection fieldsToWatch); + public void updateWatcherFields(String category, String subCategory, String fieldName, Collection fieldsToWatch, String[] params); public void saveChangeWatcherResult(ClassIDPair classIdPair, String fieldName, Object result); public void saveChangeWatcherResult(ClassIDPair classIdPair, String fieldName, Object result, String[] params); public Boolean getChangeWatcherResultExists(ClassIDPair classIdPair, String fieldName); diff --git a/src/main/java/com/percero/agents/sync/access/RedisAccessManager.java b/src/main/java/com/percero/agents/sync/access/RedisAccessManager.java index cdcc43b..c392dec 100644 --- a/src/main/java/com/percero/agents/sync/access/RedisAccessManager.java +++ b/src/main/java/com/percero/agents/sync/access/RedisAccessManager.java @@ -26,7 +26,7 @@ import com.percero.agents.sync.cw.ChangeWatcherReporting; import com.percero.agents.sync.cw.IChangeWatcherHelper; import com.percero.agents.sync.cw.IChangeWatcherHelperFactory; -import com.percero.agents.sync.datastore.RedisDataStore; +import com.percero.agents.sync.datastore.ICacheDataStore; import com.percero.agents.sync.exceptions.ClientException; import com.percero.agents.sync.helpers.RedisPostClientHelper; import com.percero.agents.sync.services.IPushSyncHelper; @@ -75,9 +75,9 @@ public void setPostClientHelper(RedisPostClientHelper value) { //MongoOperations mongoOperations; @Autowired - RedisDataStore redisDataStore; - public void setRedisDataStore(RedisDataStore redisDataStore) { - this.redisDataStore = redisDataStore; + ICacheDataStore cacheDataStore; + public void setCacheDataStore(ICacheDataStore cacheDataStore) { + this.cacheDataStore = cacheDataStore; } @Autowired @@ -105,48 +105,48 @@ public void setPushSyncHelper(IPushSyncHelper pushSyncHelper) { public void createClient(String clientId, String userId, String deviceType, String deviceId) throws Exception { String clientUserKey = RedisKeyUtils.clientUser(userId); - redisDataStore.addSetValue(clientUserKey, clientId); + cacheDataStore.addSetValue(clientUserKey, clientId); if (StringUtils.hasText(deviceId)) { String userDeviceKey = RedisKeyUtils.userDeviceHash(userId); - redisDataStore.setHashValue(userDeviceKey, deviceId, clientId); + cacheDataStore.setHashValue(userDeviceKey, deviceId, clientId); String deviceKey = RedisKeyUtils.deviceHash(deviceId); - redisDataStore.addSetValue(deviceKey, clientId); + cacheDataStore.addSetValue(deviceKey, clientId); } // Set the client's userId. - redisDataStore.setValue(RedisKeyUtils.client(clientId), userId); + cacheDataStore.setValue(RedisKeyUtils.client(clientId), userId); // Add to ClientUser list - redisDataStore.addSetValue(clientUserKey, clientId); + cacheDataStore.addSetValue(clientUserKey, clientId); if (Client.PERSISTENT_TYPE.equalsIgnoreCase(deviceType)) { - redisDataStore.addSetValue(RedisKeyUtils.clientsPersistent(), clientId); + cacheDataStore.addSetValue(RedisKeyUtils.clientsPersistent(), clientId); } else { - redisDataStore.addSetValue(RedisKeyUtils.clientsNonPersistent(), clientId); + cacheDataStore.addSetValue(RedisKeyUtils.clientsNonPersistent(), clientId); } } public Boolean isNonPersistentClient(String clientId) { - return redisDataStore.getSetIsMember(RedisKeyUtils.clientsNonPersistent(), clientId); + return cacheDataStore.getSetIsMember(RedisKeyUtils.clientsNonPersistent(), clientId); } public String getClientUserId(String clientId) { - return (String) redisDataStore.getValue(RedisKeyUtils.client(clientId)); + return (String) cacheDataStore.getValue(RedisKeyUtils.client(clientId)); } public Boolean findClientByClientIdUserId(String clientId, String userId) throws Exception { - return redisDataStore.getSetIsMember(RedisKeyUtils.clientUser(userId), clientId); + return cacheDataStore.getSetIsMember(RedisKeyUtils.clientUser(userId), clientId); } @SuppressWarnings("unchecked") public Set findClientByUserIdDeviceId(String userId, String deviceId) throws Exception { - Set deviceClientIds = (Set) redisDataStore.getSetValue(RedisKeyUtils.deviceHash(deviceId)); + Set deviceClientIds = (Set) cacheDataStore.getSetValue(RedisKeyUtils.deviceHash(deviceId)); if (deviceClientIds == null) { deviceClientIds = new HashSet(1); } - String result = (String) redisDataStore.getHashValue(RedisKeyUtils.userDeviceHash(userId), deviceId); + String result = (String) cacheDataStore.getHashValue(RedisKeyUtils.userDeviceHash(userId), deviceId); if (!StringUtils.hasText(result)) { deviceClientIds.add(result); } @@ -155,9 +155,9 @@ public Set findClientByUserIdDeviceId(String userId, String deviceId) th } public Boolean findClientByClientId(String clientId) { - if (redisDataStore.getSetIsMember(RedisKeyUtils.clientsPersistent(), clientId)) + if (cacheDataStore.getSetIsMember(RedisKeyUtils.clientsPersistent(), clientId)) return true; - else if (redisDataStore.getSetIsMember(RedisKeyUtils.clientsNonPersistent(), clientId)) + else if (cacheDataStore.getSetIsMember(RedisKeyUtils.clientsNonPersistent(), clientId)) return true; else return false; @@ -186,13 +186,13 @@ public Boolean validateClientByClientId(String clientId, Boolean setClientTimeou @SuppressWarnings("unchecked") public Set validateClients(Collection clientIds) throws Exception { - Set validClients = (Set) redisDataStore.getSetsContainsMembers(CLIENT_KEYS_SEY, clientIds.toArray()); + Set validClients = (Set) cacheDataStore.getSetsContainsMembers(CLIENT_KEYS_SEY, clientIds.toArray()); return validClients; } @SuppressWarnings("unchecked") public Set validateClientsIncludeFromDeviceHistory(Map clientDevices) throws Exception { - Set validClients = (Set) redisDataStore.getSetsContainsMembers(CLIENT_KEYS_SEY, clientDevices.keySet().toArray()); + Set validClients = (Set) cacheDataStore.getSetsContainsMembers(CLIENT_KEYS_SEY, clientDevices.keySet().toArray()); // Now check each device to see if it has a corresponding clientId. Iterator> itrClientDevices = clientDevices.entrySet().iterator(); @@ -201,7 +201,7 @@ public Set validateClientsIncludeFromDeviceHistory(Map c String nextClient = nextClientDevice.getKey(); String nextDevice = nextClientDevice.getValue(); - if (redisDataStore.getSetIsMember(RedisKeyUtils.deviceHash(nextDevice), nextClient)) { + if (cacheDataStore.getSetIsMember(RedisKeyUtils.deviceHash(nextDevice), nextClient)) { validClients.add(nextClient); } } @@ -217,9 +217,9 @@ public String validateAndRetrieveCurrentClientId(String clientId, String deviceI } else { // Client is NOT current, but could still be valid. - if (redisDataStore.getSetIsMember(RedisKeyUtils.deviceHash(deviceId), clientId)) { + if (cacheDataStore.getSetIsMember(RedisKeyUtils.deviceHash(deviceId), clientId)) { // Client IS valid, now get current Client. - Set validDeviceClientIds = (Set) redisDataStore.getSetValue(RedisKeyUtils.deviceHash(deviceId)); + Set validDeviceClientIds = (Set) cacheDataStore.getSetValue(RedisKeyUtils.deviceHash(deviceId)); Iterator itrValidDeviceClientIds = validDeviceClientIds.iterator(); while (itrValidDeviceClientIds.hasNext()) { String nextClientId = itrValidDeviceClientIds.next(); @@ -250,8 +250,8 @@ public void registerClient(String clientId, String userId, String deviceId, Stri if (!isValidClient) createClient(clientId, userId, deviceType, deviceId); - redisDataStore.addSetValue(RedisKeyUtils.clientsLoggedIn(), clientId); - redisDataStore.deleteHashKey(RedisKeyUtils.clientsHibernated(), clientId); + cacheDataStore.addSetValue(RedisKeyUtils.clientsLoggedIn(), clientId); + cacheDataStore.deleteHashKey(RedisKeyUtils.clientsHibernated(), clientId); postClient(clientId); } @@ -263,8 +263,8 @@ public Boolean hibernateClient(String clientId, String userId) throws Exception // Make sure this client/user combo is a valid one. if (findClientByClientIdUserId(clientId, userId)) { // Remove the client from the LoggedIn list and add it to the Hibernated list. - redisDataStore.setHashValue(RedisKeyUtils.clientsHibernated(), clientId, System.currentTimeMillis()); - redisDataStore.removeSetValue(RedisKeyUtils.clientsLoggedIn(), clientId); + cacheDataStore.setHashValue(RedisKeyUtils.clientsHibernated(), clientId, System.currentTimeMillis()); + cacheDataStore.removeSetValue(RedisKeyUtils.clientsLoggedIn(), clientId); // NOTE: The client is still in either the "client persistent" or "client non-persistent" list and // is therefore still considered a valid client. @@ -294,16 +294,16 @@ public Boolean upgradeClient(String clientId, String deviceId, String deviceType try { // Remove from NonPersistent list. - redisDataStore.removeSetValue(RedisKeyUtils.clientsNonPersistent(), clientId); + cacheDataStore.removeSetValue(RedisKeyUtils.clientsNonPersistent(), clientId); // Add to Persistent List. - redisDataStore.addSetValue(RedisKeyUtils.clientsPersistent(), clientId); + cacheDataStore.addSetValue(RedisKeyUtils.clientsPersistent(), clientId); // Add to LoggedIn list - redisDataStore.addSetValue(RedisKeyUtils.clientsLoggedIn(), clientId); + cacheDataStore.addSetValue(RedisKeyUtils.clientsLoggedIn(), clientId); // Remove from Hibernated list - redisDataStore.deleteHashKey(RedisKeyUtils.clientsHibernated(), clientId); + cacheDataStore.deleteHashKey(RedisKeyUtils.clientsHibernated(), clientId); if (previousClientIds != null && !previousClientIds.isEmpty()) { Iterator itrPreviousClientIds = previousClientIds.iterator(); @@ -311,7 +311,7 @@ public Boolean upgradeClient(String clientId, String deviceId, String deviceType String nextPreviousClient = itrPreviousClientIds.next(); if (StringUtils.hasText(nextPreviousClient) && nextPreviousClient.equals(clientId)) { // Remove from NonPersistent list. - redisDataStore.removeSetValue(RedisKeyUtils.clientsNonPersistent(), nextPreviousClient); + cacheDataStore.removeSetValue(RedisKeyUtils.clientsNonPersistent(), nextPreviousClient); renameClient(nextPreviousClient, clientId); } @@ -319,7 +319,7 @@ public Boolean upgradeClient(String clientId, String deviceId, String deviceType } else { // Add to ClientUser list - redisDataStore.addSetValue(RedisKeyUtils.clientUser(userId), clientId); + cacheDataStore.addSetValue(RedisKeyUtils.clientUser(userId), clientId); } result = true; @@ -344,94 +344,94 @@ else if (thePreviousClient.equals(clientId)) { } // Get the existing UserId. - String userId = (String) redisDataStore.getValue(RedisKeyUtils.client(thePreviousClient)); + String userId = (String) cacheDataStore.getValue(RedisKeyUtils.client(thePreviousClient)); log.debug("Renaming client " + thePreviousClient + " to " + clientId); if (StringUtils.hasText(userId)) { log.debug("Renaming user " + userId + " from client " + thePreviousClient + " to " + clientId); // Remove Previous Client from ClientUser list - redisDataStore.removeSetValue(RedisKeyUtils.clientUser(userId), thePreviousClient); + cacheDataStore.removeSetValue(RedisKeyUtils.clientUser(userId), thePreviousClient); // Add to ClientUser list - redisDataStore.addSetValue(RedisKeyUtils.clientUser(userId), clientId); + cacheDataStore.addSetValue(RedisKeyUtils.clientUser(userId), clientId); // Update the UserDevice ClientID. String userDeviceHashKey = RedisKeyUtils.userDeviceHash(userId); - Collection userDeviceKeys = redisDataStore.getHashKeys(userDeviceHashKey); - Iterator itrUserDevices = userDeviceKeys.iterator(); + Collection userDeviceKeys = cacheDataStore.getHashKeys(userDeviceHashKey); + Iterator itrUserDevices = userDeviceKeys.iterator(); while (itrUserDevices.hasNext()) { - String nextDeviceKey = (String) itrUserDevices.next(); - if (thePreviousClient.equals(redisDataStore.getHashValue(userDeviceHashKey, nextDeviceKey))) { - redisDataStore.addSetValue(RedisKeyUtils.deviceHash(nextDeviceKey), clientId); - redisDataStore.setHashValue(userDeviceHashKey, nextDeviceKey, clientId); + String nextDeviceKey = itrUserDevices.next(); + if (thePreviousClient.equals(cacheDataStore.getHashValue(userDeviceHashKey, nextDeviceKey))) { + cacheDataStore.addSetValue(RedisKeyUtils.deviceHash(nextDeviceKey), clientId); + cacheDataStore.setHashValue(userDeviceHashKey, nextDeviceKey, clientId); } } // Set the Client's userId - redisDataStore.setValue(RedisKeyUtils.client(clientId), userId); + cacheDataStore.setValue(RedisKeyUtils.client(clientId), userId); // Remove the Previous Client's userId - redisDataStore.deleteKey(RedisKeyUtils.client(thePreviousClient)); + cacheDataStore.deleteKey(RedisKeyUtils.client(thePreviousClient)); } else { log.debug("Previous Client" + thePreviousClient + " has no corresponding UserID"); } // Swap NonPersistent list. - redisDataStore.swapSetValue(RedisKeyUtils.clientsNonPersistent(), thePreviousClient, clientId); + cacheDataStore.swapSetValue(RedisKeyUtils.clientsNonPersistent(), thePreviousClient, clientId); // Swap Persistent list. - redisDataStore.swapSetValue(RedisKeyUtils.clientsPersistent(), thePreviousClient, clientId); + cacheDataStore.swapSetValue(RedisKeyUtils.clientsPersistent(), thePreviousClient, clientId); // Swap LoggedIn list. - redisDataStore.swapSetValue(RedisKeyUtils.clientsLoggedIn(), thePreviousClient, clientId); + cacheDataStore.swapSetValue(RedisKeyUtils.clientsLoggedIn(), thePreviousClient, clientId); // Swap Hibernated list. - redisDataStore.swapHashKey(RedisKeyUtils.clientsHibernated(), thePreviousClient, clientId); + cacheDataStore.swapHashKey(RedisKeyUtils.clientsHibernated(), thePreviousClient, clientId); // Rename the UpdateJournals list. String prevUpdateJournalKey = RedisKeyUtils.updateJournal(thePreviousClient); - if (redisDataStore.hasKey(prevUpdateJournalKey)) { - if (!redisDataStore.renameIfAbsent(prevUpdateJournalKey, RedisKeyUtils.updateJournal(clientId))) { + if (cacheDataStore.hasKey(prevUpdateJournalKey)) { + if (!cacheDataStore.renameIfAbsent(prevUpdateJournalKey, RedisKeyUtils.updateJournal(clientId))) { // If new list already exists, then merge the two. - redisDataStore.setUnionAndStore(RedisKeyUtils.updateJournal(thePreviousClient), RedisKeyUtils.updateJournal(clientId), RedisKeyUtils.updateJournal(clientId)); + cacheDataStore.setUnionAndStore(RedisKeyUtils.updateJournal(thePreviousClient), RedisKeyUtils.updateJournal(clientId), RedisKeyUtils.updateJournal(clientId)); // Delete the old Key - redisDataStore.deleteKey(RedisKeyUtils.updateJournal(thePreviousClient)); + cacheDataStore.deleteKey(RedisKeyUtils.updateJournal(thePreviousClient)); } } // Rename the DeleteJournals list. String prevDeleteJournalKey = RedisKeyUtils.deleteJournal(thePreviousClient); - if (redisDataStore.hasKey(prevDeleteJournalKey)) { - if (!redisDataStore.renameIfAbsent(RedisKeyUtils.deleteJournal(thePreviousClient), RedisKeyUtils.deleteJournal(clientId))) { + if (cacheDataStore.hasKey(prevDeleteJournalKey)) { + if (!cacheDataStore.renameIfAbsent(RedisKeyUtils.deleteJournal(thePreviousClient), RedisKeyUtils.deleteJournal(clientId))) { // If new list already exists, then merge the two. - redisDataStore.setUnionAndStore(RedisKeyUtils.deleteJournal(thePreviousClient), RedisKeyUtils.deleteJournal(clientId), RedisKeyUtils.deleteJournal(clientId)); + cacheDataStore.setUnionAndStore(RedisKeyUtils.deleteJournal(thePreviousClient), RedisKeyUtils.deleteJournal(clientId), RedisKeyUtils.deleteJournal(clientId)); // Delete the old Key - redisDataStore.deleteKey(RedisKeyUtils.deleteJournal(thePreviousClient)); + cacheDataStore.deleteKey(RedisKeyUtils.deleteJournal(thePreviousClient)); } } // Merge sets of Access Journals. String prevClientAccessJournalKey = RedisKeyUtils.clientAccessJournal(thePreviousClient); - if (redisDataStore.hasKey(prevClientAccessJournalKey)) { - Set accessJournalIds = (Set)redisDataStore.getSetValue(prevClientAccessJournalKey); + if (cacheDataStore.hasKey(prevClientAccessJournalKey)) { + Set accessJournalIds = (Set)cacheDataStore.getSetValue(prevClientAccessJournalKey); Iterator itrAccessJournalIds = accessJournalIds.iterator(); while (itrAccessJournalIds.hasNext()) { String nextAccessJournalId = itrAccessJournalIds.next(); - redisDataStore.swapSetValue(RedisKeyUtils.accessJournal(nextAccessJournalId), thePreviousClient, clientId); + cacheDataStore.swapSetValue(RedisKeyUtils.accessJournal(nextAccessJournalId), thePreviousClient, clientId); } } - redisDataStore.setUnionAndStore(RedisKeyUtils.clientAccessJournal(clientId), RedisKeyUtils.clientAccessJournal(thePreviousClient), RedisKeyUtils.clientAccessJournal(clientId)); + cacheDataStore.setUnionAndStore(RedisKeyUtils.clientAccessJournal(clientId), RedisKeyUtils.clientAccessJournal(thePreviousClient), RedisKeyUtils.clientAccessJournal(clientId)); // Merge sets of ChangeWatchers. String prevWatcherClientKey = RedisKeyUtils.watcherClient(thePreviousClient); - if (redisDataStore.hasKey(prevWatcherClientKey)) { - Set changeWatcherIds = (Set)redisDataStore.getSetValue(prevWatcherClientKey); + if (cacheDataStore.hasKey(prevWatcherClientKey)) { + Set changeWatcherIds = (Set)cacheDataStore.getSetValue(prevWatcherClientKey); Iterator itrChangeWatcherIds = changeWatcherIds.iterator(); while (itrChangeWatcherIds.hasNext()) { String nextChangeWatcherId = itrChangeWatcherIds.next(); - redisDataStore.swapSetValue(nextChangeWatcherId, thePreviousClient, clientId); + cacheDataStore.swapSetValue(nextChangeWatcherId, thePreviousClient, clientId); } } - redisDataStore.setUnionAndStore(RedisKeyUtils.watcherClient(clientId), RedisKeyUtils.watcherClient(thePreviousClient), RedisKeyUtils.watcherClient(clientId)); + cacheDataStore.setUnionAndStore(RedisKeyUtils.watcherClient(clientId), RedisKeyUtils.watcherClient(thePreviousClient), RedisKeyUtils.watcherClient(clientId)); // // Rename the TransactionJournals list. // Collection transKeys = redisDataStore.keys(RedisKeyUtils.transactionJournal(clientId, "*")); @@ -454,10 +454,10 @@ public void logoutClient(String clientId, Boolean pleaseDestroyClient) { log.debug("Logging out client " + clientId + " [" + (pleaseDestroyClient != null && pleaseDestroyClient ? "T" : "F") + "]"); // Remove from LoggedIn Clients - redisDataStore.removeSetValue(RedisKeyUtils.clientsLoggedIn(), clientId); + cacheDataStore.removeSetValue(RedisKeyUtils.clientsLoggedIn(), clientId); // Check to see if this is a non-persistent client type. - Boolean isNonPersistent = redisDataStore.getSetIsMember(RedisKeyUtils.clientsNonPersistent(), clientId); + Boolean isNonPersistent = cacheDataStore.getSetIsMember(RedisKeyUtils.clientsNonPersistent(), clientId); if (isNonPersistent || pleaseDestroyClient) { destroyClient(clientId); @@ -468,7 +468,6 @@ public void logoutClient(String clientId, Boolean pleaseDestroyClient) { } } - @Override @Transactional public void destroyClient(String clientId) { if (!StringUtils.hasText(clientId)) { @@ -476,37 +475,37 @@ public void destroyClient(String clientId) { } // Get the client's userId. - String userId = (String) redisDataStore.getValue(RedisKeyUtils.client(clientId)); + String userId = (String) cacheDataStore.getValue(RedisKeyUtils.client(clientId)); Boolean validUser = StringUtils.hasText(userId); // Remove from ClientUser list if (validUser) { - redisDataStore.removeSetValue(RedisKeyUtils.clientUser(userId), clientId); + cacheDataStore.removeSetValue(RedisKeyUtils.clientUser(userId), clientId); } // Remove from Client's User ID. - redisDataStore.deleteKey(RedisKeyUtils.client(clientId)); + cacheDataStore.deleteKey(RedisKeyUtils.client(clientId)); // Remove from LoggedIn Clients - redisDataStore.removeSetValue(RedisKeyUtils.clientsLoggedIn(), clientId); + cacheDataStore.removeSetValue(RedisKeyUtils.clientsLoggedIn(), clientId); // Remove from Hibernated Clients - redisDataStore.deleteHashKey(RedisKeyUtils.clientsHibernated(), clientId); + cacheDataStore.deleteHashKey(RedisKeyUtils.clientsHibernated(), clientId); // Remove from NonPersistent Clients - redisDataStore.removeSetValue(RedisKeyUtils.clientsNonPersistent(), clientId); + cacheDataStore.removeSetValue(RedisKeyUtils.clientsNonPersistent(), clientId); // Remove from Persistent Clients - redisDataStore.removeSetValue(RedisKeyUtils.clientsPersistent(), clientId); + cacheDataStore.removeSetValue(RedisKeyUtils.clientsPersistent(), clientId); // Delete all UpdateJournals for this Client. String updateJournalKey = RedisKeyUtils.updateJournal(clientId); // Set updateJournalIds = (Set)redisDataStore.getSetValue(updateJournalKey); // redisDataStore.removeSetsValue(updateJournalIds, clientId); - redisDataStore.deleteKey(updateJournalKey); + cacheDataStore.deleteKey(updateJournalKey); // Delete all DeleteJournals for this Client. - redisDataStore.deleteKey(RedisKeyUtils.deleteJournal(clientId)); + cacheDataStore.deleteKey(RedisKeyUtils.deleteJournal(clientId)); // // Delete all TransactionJournals for this Client. // Collection transJournalKeys = redisDataStore.keys(RedisKeyUtils.transactionJournal(clientId, "*")); @@ -515,25 +514,25 @@ public void destroyClient(String clientId) { // Delete Client's UserDevice. if (validUser) { String userDeviceHashKey = RedisKeyUtils.userDeviceHash(userId); - Collection userDeviceKeys = redisDataStore.getHashKeys(userDeviceHashKey); + Collection userDeviceKeys = cacheDataStore.getHashKeys(userDeviceHashKey); int originalSize = userDeviceKeys.size(); int countRemoved = 0; - Iterator itrUserDevices = userDeviceKeys.iterator(); + Iterator itrUserDevices = userDeviceKeys.iterator(); while (itrUserDevices.hasNext()) { - String nextDeviceKey = (String) itrUserDevices.next(); - if (clientId.equals(redisDataStore.getHashValue(userDeviceHashKey, nextDeviceKey))) { + String nextDeviceKey = itrUserDevices.next(); + if (clientId.equals(cacheDataStore.getHashValue(userDeviceHashKey, nextDeviceKey))) { // Since the client id being destroyed, the whole device history related to this client is also to be destroyed. // However, as a safety precaution, set the expiration for the device hash. - redisDataStore.expire(RedisKeyUtils.deviceHash(nextDeviceKey), userDeviceTimeout, TimeUnit.SECONDS); - redisDataStore.removeSetValue(RedisKeyUtils.deviceHash(nextDeviceKey), clientId); - redisDataStore.deleteHashKey(userDeviceHashKey, nextDeviceKey); + cacheDataStore.expire(RedisKeyUtils.deviceHash(nextDeviceKey), userDeviceTimeout, TimeUnit.SECONDS); + cacheDataStore.removeSetValue(RedisKeyUtils.deviceHash(nextDeviceKey), clientId); + cacheDataStore.deleteHashKey(userDeviceHashKey, nextDeviceKey); countRemoved++; } } if (countRemoved >= originalSize) { // Remove the UserDevice Hash. - redisDataStore.deleteKey(userDeviceHashKey); + cacheDataStore.deleteKey(userDeviceHashKey); } } @@ -566,29 +565,40 @@ public void destroyClient(String clientId) { * This function checks to see if a User has no more UserDevices. If they do not have * any, then it removes all the User's entries in ALL AccessJournal sets. * - * @param userId - * @param clientId + * @param clientId */ @SuppressWarnings("unchecked") @Transactional private void deleteClientAccessJournals(String clientId) { String clientAccessJournalsKey = RedisKeyUtils.clientAccessJournal(clientId); - Set clientAccessJournals = (Set) redisDataStore.getSetValue(clientAccessJournalsKey); - redisDataStore.removeSetsValue(RedisKeyUtils.ACCESS_JOURNAL_PREFIX, clientAccessJournals, clientId); + Set clientAccessJournals = (Set) cacheDataStore.getSetValue(clientAccessJournalsKey); + cacheDataStore.removeSetsValue(RedisKeyUtils.ACCESS_JOURNAL_PREFIX, clientAccessJournals, clientId); // Now remove the Client's AccessJournal key. - redisDataStore.deleteKey(clientAccessJournalsKey); + cacheDataStore.deleteKey(clientAccessJournalsKey); + + // Check to see if each Object's access journal set is empty and if so remove it + // from the class access journal set + for(String caj : clientAccessJournals){ + if(cacheDataStore.getSetIsEmpty(RedisKeyUtils.ACCESS_JOURNAL_PREFIX+caj)){ + String[] parts = caj.split(":"); + String className = parts[0]; + String ID = parts[1]; + String classAccessJournalKey = RedisKeyUtils.classAccessJournal(className); + cacheDataStore.removeSetValue(classAccessJournalKey, ID); + } + } } @SuppressWarnings("unchecked") @Transactional private void deleteClientWatchers(String clientId) { String watcherClientKey = RedisKeyUtils.watcherClient(clientId); - Set watcherClientIds = (Set) redisDataStore.getSetValue(watcherClientKey); - redisDataStore.removeSetsValue("", watcherClientIds, clientId); + Set watcherClientIds = (Set) cacheDataStore.getSetValue(watcherClientKey); + cacheDataStore.removeSetsValue("", watcherClientIds, clientId); // Now remove the Client's Watcher key. - redisDataStore.deleteKey(watcherClientKey); + cacheDataStore.deleteKey(watcherClientKey); } /** @@ -619,7 +629,7 @@ public void postAccessJournals() { Iterator itrAccessJournals = accessJournalsToDelete.iterator(); while (itrAccessJournals.hasNext()) { String nextAccessJournal = itrAccessJournals.next(); - Set accessJournalClients = (Set)redisDataStore.getSetValue(nextAccessJournal); + Set accessJournalClients = (Set)cacheDataStore.getSetValue(nextAccessJournal); if (accessJournalClients != null) { Set clientIdsToRemove = new HashSet(); @@ -638,7 +648,7 @@ else if (!findClientByClientId(nextClientId) || !StringUtils.hasText(getClientUs } if (!clientIdsToRemove.isEmpty()) { - redisDataStore.removeSetValues(nextAccessJournal, clientIdsToRemove); + cacheDataStore.removeSetValues(nextAccessJournal, clientIdsToRemove); } } } @@ -689,17 +699,17 @@ public void postClients() { } // Now check Hibernating clients to see if any of those need to be removed - Map hibernatingClients = redisDataStore.getHashEntries(RedisKeyUtils.clientsHibernated()); - Iterator> itrHibernatingClientsEntries = hibernatingClients.entrySet().iterator(); + Map hibernatingClients = cacheDataStore.getHashEntries(RedisKeyUtils.clientsHibernated()); + Iterator> itrHibernatingClientsEntries = hibernatingClients.entrySet().iterator(); while (itrHibernatingClientsEntries.hasNext()) { - Map.Entry nextEntry = itrHibernatingClientsEntries.next(); + Map.Entry nextEntry = itrHibernatingClientsEntries.next(); String nextHibernatingClientId = null; try { nextHibernatingClientId = (String) nextEntry.getKey(); } catch(Exception e) { // This is an invalid key, so remove it from the hash. try { - redisDataStore.deleteHashKey(RedisKeyUtils.clientsHibernated(), nextEntry.getKey()); + cacheDataStore.deleteHashKey(RedisKeyUtils.clientsHibernated(), nextEntry.getKey()); } catch(Exception e1) { log.error("Invalid hash key in " + RedisKeyUtils.clientsHibernated()); } @@ -712,7 +722,7 @@ public void postClients() { } catch(Exception e) { // This timeout is not in a valid format, set to current time and move on. try { - redisDataStore.setHashValue(RedisKeyUtils.clientsHibernated(), nextHibernatingClientId, nextEntry.getValue()); + cacheDataStore.setHashValue(RedisKeyUtils.clientsHibernated(), nextHibernatingClientId, nextEntry.getValue()); } catch(Exception e1) { log.error("Invalid hash value in " + RedisKeyUtils.clientsHibernated()); } @@ -758,7 +768,7 @@ public void saveUpdateJournalClients(ClassIDPair pair, Collection client } String key = RedisKeyUtils.updateJournal(nextClient); - redisDataStore.addSetValue(key, classValue); + cacheDataStore.addSetValue(key, classValue); } } catch(Exception e) { @@ -768,12 +778,12 @@ public void saveUpdateJournalClients(ClassIDPair pair, Collection client } public Long deleteUpdateJournal(String clientId, String className, String classId) { - return redisDataStore.removeSetValue(RedisKeyUtils.updateJournal(clientId), RedisKeyUtils.classIdPair(className, classId)); + return cacheDataStore.removeSetValue(RedisKeyUtils.updateJournal(clientId), RedisKeyUtils.classIdPair(className, classId)); } public void deleteUpdateJournals(String clientId, ClassIDPair[] objects) { for(ClassIDPair nextObject : objects) { - redisDataStore.removeSetValue(RedisKeyUtils.updateJournal(clientId), RedisKeyUtils.classIdPair(nextObject.getClassName(), nextObject.getID())); + cacheDataStore.removeSetValue(RedisKeyUtils.updateJournal(clientId), RedisKeyUtils.classIdPair(nextObject.getClassName(), nextObject.getID())); } } @@ -791,7 +801,7 @@ public void saveDeleteJournalClients(ClassIDPair pair, Collection client } String key = RedisKeyUtils.deleteJournal(nextClient); - redisDataStore.addSetValue(key, classValue); + cacheDataStore.addSetValue(key, classValue); } } catch(Exception e) { @@ -801,12 +811,12 @@ public void saveDeleteJournalClients(ClassIDPair pair, Collection client } public Long deleteDeleteJournal(String clientId, String className, String classId) { - return redisDataStore.removeSetValue(RedisKeyUtils.deleteJournal(clientId), RedisKeyUtils.classIdPair(className, classId)); + return cacheDataStore.removeSetValue(RedisKeyUtils.deleteJournal(clientId), RedisKeyUtils.classIdPair(className, classId)); } public void deleteDeleteJournals(String clientId, ClassIDPair[] objects) { for(ClassIDPair nextObject : objects) { - redisDataStore.removeSetValue(RedisKeyUtils.deleteJournal(clientId), RedisKeyUtils.classIdPair(nextObject.getClassName(), nextObject.getID())); + cacheDataStore.removeSetValue(RedisKeyUtils.deleteJournal(clientId), RedisKeyUtils.classIdPair(nextObject.getClassName(), nextObject.getID())); } } @@ -829,10 +839,17 @@ private Long upsertRedisAccessJournal(String userId, String clientId, String cla try { // Need to add the ObjectID to the Client's AccessJournals set. String clientAccessJournalKey = RedisKeyUtils.clientAccessJournal(clientId); - redisDataStore.addSetValue(clientAccessJournalKey, RedisKeyUtils.objectId(className, classId)); + cacheDataStore.addSetValue(clientAccessJournalKey, RedisKeyUtils.objectId(className, classId)); + // Add to the class's AccessJournals set + if(classId != null && !classId.isEmpty()) { // && !classId.equals("0")) { + log.info("Adding to class AccessJournals: "+classId); + String classAccessJournalKey = RedisKeyUtils.classAccessJournal(className); + cacheDataStore.addSetValue(classAccessJournalKey, classId); + } + // Need to add the ClientID to the Object's AccessJournal set. - return redisDataStore.addSetValue(key, clientId); + return cacheDataStore.addSetValue(key, clientId); } catch(Exception e) { log.error("Unable to upsertRedisAccessJournal", e); } finally { @@ -868,7 +885,7 @@ public List getObjectAccessJournals(String className, String classId) th //moveAccessJournals(); // TODO: Use a class id map instead of class name for faster lookup. // Use hash of full class name to generate ID? - Set redisClassIdSet = (Set) redisDataStore.getSetUnion(objectAccessJournalKey, allObjectAccessJournalKey); + Set redisClassIdSet = (Set) cacheDataStore.getSetUnion(objectAccessJournalKey, allObjectAccessJournalKey); result.addAll(redisClassIdSet); // RedisKeyUtils.accessJournal(className, classId), // RedisKeyUtils.accessJournal(className, "0")); @@ -881,6 +898,15 @@ public List getObjectAccessJournals(String className, String classId) th return result; } + @SuppressWarnings("unchecked") + public Set getClassAccessJournalIDs(String className){ + return (Set) cacheDataStore.getSetValue(RedisKeyUtils.classAccessJournal(className)); + } + + public long getNumClientsInterestedInWholeClass(String className){ + return cacheDataStore.getSetSize(RedisKeyUtils.accessJournal(className,"0")); + } + public List checkUserListAccessRights(Collection clientIdList, String className, String classId) throws Exception { List result = new ArrayList(); @@ -901,15 +927,15 @@ public Collection getClientUpdateJournals(String clientId, Boolean logge Collection updateJournalSet = null; if (loggedInOnly) { - if (redisDataStore.getSetIsMember(RedisKeyUtils.clientsLoggedIn(), clientId)) { - updateJournalSet = (Set) redisDataStore.getSetValue(key); + if (cacheDataStore.getSetIsMember(RedisKeyUtils.clientsLoggedIn(), clientId)) { + updateJournalSet = (Set) cacheDataStore.getSetValue(key); } else { updateJournalSet = new HashSet(0); } } else { - updateJournalSet = (Set) redisDataStore.getSetValue(key); + updateJournalSet = (Set) cacheDataStore.getSetValue(key); } return updateJournalSet; @@ -924,15 +950,15 @@ public Collection getClientDeleteJournals(String clientId, Boolean logge Collection deleteJournalSet = null; if (loggedInOnly) { - if (redisDataStore.getSetIsMember(RedisKeyUtils.clientsLoggedIn(), clientId)) { - deleteJournalSet = (Set) redisDataStore.getSetValue(key); + if (cacheDataStore.getSetIsMember(RedisKeyUtils.clientsLoggedIn(), clientId)) { + deleteJournalSet = (Set) cacheDataStore.getSetValue(key); } else { deleteJournalSet = new HashSet(0); } } else { - deleteJournalSet = (Set) redisDataStore.getSetValue(key); + deleteJournalSet = (Set) cacheDataStore.getSetValue(key); } return deleteJournalSet; @@ -943,7 +969,7 @@ public Collection getClientDeleteJournals(String clientId, Boolean logge */ public void removeUpdateJournals(String clientId, Collection updateJournalsToRemove) throws Exception { String key = RedisKeyUtils.updateJournal(clientId); - redisDataStore.removeSetValues(key, updateJournalsToRemove); + cacheDataStore.removeSetValues(key, updateJournalsToRemove); } /* (non-Javadoc) @@ -959,7 +985,7 @@ public void removeAccessJournalsByObject(ClassIDPair classIdPair) { String objectId = RedisKeyUtils.objectId(classIdPair.getClassName(), classIdPair.getID()); String accessJournalKey = RedisKeyUtils.accessJournal(classIdPair.getClassName(), classIdPair.getID()); - Set clientIds = (Set) redisDataStore.getSetValue(accessJournalKey); + Set clientIds = (Set) cacheDataStore.getSetValue(accessJournalKey); Set clientAccessJournalKeys = new HashSet(clientIds.size()); Iterator itrClientIds = clientIds.iterator(); while (itrClientIds.hasNext()) { @@ -969,7 +995,7 @@ public void removeAccessJournalsByObject(ClassIDPair classIdPair) { clientAccessJournalKeys.add(clientAccessJournalsKey); } - redisDataStore.removeSetsValue(clientAccessJournalKeys, objectId); + cacheDataStore.removeSetsValue(clientAccessJournalKeys, objectId); // Iterator itrClientIds = clientIds.iterator(); // while (itrClientIds.hasNext()) { @@ -984,16 +1010,19 @@ public void removeAccessJournalsByObject(ClassIDPair classIdPair) { // } // } + String classAccessJournalKey = RedisKeyUtils.classAccessJournal(classIdPair.getClassName()); + cacheDataStore.removeSetValue(classAccessJournalKey, classIdPair.getID()); + // Now delete the AccessJournal record. - redisDataStore.deleteKey(accessJournalKey); + cacheDataStore.deleteKey(accessJournalKey); } public void removeObjectModJournalsByObject(ClassIDPair classIdPair) { - redisDataStore.deleteKey(RedisKeyUtils.objectModJournal(classIdPair.getClassName(), classIdPair.getID())); + cacheDataStore.deleteKey(RedisKeyUtils.objectModJournal(classIdPair.getClassName(), classIdPair.getID())); } public void removeHistoricalObjectsByObject(ClassIDPair classIdPair) { - redisDataStore.deleteKey(RedisKeyUtils.historicalObject(classIdPair.getClassName(), classIdPair.getID())); + cacheDataStore.deleteKey(RedisKeyUtils.historicalObject(classIdPair.getClassName(), classIdPair.getID())); } @@ -1012,11 +1041,17 @@ public void addWatcherField(ClassIDPair classIdPair, String fieldName, Collectio addWatcherField(classIdPair, fieldName, collection, null); } public void addWatcherField(ClassIDPair classIdPair, String fieldName, Collection collection, String[] params) { + addWatcherField(classIdPair.getClassName(), classIdPair.getID(), fieldName, collection, params); + } + public void addWatcherField(String category, String subCategory, String fieldName, Collection collection) { + addWatcherField(category, subCategory, fieldName, collection, null); + } + public void addWatcherField(String category, String subCategory, String fieldName, Collection collection, String[] params) { String fieldWatcherId = ""; if (params != null) - fieldWatcherId = RedisKeyUtils.fieldWatcher(RedisKeyUtils.changeWatcherWithParams(classIdPair.getClassName(), classIdPair.getID(), fieldName, params)); + fieldWatcherId = RedisKeyUtils.fieldWatcher(RedisKeyUtils.changeWatcherWithParams(category, subCategory, fieldName, params)); else - fieldWatcherId = RedisKeyUtils.fieldWatcher(RedisKeyUtils.changeWatcher(classIdPair.getClassName(), classIdPair.getID(), fieldName)); + fieldWatcherId = RedisKeyUtils.fieldWatcher(RedisKeyUtils.changeWatcher(category, subCategory, fieldName)); if (collection != null) collection.add(fieldWatcherId); } @@ -1035,33 +1070,41 @@ public void addWatcherClient(ClassIDPair classIdPair, String fieldName, String c changeWatcherId = RedisKeyUtils.clientWatcher(RedisKeyUtils.changeWatcher(classIdPair.getClassName(), classIdPair.getID(), fieldName)); // Add the ClientID to the ChangeWatcher client Set - redisDataStore.addSetValue(changeWatcherId, clientId); + cacheDataStore.addSetValue(changeWatcherId, clientId); // Also add the ChangeWatcher ID to the client ChangeWatcher Set - redisDataStore.addSetValue(RedisKeyUtils.watcherClient(clientId), changeWatcherId); + cacheDataStore.addSetValue(RedisKeyUtils.watcherClient(clientId), changeWatcherId); } public void updateWatcherFields(ClassIDPair classIdPair, String fieldName, Collection fieldsToWatch) { updateWatcherFields(classIdPair, fieldName, fieldsToWatch, null); } + public void updateWatcherFields(ClassIDPair classIdPair, String fieldName, Collection fieldsToWatch, String[] params) { + updateWatcherFields(classIdPair.getClassName(), classIdPair.getID(), fieldName, fieldsToWatch, params); + } + + public void updateWatcherFields(String category, String subCategory, String fieldName, Collection fieldsToWatch) { + updateWatcherFields(category, subCategory, fieldName, fieldsToWatch, null); + } + @SuppressWarnings("unchecked") @Transactional - public void updateWatcherFields(ClassIDPair classIdPair, String fieldName, Collection fieldsToWatch, String[] params) { + public void updateWatcherFields(String category, String subCategory, String fieldName, Collection fieldsToWatch, String[] params) { String changeWatcherId = ""; String watcherField = ""; if (params != null) { - changeWatcherId = RedisKeyUtils.changeWatcherWithParams(classIdPair.getClassName(), classIdPair.getID(), fieldName, params); - watcherField = RedisKeyUtils.watcherField(RedisKeyUtils.changeWatcher(classIdPair.getClassName(), classIdPair.getID(), fieldName)); + changeWatcherId = RedisKeyUtils.changeWatcherWithParams(category, subCategory, fieldName, params); + watcherField = RedisKeyUtils.watcherField(RedisKeyUtils.changeWatcher(category, subCategory, fieldName)); } else { - changeWatcherId = RedisKeyUtils.changeWatcher(classIdPair.getClassName(), classIdPair.getID(), fieldName); + changeWatcherId = RedisKeyUtils.changeWatcher(category, subCategory, fieldName); watcherField = RedisKeyUtils.watcherField(changeWatcherId); } - Collection watchedFields = (Set) redisDataStore.getSetValue(watcherField); + Collection watchedFields = (Set) cacheDataStore.getSetValue(watcherField); // Remove current set of fields to watch and replace with new set. - redisDataStore.replaceSet(watcherField, fieldsToWatch); + cacheDataStore.replaceSet(watcherField, fieldsToWatch); if (fieldsToWatch != null) { Iterator itrFieldsToWatch = fieldsToWatch.iterator(); @@ -1069,7 +1112,7 @@ public void updateWatcherFields(ClassIDPair classIdPair, String fieldName, Colle String nextField = itrFieldsToWatch.next(); // Also add this ChangeWatcher from the FieldWatcher. - redisDataStore.addSetValue(nextField, changeWatcherId); + cacheDataStore.addSetValue(nextField, changeWatcherId); // Keep track of field that is being watched at an object level. String[] nextFieldArr = nextField.split(":"); @@ -1091,7 +1134,7 @@ public void updateWatcherFields(ClassIDPair classIdPair, String fieldName, Colle } String objectWatchedFieldKey = RedisKeyUtils.objectWatchedFields(nextFieldClassName, nextFieldClassId); - redisDataStore.setHashValue(objectWatchedFieldKey, hashKey, nextField); + cacheDataStore.setHashValue(objectWatchedFieldKey, hashKey, nextField); } if (watchedFields != null) { @@ -1108,24 +1151,27 @@ public void updateWatcherFields(ClassIDPair classIdPair, String fieldName, Colle String nextOldField = itrOldWatchedFields.next(); // Also remove this ChangeWatcher from the FieldWatcher. - redisDataStore.removeSetValue(nextOldField, changeWatcherId); + cacheDataStore.removeSetValue(nextOldField, changeWatcherId); } } } - @SuppressWarnings("unchecked") public void removeChangeWatchersByObject(ClassIDPair classIdPair) { - String classKey = RedisKeyUtils.changeWatcherClass(classIdPair.getClassName(), classIdPair.getID()); + removeChangeWatchers(classIdPair.getClassName(), classIdPair.getID()); + } + @SuppressWarnings("unchecked") + public void removeChangeWatchers(String category, String subCategory) { + String categoryKey = RedisKeyUtils.changeWatcherClass(category, subCategory); // Get all change watcher values associated with this object. - Set changeWatcherValueKeys = redisDataStore.getHashKeys(classKey); - Iterator itrChangeWatcherValueKeys = changeWatcherValueKeys.iterator(); + Set changeWatcherValueKeys = cacheDataStore.getHashKeys(categoryKey); + Iterator itrChangeWatcherValueKeys = changeWatcherValueKeys.iterator(); while (itrChangeWatcherValueKeys.hasNext()) { - String nextChangeWatcherValueKey = (String) itrChangeWatcherValueKeys.next(); + String nextChangeWatcherValueKey = itrChangeWatcherValueKeys.next(); // If this is a RESULT key, then add it to the list to check. if (nextChangeWatcherValueKey.endsWith(":" + RedisKeyUtils.RESULT)) { int resultIndex = nextChangeWatcherValueKey.lastIndexOf(":" + RedisKeyUtils.RESULT); - String changeWatcherKey = classKey + ":" + nextChangeWatcherValueKey.substring(0, resultIndex); + String changeWatcherKey = categoryKey + ":" + nextChangeWatcherValueKey.substring(0, resultIndex); String key = changeWatcherKey; String clientWatcherKey = RedisKeyUtils.clientWatcher(changeWatcherKey); @@ -1133,36 +1179,36 @@ public void removeChangeWatchersByObject(ClassIDPair classIdPair) { // For every watcherField, find the corresponding set and remove this changeWatcherKey // from that set. String watcherField = RedisKeyUtils.watcherField(changeWatcherKey); - Set watcherFieldValues = (Set) redisDataStore.getSetValue(watcherField); + Set watcherFieldValues = (Set) cacheDataStore.getSetValue(watcherField); Iterator itrWatcherFieldValues = watcherFieldValues.iterator(); while (itrWatcherFieldValues.hasNext()) { String nextWatcherFieldValue = itrWatcherFieldValues.next(); - redisDataStore.removeSetValue(nextWatcherFieldValue, changeWatcherKey); + cacheDataStore.removeSetValue(nextWatcherFieldValue, changeWatcherKey); } String fieldWatcher = RedisKeyUtils.fieldWatcher(changeWatcherKey); // Now remove all keys associated with this Change Watcher Value. - redisDataStore.deleteKey(key); - redisDataStore.deleteKey(clientWatcherKey); - redisDataStore.deleteKey(watcherField); - redisDataStore.deleteKey(fieldWatcher); + cacheDataStore.deleteKey(key); + cacheDataStore.deleteKey(clientWatcherKey); + cacheDataStore.deleteKey(watcherField); + cacheDataStore.deleteKey(fieldWatcher); } } - String objectWatchedFieldKey = RedisKeyUtils.objectWatchedFields(classIdPair.getClassName(), classIdPair.getID()); - Set objectWatcherValueKeys = redisDataStore.getHashKeys(objectWatchedFieldKey); - Iterator itrObjectWatcherValueKeys = objectWatcherValueKeys.iterator(); + String objectWatchedFieldKey = RedisKeyUtils.objectWatchedFields(category, subCategory); + Set objectWatcherValueKeys = cacheDataStore.getHashKeys(objectWatchedFieldKey); + Iterator itrObjectWatcherValueKeys = objectWatcherValueKeys.iterator(); while (itrObjectWatcherValueKeys.hasNext()) { - String nextObjectWatcherValueKey = (String) itrObjectWatcherValueKeys.next(); + String nextObjectWatcherValueKey = itrObjectWatcherValueKeys.next(); // Get all fields for this object that are being watched and remove them. - String changeWatcherKey = (String) redisDataStore.getHashValue(objectWatchedFieldKey, nextObjectWatcherValueKey); - redisDataStore.deleteKey(changeWatcherKey); + String changeWatcherKey = (String) cacheDataStore.getHashValue(objectWatchedFieldKey, nextObjectWatcherValueKey); + cacheDataStore.deleteKey(changeWatcherKey); } - redisDataStore.deleteKey(objectWatchedFieldKey); // Removes ALL change watcher values for this Object. + cacheDataStore.deleteKey(objectWatchedFieldKey); // Removes ALL change watcher values for this Object. - redisDataStore.deleteKey(classKey); // Removes ALL change watcher values for this Object. + cacheDataStore.deleteKey(categoryKey); // Removes ALL change watcher values for this Object. } public void saveChangeWatcherResult(ClassIDPair classIdPair, String fieldName, Object result) { @@ -1176,7 +1222,7 @@ public void saveChangeWatcherResult(ClassIDPair classIdPair, String fieldName, O HashMap resultHash = new HashMap(); resultHash.put(RedisKeyUtils.changeWatcherValueTimestamp(fieldName, params), System.currentTimeMillis()); resultHash.put(RedisKeyUtils.changeWatcherValueResult(fieldName, params), result); - redisDataStore.setAllHashValues(classKey, resultHash); + cacheDataStore.setAllHashValues(classKey, resultHash); } public void removeChangeWatcherResult(ClassIDPair classIdPair, String fieldName) { @@ -1187,8 +1233,8 @@ public void removeChangeWatcherResult(ClassIDPair classIdPair, String fieldName) public void removeChangeWatcherResult(ClassIDPair classIdPair, String fieldName, String[] params) { String key = RedisKeyUtils.changeWatcherClass(classIdPair.getClassName(), classIdPair.getID()); - redisDataStore.deleteHashKey(key, RedisKeyUtils.changeWatcherValueTimestamp(fieldName, params)); - redisDataStore.deleteHashKey(key, RedisKeyUtils.changeWatcherValueResult(fieldName, params)); + cacheDataStore.deleteHashKey(key, RedisKeyUtils.changeWatcherValueTimestamp(fieldName, params)); + cacheDataStore.deleteHashKey(key, RedisKeyUtils.changeWatcherValueResult(fieldName, params)); } public Long getChangeWatcherResultTimestamp(ClassIDPair classIdPair, String fieldName) { @@ -1198,7 +1244,7 @@ public Long getChangeWatcherResultTimestamp(ClassIDPair classIdPair, String fiel public Long getChangeWatcherResultTimestamp(ClassIDPair classIdPair, String fieldName, String[] params) { String key = RedisKeyUtils.changeWatcherClass(classIdPair.getClassName(), classIdPair.getID()); - Long result = (Long) redisDataStore.getHashValue(key, RedisKeyUtils.changeWatcherValueTimestamp(fieldName, params)); + Long result = (Long) cacheDataStore.getHashValue(key, RedisKeyUtils.changeWatcherValueTimestamp(fieldName, params)); return result; } @@ -1253,7 +1299,7 @@ public Boolean getChangeWatcherResultExists(ClassIDPair classIdPair, String fiel public Boolean getChangeWatcherResultExists(ClassIDPair classIdPair, String fieldName, String[] params) { String key = RedisKeyUtils.changeWatcherClass(classIdPair.getClassName(), classIdPair.getID()); - Boolean hasKey = redisDataStore.hasHashKey(key, RedisKeyUtils.changeWatcherValueResult(fieldName, params)); + Boolean hasKey = cacheDataStore.hasHashKey(key, RedisKeyUtils.changeWatcherValueResult(fieldName, params)); return hasKey; } @@ -1265,7 +1311,7 @@ public Object getChangeWatcherResult(ClassIDPair classIdPair, String fieldName) public Object getChangeWatcherResult(ClassIDPair classIdPair, String fieldName, String[] params) { String key = RedisKeyUtils.changeWatcherClass(classIdPair.getClassName(), classIdPair.getID()); - return redisDataStore.getHashValue(key, RedisKeyUtils.changeWatcherValueResult(fieldName, params)); + return cacheDataStore.getHashValue(key, RedisKeyUtils.changeWatcherValueResult(fieldName, params)); } public void checkChangeWatchers(ClassIDPair classIdPair) { @@ -1306,18 +1352,42 @@ protected Collection getChangeWatchersForField(ClassIDPair classIdPair, } else { String fieldWatcherKey = ""; - if (params != null) + // Also add for ALL objects of this class type + String allClassObjectsFieldWatcherKey = ""; + if (params != null) { fieldWatcherKey = RedisKeyUtils.fieldWatcher(RedisKeyUtils.changeWatcherWithParams(classIdPair.getClassName(), classIdPair.getID(), fieldName, params)); - else + allClassObjectsFieldWatcherKey = RedisKeyUtils.fieldWatcher(RedisKeyUtils.changeWatcherWithParams(classIdPair.getClassName(), "0", fieldName, params)); + } + else { fieldWatcherKey = RedisKeyUtils.fieldWatcher(RedisKeyUtils.changeWatcher(classIdPair.getClassName(), classIdPair.getID(), fieldName)); + allClassObjectsFieldWatcherKey = RedisKeyUtils.fieldWatcher(RedisKeyUtils.changeWatcher(classIdPair.getClassName(), "0", fieldName)); + } + fieldWatcherKeys.add(fieldWatcherKey); + fieldWatcherKeys.add(allClassObjectsFieldWatcherKey); } } } + // Also add for this object of this class type with NO field name + String noFieldWatcherKey = ""; + // Also add for ALL objects of this class type with NO field name + String allClassObjectsNoFieldWatcherKey = ""; + if (params != null) { + noFieldWatcherKey = RedisKeyUtils.fieldWatcher(RedisKeyUtils.changeWatcherWithParams(classIdPair.getClassName(), classIdPair.getID(), "", params)); + allClassObjectsNoFieldWatcherKey = RedisKeyUtils.fieldWatcher(RedisKeyUtils.changeWatcherWithParams(classIdPair.getClassName(), "0", "", params)); + } + else { + noFieldWatcherKey = RedisKeyUtils.fieldWatcher(RedisKeyUtils.changeWatcher(classIdPair.getClassName(), classIdPair.getID(), "")); + allClassObjectsNoFieldWatcherKey = RedisKeyUtils.fieldWatcher(RedisKeyUtils.changeWatcher(classIdPair.getClassName(), "0", "")); + } + + fieldWatcherKeys.add(noFieldWatcherKey); + fieldWatcherKeys.add(allClassObjectsNoFieldWatcherKey); + Collection changeWatchers = null; if (fieldWatcherKeys.size() > 0) { - changeWatchers = (Set) redisDataStore.getSetUnion(null, fieldWatcherKeys); + changeWatchers = (Set) cacheDataStore.getSetUnion(noFieldWatcherKey, fieldWatcherKeys); } return changeWatchers; @@ -1327,14 +1397,22 @@ protected Set getAllFieldWatchersForObject(ClassIDPair classIdPair) { Set fieldWatcherKeys = new HashSet(); String objectWatchedFieldKey = RedisKeyUtils.objectWatchedFields(classIdPair.getClassName(), classIdPair.getID()); - Set objectWatcherValueKeys = redisDataStore.getHashKeys(objectWatchedFieldKey); - Iterator itrObjectWatcherValueKeys = objectWatcherValueKeys.iterator(); - while (itrObjectWatcherValueKeys.hasNext()) { - String nextObjectWatcherValueKey = (String) itrObjectWatcherValueKeys.next(); + // Also add for ALL objects of this class type + String allObjectsWatchedFieldKey = RedisKeyUtils.objectWatchedFields(classIdPair.getClassName(), "0"); - // Get all fields for this object that are being watched and remove them. - String changeWatcherKey = (String) redisDataStore.getHashValue(objectWatchedFieldKey, nextObjectWatcherValueKey); - fieldWatcherKeys.add(changeWatcherKey); + String[] fieldKeys = {objectWatchedFieldKey, allObjectsWatchedFieldKey}; + + for(int i=0; i objectWatcherValueKeys = cacheDataStore.getHashKeys(nextKey); + Iterator itrObjectWatcherValueKeys = objectWatcherValueKeys.iterator(); + while (itrObjectWatcherValueKeys.hasNext()) { + String nextObjectWatcherValueKey = itrObjectWatcherValueKeys.next(); + + // Get all fields for this object that are being watched and remove them. + String changeWatcherKey = (String) cacheDataStore.getHashValue(nextKey, nextObjectWatcherValueKey); + fieldWatcherKeys.add(changeWatcherKey); + } } return fieldWatcherKeys; @@ -1367,7 +1445,7 @@ protected Long removeChangeWatcherForField(ClassIDPair classIdPair, String[] fie Iterator itrFieldWatcherKeys = fieldWatcherKeys.iterator(); while (itrFieldWatcherKeys.hasNext()) { String nextFieldWatcherKey = itrFieldWatcherKeys.next(); - Long countRemoved = redisDataStore.removeSetValue(nextFieldWatcherKey, changeWatcherId); + Long countRemoved = cacheDataStore.removeSetValue(nextFieldWatcherKey, changeWatcherId); result += countRemoved; } } @@ -1399,11 +1477,11 @@ public void recalculateChangeWatcher(String changeWatcherId) { // Need to break up String[] params = changeWatcherId.split(":"); - // 3rd var should be class name. - String className = params[2]; + // 3rd var should be category. + String category = params[2]; - // 4th var should be classId. - String classId = params[3]; + // 4th var should be subCategory. + String subCategory = params[3]; // 5th var should be fieldName. String fieldName = params[4]; @@ -1418,11 +1496,11 @@ public void recalculateChangeWatcher(String changeWatcherId) { if (changeWatcherHelperFactory != null) { - Collection clientIds = (Set) redisDataStore.getSetValue(RedisKeyUtils.clientWatcher(changeWatcherId)); // The list of ClientID's who are interested in this object. - ClassIDPair pair = new ClassIDPair(classId, className); + Collection clientIds = (Set) cacheDataStore.getSetValue(RedisKeyUtils.clientWatcher(changeWatcherId)); // The list of ClientID's who are interested in this object. + + IChangeWatcherHelper cwh = changeWatcherHelperFactory.getHelper(category); + cwh.reprocess(category, subCategory, fieldName, clientIds, otherParams, requestTimestamp); - IChangeWatcherHelper cwh = changeWatcherHelperFactory.getHelper(className); - cwh.recalculate(fieldName, pair, clientIds, otherParams, requestTimestamp); /** // If no clients interested in this value, then remove it from the cache. if (clientIds == null || clientIds.isEmpty()) { diff --git a/src/main/java/com/percero/agents/sync/access/RedisKeyUtils.java b/src/main/java/com/percero/agents/sync/access/RedisKeyUtils.java index 4cd1cb4..8dd3df0 100644 --- a/src/main/java/com/percero/agents/sync/access/RedisKeyUtils.java +++ b/src/main/java/com/percero/agents/sync/access/RedisKeyUtils.java @@ -111,6 +111,10 @@ public static String accessJournalPrefix() { public static String clientAccessJournal(String clientId) { return (new StringBuilder(INT_64).append("c:").append(ACCESS_JOURNAL_PREFIX).append(clientId)).toString(); } + + public static String classAccessJournal(String className) { + return (new StringBuilder(INT_64).append("class:").append(ACCESS_JOURNAL_PREFIX).append(className)).toString(); + } public static String historicalObject(String className, String classId) { return (new StringBuilder(INT_128).append("ho:").append(className).append(":").append(classId)).toString(); diff --git a/src/main/java/com/percero/agents/sync/cache/CacheManager.java b/src/main/java/com/percero/agents/sync/cache/CacheManager.java new file mode 100644 index 0000000..83f2cf1 --- /dev/null +++ b/src/main/java/com/percero/agents/sync/cache/CacheManager.java @@ -0,0 +1,260 @@ +package com.percero.agents.sync.cache; + +import com.percero.agents.sync.access.RedisKeyUtils; +import com.percero.agents.sync.datastore.ICacheDataStore; +import com.percero.agents.sync.hibernate.SyncHibernateUtils; +import com.percero.agents.sync.metadata.IMappedClassManager; +import com.percero.agents.sync.metadata.MappedClass; +import com.percero.agents.sync.metadata.MappedClassManagerFactory; +import com.percero.agents.sync.metadata.MappedField; +import com.percero.agents.sync.metadata.MappedFieldPerceroObject; +import com.percero.agents.sync.services.DataProviderManager; +import com.percero.agents.sync.services.IDataProvider; +import com.percero.agents.sync.vo.BaseDataObject; +import com.percero.agents.sync.vo.ClassIDPair; +import com.percero.framework.vo.IPerceroObject; +import org.apache.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import sun.misc.Cache; + +import java.lang.reflect.InvocationTargetException; +import java.util.*; + +/** + * Created by jonnysamps on 9/5/15. + */ +@Component +public class CacheManager { + + private static Logger logger = Logger.getLogger(CacheManager.class); + + @Autowired + ICacheDataStore cacheDataStore; + + @Autowired + Long cacheTimeout = Long.valueOf(60 * 60 * 24 * 14); // Two weeks + + public void updateCachedObject(IPerceroObject perceroObject, Map> changedFields){ + // TODO: Field-level updates could be REALLY useful here. Would avoid A TON of UNNECESSARY work... + try { + if (cacheTimeout > 0) { + // TODO: Also need to update the caches of anything object that is related to this object. + String key = RedisKeyUtils.classIdPair(perceroObject.getClass().getCanonicalName(), perceroObject.getID()); + if (cacheDataStore.hasKey(key)) { + cacheDataStore.setValue(key, ((BaseDataObject)perceroObject).toJson()); + } + + // Iterate through each changed object and reset the cache for that object. + if (changedFields != null) { +// Iterator>> itrChangedFieldEntrySet = changedFields.entrySet().iterator(); +// Set keysToDelete = new HashSet(); +// while (itrChangedFieldEntrySet.hasNext()) { +// Map.Entry> nextEntry = itrChangedFieldEntrySet.next(); +// ClassIDPair thePair = nextEntry.getKey(); +// if (!thePair.comparePerceroObject(perceroObject)) { +// String nextKey = RedisKeyUtils.classIdPair(thePair.getClassName(), thePair.getID()); +// keysToDelete.add(nextKey); +// } +// } + Iterator itrChangedFieldKeyset = changedFields.keySet().iterator(); + Set keysToDelete = new HashSet(); + while (itrChangedFieldKeyset.hasNext()) { + ClassIDPair thePair = itrChangedFieldKeyset.next(); + if (!thePair.comparePerceroObject(perceroObject)) { + String nextKey = RedisKeyUtils.classIdPair(thePair.getClassName(), thePair.getID()); + keysToDelete.add(nextKey); + } + } + + if (!keysToDelete.isEmpty()) { + cacheDataStore.deleteKeys(keysToDelete); + // TODO: Do we simply delete the key? Or do we refetch the object here and update the key? + //redisDataStore.setValue(nextKey, ((BaseDataObject)perceroObject).toJson()); + } + } + else { + // No changedFields? We should never get here? + IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); + MappedClass mappedClass = mcm.getMappedClassByClassName(perceroObject.getClass().getName()); + Iterator itrToManyFields = mappedClass.toManyFields.iterator(); + while(itrToManyFields.hasNext()) { + MappedField nextMappedField = itrToManyFields.next(); + Object fieldObject = nextMappedField.getGetter().invoke(perceroObject); + if (fieldObject != null) { + if (fieldObject instanceof IPerceroObject) { + String nextKey = RedisKeyUtils.classIdPair(fieldObject.getClass().getCanonicalName(), ((IPerceroObject)fieldObject).getID()); + if (cacheDataStore.hasKey(nextKey)) { + cacheDataStore.deleteKey(nextKey); + // TODO: Do we simply delete the key? Or do we refetch the object here and update the key? + //redisDataStore.setValue(nextKey, ((BaseDataObject)perceroObject).toJson()); + } + } + else if (fieldObject instanceof Collection) { + Iterator itrFieldObject = ((Collection) fieldObject).iterator(); + while(itrFieldObject.hasNext()) { + Object nextListObject = itrFieldObject.next(); + if (nextListObject instanceof IPerceroObject) { + String nextKey = RedisKeyUtils.classIdPair(nextListObject.getClass().getCanonicalName(), ((IPerceroObject)nextListObject).getID()); + if (cacheDataStore.hasKey(nextKey)) { + cacheDataStore.deleteKey(nextKey); + // TODO: Do we simply delete the key? Or do we refetch the object here and update the key? + //redisDataStore.setValue(nextKey, ((BaseDataObject)perceroObject).toJson()); + } + } + } + } + } + } + Iterator itrToOneFields = mappedClass.toOneFields.iterator(); + while(itrToOneFields.hasNext()) { + MappedFieldPerceroObject nextMappedField = itrToOneFields.next(); + Object fieldObject = nextMappedField.getGetter().invoke(perceroObject); + if (fieldObject != null) { + if (fieldObject instanceof IPerceroObject) { + String nextKey = RedisKeyUtils.classIdPair(fieldObject.getClass().getCanonicalName(), ((IPerceroObject)fieldObject).getID()); + if (cacheDataStore.hasKey(nextKey)) { + cacheDataStore.deleteKey(nextKey); + // TODO: Do we simply delete the key? Or do we refetch the object here and update the key? + //redisDataStore.setValue(nextKey, ((BaseDataObject)perceroObject).toJson()); + } + } + else if (fieldObject instanceof Collection) { + Iterator itrFieldObject = ((Collection) fieldObject).iterator(); + while(itrFieldObject.hasNext()) { + Object nextListObject = itrFieldObject.next(); + if (nextListObject instanceof IPerceroObject) { + String nextKey = RedisKeyUtils.classIdPair(nextListObject.getClass().getCanonicalName(), ((IPerceroObject)nextListObject).getID()); + if (cacheDataStore.hasKey(nextKey)) { + cacheDataStore.deleteKey(nextKey); + // TODO: Do we simply delete the key? Or do we refetch the object here and update the key? + //redisDataStore.setValue(nextKey, ((BaseDataObject)perceroObject).toJson()); + } + } + } + } + } + } + } + } + } catch (Exception e){ + logger.warn(e.getMessage(), e); + } + } + + public void handleDeletedObject(IPerceroObject perceroObject, String className, Boolean isShellObject) { + if (cacheTimeout > 0) { + try { + Set keysToDelete = new HashSet(); + + String key = RedisKeyUtils.classIdPair(className, perceroObject.getID()); + keysToDelete.add(key); + + Set relatedClassIdPairs = getRelatedClassIdPairs(perceroObject, className, isShellObject); + Iterator itrRelatedClassIdPairs = relatedClassIdPairs.iterator(); + while (itrRelatedClassIdPairs.hasNext()) { + ClassIDPair nextRelatedClassIdPair = itrRelatedClassIdPairs.next(); + String nextKey = RedisKeyUtils.classIdPair(nextRelatedClassIdPair.getClassName(), nextRelatedClassIdPair.getID()); + keysToDelete.add(nextKey); + } + + if (!keysToDelete.isEmpty()) { + cacheDataStore.deleteKeys(keysToDelete); + // TODO: Do we simply delete the key? Or do we refetch the object here and update the key? + //redisDataStore.setValue(nextKey, ((BaseDataObject)perceroObject).toJson()); + } + } catch (Exception e) { + logger.error(e.getMessage(), e); + } + } + } + + public void deleteObjectFromCache(ClassIDPair classIdPair) { + String key = RedisKeyUtils.classIdPair(classIdPair.getClassName(), classIdPair.getID()); + cacheDataStore.deleteKey(key); + } + + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Set getRelatedClassIdPairs(IPerceroObject perceroObject, String className, Boolean isShellObject) throws Exception { + Set results = new HashSet(); + + IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); + MappedClass mappedClass = mcm.getMappedClassByClassName(className); + + Iterator itrToManyFields = mappedClass.toManyFields.iterator(); + while(itrToManyFields.hasNext()) { + MappedField nextMappedField = itrToManyFields.next(); + + // If this is a SHELL object, then we need to get ALL objects of this MappedField type. + if (isShellObject) { + // If NO reverse mapped field, then nothing to update. + if (nextMappedField.getReverseMappedField() != null) { + MappedClass reverseMappedClass = nextMappedField.getReverseMappedField().getMappedClass(); + IDataProvider reverseDataProvider = DataProviderManager.getInstance().getDataProviderByName(reverseMappedClass.className); + Set allClassIdPairs = reverseDataProvider.getAllClassIdPairsByName(reverseMappedClass.className); + Iterator itrAllClassIdPairs = allClassIdPairs.iterator(); + while (itrAllClassIdPairs.hasNext()) { + ClassIDPair nextAllClassIdPair = itrAllClassIdPairs.next(); + results.add(nextAllClassIdPair); + } + } + } + else { + Object fieldObject = nextMappedField.getGetter().invoke(perceroObject); + if (fieldObject != null) { + if (fieldObject instanceof IPerceroObject) { + results.add(new ClassIDPair(((IPerceroObject)fieldObject).getID(), fieldObject.getClass().getCanonicalName())); + } + else if (fieldObject instanceof Collection) { + Iterator itrFieldObject = ((Collection) fieldObject).iterator(); + while(itrFieldObject.hasNext()) { + Object nextListObject = itrFieldObject.next(); + if (nextListObject instanceof IPerceroObject) { + results.add(new ClassIDPair(((IPerceroObject)nextListObject).getID(), nextListObject.getClass().getCanonicalName())); + } + } + } + } + } + } + Iterator itrToOneFields = mappedClass.toOneFields.iterator(); + while(itrToOneFields.hasNext()) { + MappedFieldPerceroObject nextMappedField = itrToOneFields.next(); + + // If this is a SHELL object, then we need to get ALL objects of this MappedField type. + if (isShellObject) { + // If NO reverse mapped field, then nothing to update. + if (nextMappedField.getReverseMappedField() != null) { + MappedClass reverseMappedClass = nextMappedField.getReverseMappedField().getMappedClass(); + IDataProvider reverseDataProvider = DataProviderManager.getInstance().getDataProviderByName(reverseMappedClass.className); + Set allClassIdPairs = reverseDataProvider.getAllClassIdPairsByName(reverseMappedClass.className); + Iterator itrAllClassIdPairs = allClassIdPairs.iterator(); + while (itrAllClassIdPairs.hasNext()) { + ClassIDPair nextAllClassIdPair = itrAllClassIdPairs.next(); + results.add(nextAllClassIdPair); + } + } + } + else { + Object fieldObject = nextMappedField.getGetter().invoke(perceroObject); + if (fieldObject != null) { + if (fieldObject instanceof IPerceroObject) { + results.add(new ClassIDPair(((IPerceroObject)fieldObject).getID(), fieldObject.getClass().getCanonicalName())); + } + else if (fieldObject instanceof Collection) { + Iterator itrFieldObject = ((Collection) fieldObject).iterator(); + while(itrFieldObject.hasNext()) { + Object nextListObject = itrFieldObject.next(); + if (nextListObject instanceof IPerceroObject) { + results.add(new ClassIDPair(((IPerceroObject)nextListObject).getID(), nextListObject.getClass().getCanonicalName())); + } + } + } + } + } + } + + return results; + } +} diff --git a/src/main/java/com/percero/agents/sync/cw/ChangeWatcherHelper.java b/src/main/java/com/percero/agents/sync/cw/ChangeWatcherHelper.java index 249e9cf..75d8704 100644 --- a/src/main/java/com/percero/agents/sync/cw/ChangeWatcherHelper.java +++ b/src/main/java/com/percero/agents/sync/cw/ChangeWatcherHelper.java @@ -1,48 +1,34 @@ package com.percero.agents.sync.cw; import java.util.Collection; -import java.util.HashMap; import java.util.Iterator; import java.util.List; -import java.util.Map; import org.apache.log4j.Logger; -import org.codehaus.jackson.map.ObjectMapper; -import org.joda.time.DateTime; -import org.joda.time.format.DateTimeFormat; -import org.joda.time.format.DateTimeFormatter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; import com.mchange.v2.lang.ObjectUtils; import com.percero.agents.sync.access.IAccessManager; -import com.percero.agents.sync.hibernate.SyncHibernateUtils; -import com.percero.agents.sync.metadata.IMappedClassManager; -import com.percero.agents.sync.metadata.MappedClass; -import com.percero.agents.sync.metadata.MappedClassManagerFactory; -import com.percero.agents.sync.services.IDataProvider; -import com.percero.agents.sync.services.IDataProviderManager; -import com.percero.agents.sync.services.IPushSyncHelper; -import com.percero.agents.sync.services.ISyncAgentService; import com.percero.agents.sync.vo.BaseDataObject; import com.percero.agents.sync.vo.ClassIDPair; -import com.percero.agents.sync.vo.IClassIDPair; -import com.percero.agents.sync.vo.PushCWUpdateResponse; import com.percero.framework.vo.IPerceroObject; +/** + * ChangeWatcherHelper allows code to be executed upon the change of any model + * data. The ChangeWatcherHelper is used as the mechanism to execute that code. + * A ChangeWatcherHelper must be registered with the ChangeWatcherHelperFactory. + * Ex: ChangeWatcherHelperFactory.getInstance().registerChangeWatcherHelper( + * "category", this ); + * + * @author Collin Brown + * + */ @Component public class ChangeWatcherHelper implements IChangeWatcherHelper { private static final Logger log = Logger.getLogger(ChangeWatcherHelper.class); - @Autowired - ObjectMapper objectMapper; - -// @Scheduled(fixedRate=10000) -// public void printStats() { -// log.debug(ChangeWatcherReporting.stringResults()); -// } @Autowired protected IAccessManager accessManager; @@ -50,386 +36,29 @@ public void setAccessManager(IAccessManager value) { accessManager = value; } - @Autowired - protected IDataProviderManager dataProviderManager; - public void setDataProviderManager(IDataProviderManager value) { - dataProviderManager = value; - } - public IDataProviderManager getDataProviderManager() { - return dataProviderManager; - } - - @Autowired - protected ISyncAgentService syncAgentService; - public void setSyncAgentService(ISyncAgentService value) { - syncAgentService = value; - } - public ISyncAgentService getSyncAgentService() { - return syncAgentService; - } - - @Autowired - protected IPushSyncHelper pushSyncHelper; - public void setPushSyncHelper(IPushSyncHelper value) { - pushSyncHelper = value; - } - - - public Object get(String fieldName, ClassIDPair classIdPair) { - return get(fieldName, classIdPair, null, null); - } - - public Object get(String fieldName, ClassIDPair classIdPair, String clientId) { - return get(fieldName, classIdPair, null, clientId); - } - - - public Object get(String fieldName, ClassIDPair classIdPair, String[] params) { - return get(fieldName, classIdPair, params, null); - } - - public Object get(String fieldName, ClassIDPair classIdPair, String[] params, String clientId) { - Object result = null; - try { - // Make sure this being requested by a valid person. - if (StringUtils.hasText(clientId)) { - String userId = accessManager.getClientUserId(clientId); - if (!StringUtils.hasLength(userId)) { - log.warn("Invalid clientId in get: " + fieldName); - return result; - } - } - - // Check to see if this value has already been calculated. - Boolean resultExists = accessManager.getChangeWatcherResultExists(classIdPair, fieldName, params); - - if (resultExists) { -// Long resultTimestamp = accessManager.getChangeWatcherResultTimestamp(classIdPair, fieldName, params); - result = accessManager.getChangeWatcherResult(classIdPair, fieldName, params); - } - else { - // Result has not been calculated, so need to calculate. - result = calculate(fieldName, classIdPair, params); - } - - if (StringUtils.hasText(clientId)) { - result = getForClient(fieldName, classIdPair, params, result, clientId); - accessManager.addWatcherClient(classIdPair, fieldName, clientId, params); - } - } catch(Exception e) { - log.error("Unable to get " + fieldName, e); - } - - return result; - } - - @SuppressWarnings("rawtypes") - protected Object getForClient(String fieldName, ClassIDPair classIdPair, String[] params, Object result, String clientId) { - - String userId = accessManager.getClientUserId(clientId); - - if (result instanceof IPerceroObject) { - if (validateReadAccess((BaseDataObject) result, userId)) - return result; - else - return null; - } - if (result instanceof IClassIDPair) { - if (validateReadAccess((ClassIDPair) result, userId)) - return result; - else - return null; - } - else if (result instanceof Collection) { - Iterator itr = ((Collection) result).iterator(); - while(itr.hasNext()) { - Object next = itr.next(); - - if (next instanceof IPerceroObject) { - if (!validateReadAccess((BaseDataObject) next, userId)) { - itr.remove(); - } - } - else if (next instanceof IClassIDPair) { - if (!validateReadAccess((ClassIDPair) next, userId)) { - itr.remove(); - } - } - } - - return result; - } - else { - return result; - } - } - - protected Boolean validateReadAccess(BaseDataObject bdo, String userId) { - return validateReadAccess(new ClassIDPair(bdo.getID(), bdo.getClass().getCanonicalName()), userId); - } - - protected Boolean validateReadAccess(ClassIDPair classIdPair, String userId) { - String className = classIdPair.getClassName(); - String id = classIdPair.getID(); - IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); - MappedClass mappedClass = mcm.getMappedClassByClassName(className); - Boolean hasAccess = true; - - if (mappedClass != null) { - IDataProviderManager dataProviderManager = getDataProviderManager(); - - IDataProvider dataProvider = dataProviderManager.getDataProviderByName(mappedClass.dataProviderName); - hasAccess = dataProvider.getReadAccess(new ClassIDPair(id, className), userId); - } - - if (hasAccess) - return true; - else - return false; - } - - public Object calculate(String fieldName, ClassIDPair classIdPair) { - return null; + public void process(String category, String subCategory, String fieldName) { + // This is really an error. + StringBuilder strBuilder = new StringBuilder("No value calculate method found for: ").append(category).append(":").append(subCategory).append(":").append(fieldName); + log.error(strBuilder.toString()); } - public Object calculate(String fieldName, ClassIDPair classIdPair, String[] params) { + public void process(String category, String subCategory, String fieldName, String[] params) { // This is really an error. - StringBuilder strBuilder = new StringBuilder("No value calculate method found for: ").append(classIdPair.getClassName()).append(":").append(fieldName); + StringBuilder strBuilder = new StringBuilder("No value process method found for: ").append(category).append(":").append(subCategory).append(":").append(fieldName); for(String nextString : params) { strBuilder.append(".").append(nextString); } log.error(strBuilder.toString()); - return null; - } - - /**protected void postCalculate(String fieldName, ClassIDPair classIdPair, Object result, Object oldValue) { - postCalculate(fieldName, classIdPair, result, null); - }*/ - private Map changeWatcherResults = new HashMap(); - protected void postCalculate(String fieldName, ClassIDPair classIdPair, String[] params, Object result, Object oldValue) { - // Now run past the ChangeWatcher. - try { - String cwResultKey = fieldName + classIdPair.toString() + (params == null ? "" : params.toString()); - ChangeWatcherResult cwResult = changeWatcherResults.get(cwResultKey); - if (cwResult == null) { - cwResult = new ChangeWatcherResult(oldValue, result, eqOrBothNull(oldValue, result)); - } - else { - cwResult.setNewValue(result); - cwResult.setEqOrBothNull(eqOrBothNull(oldValue, result)); - } - - // Check to see if this value has already been calculated. - if (cwResult.getEqOrBothNull()) { - ChangeWatcherReporting.unchangedResultsCounter++; -// log.debug("Object has not changed, so not checking ChangeWatchers."); - } - else { - ChangeWatcherReporting.changedResultsCounter++; - String[] fieldNames = null; - if (StringUtils.hasText(fieldName)) { - fieldNames = new String[1]; - fieldNames[0] = fieldName; - } - accessManager.checkChangeWatchers(classIdPair, fieldNames, params); - } - } catch (Exception e) { - log.error("Unable to chech change watchers for " + classIdPair.getID() + " / " + fieldName + " in " + getClass().getCanonicalName()); - } } - public void recalculate(String fieldName, ClassIDPair classIdPair, Collection clientIds, String[] params, Long requestTimestamp) { - Object value = null; - - // Check to see if this value has already been calculated. - Object currentValue = null; - ChangeWatcherReporting.recalcsCounter++; - try { - if (requestTimestamp != null) { - Long resultTimestamp = accessManager.getChangeWatcherResultTimestamp(classIdPair, fieldName, params); - if (resultTimestamp != null && resultTimestamp.compareTo(requestTimestamp) > 0) { - // If the result timestamp is after the request timestamp, then no need to recalculate. - // In essence, the recalc was already done, albeit at the behest of another process. - ChangeWatcherReporting.abortedRecalcsCounter++; -// log.debug("Aborting ChangeWatcherHelper.recalculate request since it has already been fulfilled."); - return; - } - } - currentValue = accessManager.getChangeWatcherResult(classIdPair, fieldName, params); - } catch(Exception e) {} - - - String cwResultKey = fieldName + classIdPair.toString() + (params == null ? "" : params.toString()); - ChangeWatcherResult cwResult = new ChangeWatcherResult(currentValue); - changeWatcherResults.put(cwResultKey, cwResult); - - // TODO: Pass the ChangeWatcherResult to calculate so that we don't have to retrieve the current value twice. - value = calculate(fieldName, classIdPair, params); - changeWatcherResults.remove(cwResultKey); - - if (!cwResult.getEqOrBothNull()) { -// ChangeWatcherReporting.unchangedResultsCounter++; - log.debug("Object has changed, pushing update."); - pushUpdate(classIdPair, fieldName, params, value, clientIds); - } + public void reprocess(String category, String subCategory, String fieldName, Collection clientIds, String[] params, Long requestTimestamp) { + ChangeWatcherReporting.reprocessCounter++; + process(category, subCategory, fieldName, params); log.debug(ChangeWatcherReporting.stringResults()); } - - protected void pushUpdate(ClassIDPair classIdPair, String fieldName, String[] params, Object update, Collection clientIds) { - // Now push the result to all interested clients. - try { - Iterator itrClientIds = clientIds.iterator(); - while(itrClientIds.hasNext()) { - String nextClient = itrClientIds.next(); - Object nextUpdate = getForClient(fieldName, classIdPair, params, update, nextClient); - pushChangeWatcherUpdate(nextClient, classIdPair, fieldName, params, nextUpdate); - } - } catch(Exception e) { - log.error("Unable to pushUpdate", e); - } - } - - - public void pushChangeWatcherUpdate(Collection clientIds, ClassIDPair classIdPair, String fieldName, String[] params, Object value) { - if (clientIds != null && clientIds.size() > 0) { - try { - PushCWUpdateResponse pushUpdateResponse = new PushCWUpdateResponse(); - pushUpdateResponse.setFieldName(fieldName); - pushUpdateResponse.setParams(params); - pushUpdateResponse.setClassIdPair(classIdPair); - - for(Object nextClient : clientIds) { - - String clientId = (String) nextClient; - if (!StringUtils.hasLength(clientId)) - continue; - - //if (value instanceof BaseDataObject) { - String userId = accessManager.getClientUserId(clientId); - // Clean the value if it is a framework object. - value = SyncHibernateUtils.cleanObject(value, null, userId); - //} - pushUpdateResponse.setValue(value); - pushUpdateResponse.setClientId(clientId); - - pushSyncHelper.pushSyncResponseToClient(pushUpdateResponse, clientId); - } - - } catch(Exception e) { - log.error("Error sending ChangeWatcher Updates to clients", e); - } - } - } - public void pushChangeWatcherUpdate(String clientId, ClassIDPair classIdPair, String fieldName, String[] params, Object value) { - if (StringUtils.hasText(clientId)) { - try { - PushCWUpdateResponse pushUpdateResponse = new PushCWUpdateResponse(); - pushUpdateResponse.setFieldName(fieldName); - pushUpdateResponse.setParams(params); - pushUpdateResponse.setClassIdPair(classIdPair); - - if (value instanceof IPerceroObject) { - String userId = accessManager.getClientUserId(clientId); - // Clean the value if it is a framework object. - value = (BaseDataObject) SyncHibernateUtils.cleanObject(value, null, userId); - } - pushUpdateResponse.setValue(value); - - pushUpdateResponse.setClientId(clientId); - - pushSyncHelper.pushSyncResponseToClient(pushUpdateResponse, clientId); - - } catch(Exception e) { - log.error("Error sending ChangeWatcher Updates to clients", e); - } - } - } - - /**************************** - * HELPER FUNCTIONS - ****************************/ - public DateTime parseDateTime(String theDate) { - String testDate = theDate.replace('_', ' '); - DateTime dateTime = null; - if (dateTime == null) { - try { - dateTime = new DateTime(Long.parseLong(theDate)); - } catch(IllegalArgumentException e) { - // Possibly the wrong format date, try a different format. - } - } - - //testDate = "2013-10-01 +07:00"; - DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss ZZ"); - if (dateTime == null) { - try { - dateTime = formatter.withOffsetParsed().parseDateTime(testDate); - } catch(IllegalArgumentException iae) { - // Possibly the wrong format date, try a different format. - } - } - if (dateTime == null) { - try { - formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss Z"); - dateTime = formatter.withOffsetParsed().parseDateTime(testDate); - } catch(IllegalArgumentException iae) { - // Possibly the wrong format date, try a different format. - } - } - if (dateTime == null) { - try { - formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss ZZZ"); - dateTime = formatter.withOffsetParsed().parseDateTime(testDate); - } catch(IllegalArgumentException iae) { - // Possibly the wrong format date, try a different format. - } - } - if (dateTime == null) { - try { - formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"); - dateTime = formatter.parseDateTime(testDate); - } catch(IllegalArgumentException iae) { - // Possibly the wrong format date, try a different format. - } - } - if (dateTime == null) { - try { - formatter = DateTimeFormat.forPattern("yyyy-MM-dd ZZZ"); - dateTime = formatter.withOffsetParsed().parseDateTime(testDate); - } catch(IllegalArgumentException iae) { - // Possibly the wrong format date, try a different format. - } - } - if (dateTime == null) { - try { - formatter = DateTimeFormat.forPattern("yyyy-MM-dd ZZ"); - dateTime = formatter.withOffsetParsed().parseDateTime(testDate); - } catch(IllegalArgumentException iae) { - // Possibly the wrong format date, try a different format. - } - } - if (dateTime == null) { - try { - formatter = DateTimeFormat.forPattern("yyyy-MM-dd Z"); - dateTime = formatter.withOffsetParsed().parseDateTime(testDate); - } catch(IllegalArgumentException iae) { - // Possibly the wrong format date, try a different format. - } - } - if (dateTime == null) { - try { - formatter = DateTimeFormat.forPattern("yyyy-MM-dd"); - dateTime = formatter.parseDateTime(testDate); - } catch(IllegalArgumentException iae) { - // Possibly the wrong format date, try a different format. - } - } - - return dateTime; - } @SuppressWarnings("unchecked") public static boolean eqOrBothNull(Object resultA, Object resultB) { diff --git a/src/main/java/com/percero/agents/sync/cw/ChangeWatcherHelperFactory.java b/src/main/java/com/percero/agents/sync/cw/ChangeWatcherHelperFactory.java index cbe6c2e..8783440 100644 --- a/src/main/java/com/percero/agents/sync/cw/ChangeWatcherHelperFactory.java +++ b/src/main/java/com/percero/agents/sync/cw/ChangeWatcherHelperFactory.java @@ -34,16 +34,23 @@ public static IChangeWatcherHelperFactory getInstance() { public ChangeWatcherHelperFactory() { currentInstance = this; } + + public void registerChangeWatcherHelper(String category, IChangeWatcherHelper changeWatcherHelper) { + IChangeWatcherHelper existingHelper = helperCache.get(category); + if (existingHelper == null) { + helperCache.put(category, changeWatcherHelper); + } + } private Map helperCache = new HashMap(); @SuppressWarnings({ "rawtypes", "unchecked" }) - public IChangeWatcherHelper getHelper(String className) { - IChangeWatcherHelper helper = helperCache.get(className); + public IChangeWatcherHelper getHelper(String category) { + IChangeWatcherHelper helper = helperCache.get(category); if (helper == null) { // Get the appropriate Helper Component. try { - Class clazz = MappedClass.forName(className); + Class clazz = MappedClass.forName(category); while (clazz != null) { try { // Get the class name only. @@ -55,7 +62,7 @@ public IChangeWatcherHelper getHelper(String className) { //String helperClassName = (new StringBuilder(className).append("CWHelper")).toString(); helper = (IChangeWatcherHelper) context.getBean(StringUtils.uncapitalize(theClassName) + "CWHelper", MappedClass.forName(helperClassName)); if (helper != null) { - helperCache.put(className, helper); + helperCache.put(category, helper); return helper; } } catch(Exception e) { @@ -65,7 +72,7 @@ public IChangeWatcherHelper getHelper(String className) { clazz = clazz.getSuperclass(); } } catch(Exception e) { - log.error("Unable to get ChangeWatcherHelper for " + className, e); + log.error("Unable to get ChangeWatcherHelper for " + category, e); } return null; diff --git a/src/main/java/com/percero/agents/sync/cw/ChangeWatcherReporting.java b/src/main/java/com/percero/agents/sync/cw/ChangeWatcherReporting.java index a460b89..09e3bd0 100644 --- a/src/main/java/com/percero/agents/sync/cw/ChangeWatcherReporting.java +++ b/src/main/java/com/percero/agents/sync/cw/ChangeWatcherReporting.java @@ -7,6 +7,7 @@ public class ChangeWatcherReporting { public static Integer recalcsCounter = 0; public static Integer abortedRecalcsCounter = 0; + public static Integer reprocessCounter = 0; public static Integer unchangedResultsCounter = 0; public static Integer changedResultsCounter = 0; @@ -17,6 +18,7 @@ public static String stringResults() { result.append("Internal Requests: ").append(internalRequestsCounter).append("\n"); result.append("Unchanged Results: ").append(unchangedResultsCounter).append("\n"); result.append("Changed Results: ").append(changedResultsCounter).append("\n"); + result.append("Reprocess: ").append(reprocessCounter).append("\n"); result.append("Recalcs: ").append(recalcsCounter).append("\n"); result.append("Aborted Recalcs: ").append(abortedRecalcsCounter).append("\n"); result.append("Aborted %: ").append((100.0 * abortedRecalcsCounter / recalcsCounter)).append("\n"); diff --git a/src/main/java/com/percero/agents/sync/cw/DerivedValueChangeWatcherHelper.java b/src/main/java/com/percero/agents/sync/cw/DerivedValueChangeWatcherHelper.java new file mode 100644 index 0000000..8f28c61 --- /dev/null +++ b/src/main/java/com/percero/agents/sync/cw/DerivedValueChangeWatcherHelper.java @@ -0,0 +1,459 @@ +package com.percero.agents.sync.cw; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.apache.log4j.Logger; +import org.joda.time.DateTime; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import com.percero.agents.sync.hibernate.SyncHibernateUtils; +import com.percero.agents.sync.metadata.IMappedClassManager; +import com.percero.agents.sync.metadata.MappedClass; +import com.percero.agents.sync.metadata.MappedClassManagerFactory; +import com.percero.agents.sync.services.IDataProvider; +import com.percero.agents.sync.services.IDataProviderManager; +import com.percero.agents.sync.services.IPushSyncHelper; +import com.percero.agents.sync.services.ISyncAgentService; +import com.percero.agents.sync.vo.BaseDataObject; +import com.percero.agents.sync.vo.ClassIDPair; +import com.percero.agents.sync.vo.IClassIDPair; +import com.percero.agents.sync.vo.PushCWUpdateResponse; +import com.percero.framework.vo.IPerceroObject; + +/** + * Customized Change Watcher for calculating Derived Values. Derived Values are + * properties on the data model that are derived from other properties and thus + * NOT stored in the data store. Derived Values are stored in the cache, along + * with the triggers necessary to recalculate them. + * + * For each Derived Value, a Change Watcher is meant to be set on each and every + * property that could potentially effect the resultant Derived Value. When one + * of those values changes, the Change Watcher will cause the Derived Value to + * be recalculated. Changes to a Derived Value are also automatically pushed out + * to all Clients who have registered interest in that Derived Value. + * + * Dynamic parameters can be used to register for a Derived Value. For example, + * a daily report roll-up could use the Date as part of the name of the Derived + * Value. These parameters are passed as part of the name (ie. identifier) of + * the Derived Value. + * + * @author Collin Brown + * + */ +@Component +public class DerivedValueChangeWatcherHelper extends ChangeWatcherHelper implements IChangeWatcherValueHelper { + + private static final Logger log = Logger.getLogger(ChangeWatcherHelper.class); + + @Autowired + protected IPushSyncHelper pushSyncHelper; + public void setPushSyncHelper(IPushSyncHelper value) { + pushSyncHelper = value; + } + + @Autowired + protected IDataProviderManager dataProviderManager; + public void setDataProviderManager(IDataProviderManager value) { + dataProviderManager = value; + } + public IDataProviderManager getDataProviderManager() { + return dataProviderManager; + } + + @Autowired + protected ISyncAgentService syncAgentService; + public void setSyncAgentService(ISyncAgentService value) { + syncAgentService = value; + } + public ISyncAgentService getSyncAgentService() { + return syncAgentService; + } + + + public Object calculate(String fieldName, ClassIDPair classIdPair) { + // This is really an error. + StringBuilder strBuilder = new StringBuilder("No value calculate method found for: ").append(classIdPair.getClassName()).append(":").append(fieldName); + log.error(strBuilder.toString()); + return null; + } + + public Object calculate(String fieldName, ClassIDPair classIdPair, String[] params) { + // This is really an error. + StringBuilder strBuilder = new StringBuilder("No value calculate method found for: ").append(classIdPair.getClassName()).append(":").append(fieldName); + for(String nextString : params) { + strBuilder.append(".").append(nextString); + } + log.error(strBuilder.toString()); + return null; + } + + @Override + public void process(String category, String subCategory, String fieldName) { + calculate(fieldName, new ClassIDPair(subCategory, category)); + } + + @Override + public void process(String category, String subCategory, String fieldName, String[] params) { + calculate(fieldName, new ClassIDPair(subCategory, category), params); + } + + /**protected void postCalculate(String fieldName, ClassIDPair classIdPair, Object result, Object oldValue) { + postCalculate(fieldName, classIdPair, result, null); + }*/ + private Map changeWatcherResults = new HashMap(); + protected void postCalculate(String fieldName, ClassIDPair classIdPair, String[] params, Object result, Object oldValue) { + // Now run past the ChangeWatcher. + try { + String cwResultKey = fieldName + classIdPair.toString() + (params == null ? "" : params.toString()); + ChangeWatcherResult cwResult = changeWatcherResults.get(cwResultKey); + if (cwResult == null) { + cwResult = new ChangeWatcherResult(oldValue, result, eqOrBothNull(oldValue, result)); + } + else { + cwResult.setNewValue(result); + cwResult.setEqOrBothNull(eqOrBothNull(oldValue, result)); + } + + // Check to see if this value has already been calculated. + if (cwResult.getEqOrBothNull()) { + ChangeWatcherReporting.unchangedResultsCounter++; +// log.debug("Object has not changed, so not checking ChangeWatchers."); + } + else { + ChangeWatcherReporting.changedResultsCounter++; + String[] fieldNames = null; + if (StringUtils.hasText(fieldName)) { + fieldNames = new String[1]; + fieldNames[0] = fieldName; + } + accessManager.checkChangeWatchers(classIdPair, fieldNames, params); + } + } catch (Exception e) { + log.error("Unable to chech change watchers for " + classIdPair.getID() + " / " + fieldName + " in " + getClass().getCanonicalName()); + } + } + + + public void recalculate(String fieldName, ClassIDPair classIdPair, Collection clientIds, String[] params, Long requestTimestamp) { + Object value = null; + + // Check to see if this value has already been calculated. + Object currentValue = null; + ChangeWatcherReporting.recalcsCounter++; + try { + if (requestTimestamp != null) { + Long resultTimestamp = accessManager.getChangeWatcherResultTimestamp(classIdPair, fieldName, params); + if (resultTimestamp != null && resultTimestamp.compareTo(requestTimestamp) > 0) { + // If the result timestamp is after the request timestamp, then no need to recalculate. + // In essence, the recalc was already done, albeit at the behest of another process. + ChangeWatcherReporting.abortedRecalcsCounter++; +// log.debug("Aborting ChangeWatcherHelper.recalculate request since it has already been fulfilled."); + return; + } + } + currentValue = accessManager.getChangeWatcherResult(classIdPair, fieldName, params); + } catch(Exception e) {} + + + String cwResultKey = fieldName + classIdPair.toString() + (params == null ? "" : params.toString()); + ChangeWatcherResult cwResult = new ChangeWatcherResult(currentValue); + changeWatcherResults.put(cwResultKey, cwResult); + + // TODO: Pass the ChangeWatcherResult to calculate so that we don't have to retrieve the current value twice. + value = calculate(fieldName, classIdPair, params); + changeWatcherResults.remove(cwResultKey); + + if (!cwResult.getEqOrBothNull()) { +// ChangeWatcherReporting.unchangedResultsCounter++; + log.debug("Object has changed, pushing update."); + pushUpdate(classIdPair, fieldName, params, value, clientIds); + } + + log.debug(ChangeWatcherReporting.stringResults()); + } + + @Override + public void reprocess(String category, String subCategory, String fieldName, Collection clientIds, String[] params, Long requestTimestamp) { + ChangeWatcherReporting.reprocessCounter++; + this.recalculate(fieldName, new ClassIDPair(subCategory, category), clientIds, params, requestTimestamp); + } + + + + public Object get(String fieldName, ClassIDPair classIdPair) { + return get(fieldName, classIdPair, null, null); + } + + public Object get(String fieldName, ClassIDPair classIdPair, String clientId) { + return get(fieldName, classIdPair, null, clientId); + } + + + public Object get(String fieldName, ClassIDPair classIdPair, String[] params) { + return get(fieldName, classIdPair, params, null); + } + + public Object get(String fieldName, ClassIDPair classIdPair, String[] params, String clientId) { + Object result = null; + try { + // Make sure this being requested by a valid person. + if (StringUtils.hasText(clientId)) { + String userId = accessManager.getClientUserId(clientId); + if (!StringUtils.hasLength(userId)) { + log.warn("Invalid clientId in get: " + fieldName); + return result; + } + } + + // Check to see if this value has already been calculated. + Boolean resultExists = accessManager.getChangeWatcherResultExists(classIdPair, fieldName, params); + + if (resultExists) { +// Long resultTimestamp = accessManager.getChangeWatcherResultTimestamp(classIdPair, fieldName, params); + result = accessManager.getChangeWatcherResult(classIdPair, fieldName, params); + } + else { + // Result has not been calculated, so need to calculate. + result = calculate(fieldName, classIdPair, params); + } + + if (StringUtils.hasText(clientId)) { + result = getForClient(fieldName, classIdPair, params, result, clientId); + accessManager.addWatcherClient(classIdPair, fieldName, clientId, params); + } + } catch(Exception e) { + log.error("Unable to get " + fieldName, e); + } + + return result; + } + + @SuppressWarnings("rawtypes") + protected Object getForClient(String fieldName, ClassIDPair classIdPair, String[] params, Object result, String clientId) { + + String userId = accessManager.getClientUserId(clientId); + + if (result instanceof IPerceroObject) { + if (validateReadAccess((BaseDataObject) result, userId)) + return result; + else + return null; + } + if (result instanceof IClassIDPair) { + if (validateReadAccess((ClassIDPair) result, userId)) + return result; + else + return null; + } + else if (result instanceof Collection) { + Iterator itr = ((Collection) result).iterator(); + while(itr.hasNext()) { + Object next = itr.next(); + + if (next instanceof IPerceroObject) { + if (!validateReadAccess((BaseDataObject) next, userId)) { + itr.remove(); + } + } + else if (next instanceof IClassIDPair) { + if (!validateReadAccess((ClassIDPair) next, userId)) { + itr.remove(); + } + } + } + + return result; + } + else { + return result; + } + } + + protected Boolean validateReadAccess(BaseDataObject bdo, String userId) { + return validateReadAccess(new ClassIDPair(bdo.getID(), bdo.getClass().getCanonicalName()), userId); + } + + protected Boolean validateReadAccess(ClassIDPair classIdPair, String userId) { + String className = classIdPair.getClassName(); + String id = classIdPair.getID(); + IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); + MappedClass mappedClass = mcm.getMappedClassByClassName(className); + Boolean hasAccess = true; + + if (mappedClass != null) { + IDataProviderManager dataProviderManager = getDataProviderManager(); + + IDataProvider dataProvider = dataProviderManager.getDataProviderByName(mappedClass.dataProviderName); + hasAccess = dataProvider.getReadAccess(new ClassIDPair(id, className), userId); + } + + if (hasAccess) + return true; + else + return false; + } + + + + protected void pushUpdate(ClassIDPair classIdPair, String fieldName, String[] params, Object update, Collection clientIds) { + // Now push the result to all interested clients. + try { + Iterator itrClientIds = clientIds.iterator(); + while(itrClientIds.hasNext()) { + String nextClient = itrClientIds.next(); + Object nextUpdate = getForClient(fieldName, classIdPair, params, update, nextClient); + pushChangeWatcherUpdate(nextClient, classIdPair, fieldName, params, nextUpdate); + } + } catch(Exception e) { + log.error("Unable to pushUpdate", e); + } + } + + + public void pushChangeWatcherUpdate(Collection clientIds, ClassIDPair classIdPair, String fieldName, String[] params, Object value) { + if (clientIds != null && clientIds.size() > 0) { + try { + PushCWUpdateResponse pushUpdateResponse = new PushCWUpdateResponse(); + pushUpdateResponse.setFieldName(fieldName); + pushUpdateResponse.setParams(params); + pushUpdateResponse.setClassIdPair(classIdPair); + + for(Object nextClient : clientIds) { + + String clientId = (String) nextClient; + if (!StringUtils.hasLength(clientId)) + continue; + + //if (value instanceof BaseDataObject) { + String userId = accessManager.getClientUserId(clientId); + // Clean the value if it is a framework object. + value = SyncHibernateUtils.cleanObject(value, null, userId); + //} + pushUpdateResponse.setValue(value); + pushUpdateResponse.setClientId(clientId); + + pushSyncHelper.pushSyncResponseToClient(pushUpdateResponse, clientId); + } + + } catch(Exception e) { + log.error("Error sending ChangeWatcher Updates to clients", e); + } + } + } + public void pushChangeWatcherUpdate(String clientId, ClassIDPair classIdPair, String fieldName, String[] params, Object value) { + if (StringUtils.hasText(clientId)) { + try { + PushCWUpdateResponse pushUpdateResponse = new PushCWUpdateResponse(); + pushUpdateResponse.setFieldName(fieldName); + pushUpdateResponse.setParams(params); + pushUpdateResponse.setClassIdPair(classIdPair); + + if (value instanceof IPerceroObject) { + String userId = accessManager.getClientUserId(clientId); + // Clean the value if it is a framework object. + value = (BaseDataObject) SyncHibernateUtils.cleanObject(value, null, userId); + } + pushUpdateResponse.setValue(value); + + pushUpdateResponse.setClientId(clientId); + + pushSyncHelper.pushSyncResponseToClient(pushUpdateResponse, clientId); + + } catch(Exception e) { + log.error("Error sending ChangeWatcher Updates to clients", e); + } + } + } + + + /**************************** + * HELPER FUNCTIONS + ****************************/ + public DateTime parseDateTime(String theDate) { + String testDate = theDate.replace('_', ' '); + DateTime dateTime = null; + if (dateTime == null) { + try { + dateTime = new DateTime(Long.parseLong(theDate)); + } catch(IllegalArgumentException e) { + // Possibly the wrong format date, try a different format. + } + } + + //testDate = "2013-10-01 +07:00"; + DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss ZZ"); + if (dateTime == null) { + try { + dateTime = formatter.withOffsetParsed().parseDateTime(testDate); + } catch(IllegalArgumentException iae) { + // Possibly the wrong format date, try a different format. + } + } + if (dateTime == null) { + try { + formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss Z"); + dateTime = formatter.withOffsetParsed().parseDateTime(testDate); + } catch(IllegalArgumentException iae) { + // Possibly the wrong format date, try a different format. + } + } + if (dateTime == null) { + try { + formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss ZZZ"); + dateTime = formatter.withOffsetParsed().parseDateTime(testDate); + } catch(IllegalArgumentException iae) { + // Possibly the wrong format date, try a different format. + } + } + if (dateTime == null) { + try { + formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"); + dateTime = formatter.parseDateTime(testDate); + } catch(IllegalArgumentException iae) { + // Possibly the wrong format date, try a different format. + } + } + if (dateTime == null) { + try { + formatter = DateTimeFormat.forPattern("yyyy-MM-dd ZZZ"); + dateTime = formatter.withOffsetParsed().parseDateTime(testDate); + } catch(IllegalArgumentException iae) { + // Possibly the wrong format date, try a different format. + } + } + if (dateTime == null) { + try { + formatter = DateTimeFormat.forPattern("yyyy-MM-dd ZZ"); + dateTime = formatter.withOffsetParsed().parseDateTime(testDate); + } catch(IllegalArgumentException iae) { + // Possibly the wrong format date, try a different format. + } + } + if (dateTime == null) { + try { + formatter = DateTimeFormat.forPattern("yyyy-MM-dd Z"); + dateTime = formatter.withOffsetParsed().parseDateTime(testDate); + } catch(IllegalArgumentException iae) { + // Possibly the wrong format date, try a different format. + } + } + if (dateTime == null) { + try { + formatter = DateTimeFormat.forPattern("yyyy-MM-dd"); + dateTime = formatter.parseDateTime(testDate); + } catch(IllegalArgumentException iae) { + // Possibly the wrong format date, try a different format. + } + } + + return dateTime; + } + +} diff --git a/src/main/java/com/percero/agents/sync/cw/IChangeWatcherHelper.java b/src/main/java/com/percero/agents/sync/cw/IChangeWatcherHelper.java index 650d43a..9f1a02c 100644 --- a/src/main/java/com/percero/agents/sync/cw/IChangeWatcherHelper.java +++ b/src/main/java/com/percero/agents/sync/cw/IChangeWatcherHelper.java @@ -2,19 +2,10 @@ import java.util.Collection; -import com.percero.agents.sync.services.IDataProviderManager; -import com.percero.agents.sync.services.ISyncAgentService; -import com.percero.agents.sync.vo.ClassIDPair; - public interface IChangeWatcherHelper { - public ISyncAgentService getSyncAgentService(); - public IDataProviderManager getDataProviderManager(); - public Object get(String fieldName, ClassIDPair classIdPair); - public Object get(String fieldName, ClassIDPair classIdPair, String clientId); - public Object get(String fieldName, ClassIDPair classIdPair, String[] params); - public Object get(String fieldName, ClassIDPair classIdPair, String[] params, String clientId); - public Object calculate(String fieldName, ClassIDPair classIdPair); - public Object calculate(String fieldName, ClassIDPair classIdPair, String[] params); - public void recalculate(String fieldName, ClassIDPair classIdPair, Collection clientIds, String[] params, Long requestTimestamp); + public void process(String category, String subCategory, String fieldName); + public void process(String category, String subCategory, String fieldName, String[] params); + public void reprocess(String category, String subCategory, String fieldName, Collection clientIds, String[] params, Long requestTimestamp); + } diff --git a/src/main/java/com/percero/agents/sync/cw/IChangeWatcherHelperFactory.java b/src/main/java/com/percero/agents/sync/cw/IChangeWatcherHelperFactory.java index 7bf0e7c..0de0907 100644 --- a/src/main/java/com/percero/agents/sync/cw/IChangeWatcherHelperFactory.java +++ b/src/main/java/com/percero/agents/sync/cw/IChangeWatcherHelperFactory.java @@ -4,4 +4,5 @@ public interface IChangeWatcherHelperFactory { public IChangeWatcherHelper getHelper(String className); + public void registerChangeWatcherHelper(String category, IChangeWatcherHelper changeWatcherHelper); } diff --git a/src/main/java/com/percero/agents/sync/cw/IChangeWatcherValueHelper.java b/src/main/java/com/percero/agents/sync/cw/IChangeWatcherValueHelper.java new file mode 100644 index 0000000..2408de2 --- /dev/null +++ b/src/main/java/com/percero/agents/sync/cw/IChangeWatcherValueHelper.java @@ -0,0 +1,12 @@ +package com.percero.agents.sync.cw; + +import com.percero.agents.sync.vo.ClassIDPair; + +public interface IChangeWatcherValueHelper { + + public Object get(String fieldName, ClassIDPair classIdPair); + public Object get(String fieldName, ClassIDPair classIdPair, String clientId); + public Object get(String fieldName, ClassIDPair classIdPair, String[] params); + public Object get(String fieldName, ClassIDPair classIdPair, String[] params, String clientId); + +} diff --git a/src/main/java/com/percero/agents/sync/dao/ActiveStackHibernateSqlQuery.java b/src/main/java/com/percero/agents/sync/dao/ActiveStackHibernateSqlQuery.java new file mode 100644 index 0000000..204c6f6 --- /dev/null +++ b/src/main/java/com/percero/agents/sync/dao/ActiveStackHibernateSqlQuery.java @@ -0,0 +1,87 @@ +//package com.percero.agents.sync.dao; +// +//import java.util.Collection; +//import java.util.Date; +//import java.util.List; +// +//import org.hibernate.SQLQuery; +// +//public class ActiveStackHibernateSqlQuery implements IActiveStackSqlQuery { +// +// private SQLQuery query; +// +// public ActiveStackHibernateSqlQuery(SQLQuery query) { +// this.query = query; +// } +// +// public String getQueryString() { +// return getQuery().getQueryString(); +// } +// +// @Override +// public String[] getNamedParameters() { +// return getQuery().getNamedParameters(); +// } +// +// public IActiveStackSqlQuery setString(int position, String val) { +// getQuery().setString(position, val); +// return this; +// } +// +// public IActiveStackSqlQuery setString(String name, String val) { +// getQuery().setString(name, val); +// return this; +// } +// +// public IActiveStackSqlQuery setDate(int position, Date val) { +// getQuery().setDate(position, val); +// return this; +// } +// +// public IActiveStackSqlQuery setDate(String name, Date val) { +// getQuery().setDate(name, val); +// return this; +// } +// +// public IActiveStackSqlQuery setParameter(int position, Object val) { +// getQuery().setParameter(position, val); +// return this; +// } +// +// public IActiveStackSqlQuery setParameter(String name, Object val) { +// getQuery().setParameter(name, val); +// return this; +// } +// +// public IActiveStackSqlQuery setParameterList(String name, Collection vals) { +// getQuery().setParameterList(name, vals); +// return this; +// } +// +// public IActiveStackSqlQuery setParameterList(String name, Object[] vals) { +// getQuery().setParameterList(name, vals); +// return this; +// } +// +// public List list() { +// return getQuery().list(); +// } +// +// public Object uniqueResult() { +// return getQuery().uniqueResult(); +// } +// +// public int executeUpdate() { +// return getQuery().executeUpdate(); +// } +// +// +// public SQLQuery getQuery() { +// return query; +// } +// +// public void setQuery(SQLQuery query) { +// this.query = query; +// } +// +//} diff --git a/src/main/java/com/percero/agents/sync/dao/ActiveStackHibernateSqlSession.java b/src/main/java/com/percero/agents/sync/dao/ActiveStackHibernateSqlSession.java new file mode 100644 index 0000000..d2ad25f --- /dev/null +++ b/src/main/java/com/percero/agents/sync/dao/ActiveStackHibernateSqlSession.java @@ -0,0 +1,31 @@ +//package com.percero.agents.sync.dao; +// +//import org.hibernate.Session; +// +//public class ActiveStackHibernateSqlSession implements IActiveStackSqlSession { +// +// private Session session; +// +// public ActiveStackHibernateSqlSession(Session session) { +// this.session = session; +// } +// +// public IActiveStackSqlQuery createSQLQuery(String queryString) { +// ActiveStackHibernateSqlQuery query = new ActiveStackHibernateSqlQuery(getSession().createSQLQuery(queryString)); +// return query; +// } +// +// public void close() { +// if (getSession() != null) { +// getSession().close(); +// } +// } +// +// public Session getSession() { +// return session; +// } +// +// public void setSession(Session session) { +// this.session = session; +// } +//} diff --git a/src/main/java/com/percero/agents/sync/dao/ActiveStackMySQLDialect.java b/src/main/java/com/percero/agents/sync/dao/ActiveStackMySQLDialect.java new file mode 100644 index 0000000..deb38aa --- /dev/null +++ b/src/main/java/com/percero/agents/sync/dao/ActiveStackMySQLDialect.java @@ -0,0 +1,16 @@ +package com.percero.agents.sync.dao; + +import java.sql.Types; + +import org.hibernate.Hibernate; +import org.hibernate.dialect.MySQLDialect; + +public class ActiveStackMySQLDialect extends MySQLDialect { + + public ActiveStackMySQLDialect() { + super(); + + registerHibernateType(Types.LONGVARCHAR, Hibernate.TEXT.getName()); + } + +} diff --git a/src/main/java/com/percero/agents/sync/dao/ActiveStackSqlSessionFactory.java b/src/main/java/com/percero/agents/sync/dao/ActiveStackSqlSessionFactory.java new file mode 100644 index 0000000..d5acfcc --- /dev/null +++ b/src/main/java/com/percero/agents/sync/dao/ActiveStackSqlSessionFactory.java @@ -0,0 +1,38 @@ +//package com.percero.agents.sync.dao; +// +//import org.hibernate.Session; +//import org.hibernate.SessionFactory; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.beans.factory.annotation.Qualifier; +//import org.springframework.stereotype.Component; +// +//import com.percero.agents.sync.exceptions.SyncDataException; +// +//@Component +//public class ActiveStackSqlSessionFactory { +// +// private static ActiveStackSqlSessionFactory instance = null; +// public static ActiveStackSqlSessionFactory getInstance() throws SyncDataException { +// if (instance != null) { +// return instance; +// } +// +// throw new SyncDataException("No Session Factory", -1001); +// } +// +// public ActiveStackSqlSessionFactory() { +// ActiveStackSqlSessionFactory.instance = this; +// } +// +// @Autowired +// @Qualifier(value="appSessionFactory") +// SessionFactory appSessionFactory; +// public void setAppSessionFactory(SessionFactory value) { +// appSessionFactory = value; +// } +// +// public IActiveStackSqlSession retrieveOpenSession(String dataSource) { +// ActiveStackHibernateSqlSession session = new ActiveStackHibernateSqlSession(appSessionFactory.openSession()); +// return session; +// } +//} diff --git a/src/main/java/com/percero/agents/sync/dao/DAORegistry.java b/src/main/java/com/percero/agents/sync/dao/DAORegistry.java new file mode 100644 index 0000000..ea590ff --- /dev/null +++ b/src/main/java/com/percero/agents/sync/dao/DAORegistry.java @@ -0,0 +1,39 @@ +package com.percero.agents.sync.dao; + +import java.util.HashMap; +import java.util.Map; + +import com.percero.agents.sync.services.DAODataProvider; +import com.percero.agents.sync.services.DataProviderManager; +import com.percero.framework.vo.IPerceroObject; + +import edu.emory.mathcs.backport.java.util.Collections; + +public class DAORegistry { + + private static DAORegistry instance = null; + + public static DAORegistry getInstance() { + if (instance == null) { + instance = new DAORegistry(); + } + return instance; + } + + public DAORegistry() { + instance = this; + + DataProviderManager.getInstance().addDataProvider(DAODataProvider.getInstance()); + } + + @SuppressWarnings({ "unchecked" }) + private static Map> dataAccessObjects = Collections.synchronizedMap(new HashMap>()); + + public void registerDataAccessObject(String name, IDataAccessObject dataAccessObject) { + dataAccessObjects.put(name, dataAccessObject); + } + + public IDataAccessObject getDataAccessObject(String name) { + return dataAccessObjects.get(name); + } +} diff --git a/src/main/java/com/percero/agents/sync/dao/IActiveStackSqlQuery.java b/src/main/java/com/percero/agents/sync/dao/IActiveStackSqlQuery.java new file mode 100644 index 0000000..fe7fd6e --- /dev/null +++ b/src/main/java/com/percero/agents/sync/dao/IActiveStackSqlQuery.java @@ -0,0 +1,23 @@ +//package com.percero.agents.sync.dao; +// +//import java.util.Collection; +//import java.util.Date; +//import java.util.List; +// +//public interface IActiveStackSqlQuery { +// +// public String getQueryString(); +// public String[] getNamedParameters(); +// public IActiveStackSqlQuery setString(int position, String val); +// public IActiveStackSqlQuery setString(String name, String val); +// public IActiveStackSqlQuery setDate(int position, Date val); +// public IActiveStackSqlQuery setDate(String name, Date val); +// public IActiveStackSqlQuery setParameter(int position, Object val); +// public IActiveStackSqlQuery setParameter(String name, Object val); +// public IActiveStackSqlQuery setParameterList(String name, Collection vals); +// public IActiveStackSqlQuery setParameterList(String name, Object[] vals); +// public List list(); +// public Object uniqueResult(); +// public int executeUpdate(); +// +//} diff --git a/src/main/java/com/percero/agents/sync/dao/IActiveStackSqlSession.java b/src/main/java/com/percero/agents/sync/dao/IActiveStackSqlSession.java new file mode 100644 index 0000000..83a077e --- /dev/null +++ b/src/main/java/com/percero/agents/sync/dao/IActiveStackSqlSession.java @@ -0,0 +1,8 @@ +//package com.percero.agents.sync.dao; +// +//public interface IActiveStackSqlSession { +// +// public IActiveStackSqlQuery createSQLQuery(String queryString); +// +// public void close(); +//} diff --git a/src/main/java/com/percero/agents/sync/dao/IDataAccessObject.java b/src/main/java/com/percero/agents/sync/dao/IDataAccessObject.java new file mode 100644 index 0000000..74d964e --- /dev/null +++ b/src/main/java/com/percero/agents/sync/dao/IDataAccessObject.java @@ -0,0 +1,41 @@ +package com.percero.agents.sync.dao; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import com.percero.agents.sync.exceptions.SyncException; +import com.percero.agents.sync.metadata.MappedField; +import com.percero.agents.sync.vo.ClassIDPair; +import com.percero.agents.sync.vo.ClassIDPairs; +import com.percero.framework.vo.IPerceroObject; +import com.percero.framework.vo.PerceroList; + +public interface IDataAccessObject { + + Boolean hasCreateAccess(ClassIDPair classIdPair, String userId); + Boolean hasReadAccess(ClassIDPair classIdPair, String userId); + Boolean hasUpdateAccess(ClassIDPair classIdPair, String userId); + Boolean hasDeleteAccess(ClassIDPair classIdPair, String userId); + + PerceroList getAll(Integer pageNumber, Integer pageSize, Boolean returnTotal, String userId, Boolean shellOnly) throws Exception; + Integer countAll(String userId) throws SyncException; + + List retrieveAllByRelationship(MappedField mappedField, ClassIDPair targetClassIdPair, Boolean shellOnly, String userId) throws SyncException; + + List findByExample(T theQueryObject, List excludeProperties, String userId, Boolean shellOnly) throws SyncException; + + T retrieveObject(ClassIDPair classIdPair, String userId, Boolean shellOnly) throws SyncException; + + List retrieveObjects(ClassIDPairs classIdPairs, String userId, Boolean shellOnly) throws SyncException; + + T createObject(T percero, String userIdObject) throws SyncException; + + T updateObject(T perceroObject, Map> changedFields, String userId) throws SyncException; + + Boolean deleteObject(ClassIDPair classIdPair, String userId) throws SyncException; + + List runQuery(String queryName, Object[] queryArguments, String userId) throws SyncException; + T cleanObjectForUser(T perceroObject, + String userId); +} diff --git a/src/main/java/com/percero/agents/sync/datastore/ICacheDataStore.java b/src/main/java/com/percero/agents/sync/datastore/ICacheDataStore.java new file mode 100644 index 0000000..614ed8f --- /dev/null +++ b/src/main/java/com/percero/agents/sync/datastore/ICacheDataStore.java @@ -0,0 +1,173 @@ +package com.percero.agents.sync.datastore; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.SetOperations; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.transaction.annotation.Transactional; + +public interface ICacheDataStore { + + public abstract Boolean expire(String key, long timeout, TimeUnit timeUnit); + + public abstract Boolean expire(Collection keys, long timeout, + TimeUnit timeUnit); + + public abstract Boolean expire(String key, long timeout, TimeUnit timeUnit, + Boolean forceNow); + + public abstract Boolean expire(Collection keys, long timeout, + TimeUnit timeUnit, Boolean forceNow); + + @Scheduled(fixedRate = 600000) + // 10 * 60 * 1000 -> Ten Minutes. + public abstract void postExpires(); + + public abstract Object getValue(String key); + + public abstract List getValues(Collection keys); + + public abstract void setValue(String key, Object value); + + // public void setValues( final KeyValuePair[] pairs ) { + public abstract void setValues( + Map keysAndValuesMap); + + @Transactional + public abstract Long addSetValue(String key, Object value); + + @Transactional + public abstract Long lpushListValue(String key, Object value); + + @Transactional + public abstract List listAll(String key); + + @Transactional + public abstract List listRange(String key, Long start, Long end); + + @Transactional + public abstract Object listIndex(String key, Long index); + + @Transactional + public abstract Object listIndex(String key); + + @Transactional + public abstract Object getHashValue(String key, String hashKey); + + @Transactional + public abstract Set getHashKeys(String key); + + @Transactional + public abstract Map getHashEntries(String key); + + @Transactional + public abstract Long getHashSize(String key); + + @Transactional + public abstract void deleteHashKey(String key, String hashKey); + + @Transactional + public abstract void setHashValue(String key, String hashKey, Object value); + + @Transactional + public abstract void setAllHashValues(String key, + Map hashMap); + + @Transactional + public abstract Long removeSetValue(String key, Object value); + + @Transactional + public abstract Boolean getSetIsEmpty(String key); + + @Transactional + public Long getSetSize( final String key ); + + @Transactional + public abstract Long removeSetValueAndGetSize(String key, Object value); + + public abstract void removeSetsValue(Collection keys, + Object value); + + public abstract void removeSetsValue(String keysPrefix, + Collection keys, Object value); + + public abstract Boolean hasKey(String key); + + public abstract Boolean hasHashKey(String key, String hashKey); + + @Transactional + public abstract void swapHashKey(String key, String oldHashKey, + String newHashValue); + + public abstract Boolean renameIfAbsent(String oldKey, String newKey); + + public abstract void rename(String oldKey, String newKey); + + public abstract Long setUnionAndStore(String key, String otherKey, + String destKey); + + public abstract Long setUnionAndStore(String key, + Collection otherKeys, String destKey); + + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisDataStore#removeSetValues(java.lang.String, java.util.Collection) + */ + @Transactional + public abstract void removeSetValues(String key, + Collection values); + + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisDataStore#getSetValue(java.lang.String) + */ + public abstract Set getSetValue(String key); + + @Transactional + public abstract Long replaceSet(String key, + Collection values); + + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisDataStore#swapSetValue(java.lang.String, java.lang.String, java.lang.String) + */ + @Transactional + public abstract void swapSetValue(String key, String oldValue, + String newValue); + + public abstract Boolean getSetIsMember(String key, Object object); + + @Transactional + public abstract Set getSetsContainsMembers( + Collection keys, Object[] membersToCheck); + + public abstract Set getSetIntersect(String key, + Collection otherKeys); + + public abstract Set getSetIntersect(String key, String otherKey); + + public abstract Set getSetUnion(String key, + Collection otherKeys); + + public abstract Set getSetUnion(String key, + String otherKey); + + public abstract void deleteKey(String key); + + public abstract void deleteKeys(Collection keys); + + @Transactional + public abstract Long setSetValue(String key, Object value); + + public abstract SetOperations getSet(String key); + + public abstract RedisTemplate getTemplate(); + + public abstract Collection keys(String pattern); + + @Transactional + public abstract void removeKeysValue(String pattern, Object value); + +} \ No newline at end of file diff --git a/src/main/java/com/percero/agents/sync/datastore/JedisCacheDataStore.java b/src/main/java/com/percero/agents/sync/datastore/JedisCacheDataStore.java new file mode 100644 index 0000000..647f21a --- /dev/null +++ b/src/main/java/com/percero/agents/sync/datastore/JedisCacheDataStore.java @@ -0,0 +1,648 @@ +//package com.percero.agents.sync.datastore; +// +//import java.util.ArrayList; +//import java.util.Collection; +//import java.util.HashMap; +//import java.util.HashSet; +//import java.util.Iterator; +//import java.util.List; +//import java.util.Map; +//import java.util.Map.Entry; +//import java.util.Set; +//import java.util.concurrent.TimeUnit; +// +//import org.apache.log4j.Logger; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.data.redis.core.HashOperations; +//import org.springframework.data.redis.core.RedisTemplate; +//import org.springframework.data.redis.core.SetOperations; +//import org.springframework.scheduling.annotation.Scheduled; +//import org.springframework.stereotype.Component; +//import org.springframework.transaction.annotation.Transactional; +// +//import redis.clients.jedis.HostAndPort; +//import redis.clients.jedis.JedisCluster; +// +//import com.percero.agents.sync.services.PerceroRedisTemplate; +// +//import edu.emory.mathcs.backport.java.util.Collections; +// +//@Component +//public class JedisCacheDataStore implements ICacheDataStore { +// +// private static Logger log = Logger.getLogger(JedisCacheDataStore.class); +// +// JedisCluster jc = null; +// +// public void init() { +// Set jedisClusterNodes = new HashSet(); +// //Jedis Cluster will attempt to discover cluster nodes automatically +// jedisClusterNodes.add(new HostAndPort("127.0.0.1", 7379)); +// jc = new JedisCluster(jedisClusterNodes); +// jc.set("foo", "bar"); +// String value = jc.get("foo"); +// +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#expire(java.lang.String, long, java.util.concurrent.TimeUnit) +// */ +// @Override +// public Boolean expire( final String key, final long timeout, final TimeUnit timeUnit ) { +// return expire(key, timeout, timeUnit, false); +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#expire(java.util.Collection, long, java.util.concurrent.TimeUnit) +// */ +// @Override +// public Boolean expire( final Collection keys, final long timeout, final TimeUnit timeUnit ) { +// return expire(keys, timeout, timeUnit, false); +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#expire(java.lang.String, long, java.util.concurrent.TimeUnit, java.lang.Boolean) +// */ +// @Override +// public Boolean expire( final String key, final long timeout, final TimeUnit timeUnit, Boolean forceNow ) { +// if (forceNow) { +// Long numSeconds = timeUnit.convert(timeout, TimeUnit.SECONDS); +// return jc.expire(key, numSeconds.intValue()) > 0; +// } +// else { +// expiresToBeWritten.put(key, new PendingExpire(key, timeout, timeUnit)); +// return true; +// } +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#expire(java.util.Collection, long, java.util.concurrent.TimeUnit, java.lang.Boolean) +// */ +// @Override +// public Boolean expire( final Collection keys, final long timeout, final TimeUnit timeUnit, Boolean forceNow ) { +// if (forceNow) { +// Iterator itrKeys = keys.iterator(); +// while (itrKeys.hasNext()) { +// Long numSeconds = timeUnit.convert(timeout, TimeUnit.SECONDS); +// if ( jc.expire(itrKeys.next(), numSeconds.intValue()) <= 0 ) { +// return false; +// } +// } +// return true; +// } +// else { +// Iterator itrKeys = keys.iterator(); +// while (itrKeys.hasNext()) { +// String nextKey = itrKeys.next(); +// expiresToBeWritten.put(nextKey, new PendingExpire(nextKey, timeout, timeUnit)); +// } +// return true; +// } +// } +// +// +// @SuppressWarnings("unchecked") +// private Map expiresToBeWritten = Collections.synchronizedMap(new HashMap()); +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#postExpires() +// */ +// @Override +// @Scheduled(fixedRate=600000) // 10 * 60 * 1000 -> Ten Minutes. +// public void postExpires() { +// log.info("Posting " + expiresToBeWritten.size() + " expire" + (expiresToBeWritten.size() == 1 ? "" : "s")); +// synchronized (expiresToBeWritten) { +// Collection expireKeysToRemove = new HashSet(expiresToBeWritten.size(), (float)1.0); +// Iterator> itrExpiresEntrySet = expiresToBeWritten.entrySet().iterator(); +// while (itrExpiresEntrySet.hasNext()) { +// Map.Entry nextEntry = itrExpiresEntrySet.next(); +// String nextKey = nextEntry.getKey(); +// PendingExpire nextPendingExpire = nextEntry.getValue(); +// expire(nextPendingExpire.key, nextPendingExpire.timeout, nextPendingExpire.timeUnit, true); +// expireKeysToRemove.add(nextKey); +// } +//// Iterator itrExpires = expiresToBeWritten.keySet().iterator(); +//// while (itrExpires.hasNext()) { +//// String nextKey = itrExpires.next(); +//// PendingExpire nextPendingExpire = expiresToBeWritten.get(nextKey); +//// expire(nextPendingExpire.key, nextPendingExpire.timeout, nextPendingExpire.timeUnit, true); +//// expireKeysToRemove.add(nextKey); +//// } +// +// Iterator itrExpireKeysToRemove = expireKeysToRemove.iterator(); +// while (itrExpireKeysToRemove.hasNext()) { +// expiresToBeWritten.remove(itrExpireKeysToRemove.next()); +// } +// } +// } +// +// private class PendingExpire { +// String key; +// long timeout; +// TimeUnit timeUnit; +// +// public PendingExpire(String key, long timeout, TimeUnit timeUnit) { +// this.key = key; +// this.timeout = timeout; +// this.timeUnit = timeUnit; +// } +// } +// +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#getValue(java.lang.String) +// */ +// @Override +// public Object getValue( final String key ) { +// return jc.get( key ); +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#getValues(java.util.Collection) +// */ +// @Override +// public List getValues( final Collection keys ) { +// List result = new ArrayList(); +// if (keys != null) { +// Iterator itrKeys = keys.iterator(); +// while (itrKeys.hasNext()) { +// String nextKey = itrKeys.next(); +// String nextValue = jc.get(nextKey); +// if (nextValue != null) { +// result.add(nextValue); +// } +// } +// } +// +// return result; +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#setValue(java.lang.String, java.lang.Object) +// */ +// @Override +// public void setValue( final String key, final Object value ) { +// jc.set( key, (String) value ); +// } +// +//// public void setValues( final KeyValuePair[] pairs ) { +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#setValues(java.util.Map) +// */ +// @SuppressWarnings("unchecked") +// @Override +// public void setValues( final Map keysAndValuesMap ) { +// +// if (keysAndValuesMap != null) { +// Iterator itrKeysAndValuesMap = keysAndValuesMap.entrySet().iterator(); +// while (itrKeysAndValuesMap.hasNext()) { +// Entry nextEntry = (Entry) itrKeysAndValuesMap.next(); +// jc.set(nextEntry.getKey(), (String) nextEntry.getValue()); +// } +// } +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#addSetValue(java.lang.String, java.lang.Object) +// */ +// @Override +// @Transactional +// public Long addSetValue( final String key, final Object value ) { +// return jc.sadd(key, (String) value);// ? Long.valueOf(1) : Long.valueOf(0); +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#lpushListValue(java.lang.String, java.lang.Object) +// */ +// @Override +// @Transactional +// public Long lpushListValue( final String key, final Object value ) { +// return jc.lpush(key, (String) value); +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#listAll(java.lang.String) +// */ +// @Override +// @Transactional +// public List listAll( final String key) { +// return listRange(key, Long.valueOf(0), Long.valueOf(-1)); +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#listRange(java.lang.String, java.lang.Long, java.lang.Long) +// */ +// @Override +// @Transactional +// public List listRange( final String key, final Long start, final Long end ) { +// return jc.lrange(key, start, end); +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#listIndex(java.lang.String, java.lang.Long) +// */ +// @Override +// @Transactional +// public Object listIndex( final String key, final Long index ) { +// return jc.lindex(key, index); +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#listIndex(java.lang.String) +// */ +// @Override +// @Transactional +// public Object listIndex( final String key ) { +// return jc.lindex(key, Long.valueOf(0)); +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#getHashValue(java.lang.String, java.lang.String) +// */ +// @Override +// @Transactional +// public Object getHashValue( final String key, final String hashKey ) { +// return jc.hget(key, hashKey); +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#getHashKeys(java.lang.String) +// */ +// @Override +// @Transactional +// public Set getHashKeys( final String key ) { +// return jc.hkeys(key); +// } +// +//// /* (non-Javadoc) +//// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#getHashEntries(java.lang.String) +//// */ +//// @Override +//// @Transactional +//// public Map getHashEntries( final String key ) { +//// return jc.hmget(getHashKeys(key)); +//// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#getHashSize(java.lang.String) +// */ +// @Override +// @Transactional +// public Long getHashSize( final String key ) { +// return jc.hlen(key); +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#deleteHashKey(java.lang.String, java.lang.Object) +// */ +// @Override +// @Transactional +// public void deleteHashKey( final String key, final String hashKey ) { +// jc.hdel(key, hashKey); +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#setHashValue(java.lang.String, java.lang.String, java.lang.Object) +// */ +// @Override +// @Transactional +// public void setHashValue( final String key, final String hashKey, final Object value ) { +// jc.hset(key, hashKey, (String) value); +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#setAllHashValues(java.lang.String, java.util.Map) +// */ +// @Override +// @Transactional +// public void setAllHashValues( final String key, final Map hashMap ) { +// jc.hsetAll(key, hashMap); +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#removeSetValue(java.lang.String, java.lang.Object) +// */ +// @Override +// @Transactional +// public Long removeSetValue( final String key, final Object value ) { +// return template.opsForSet().remove(key, value);// ? Long.valueOf(1) : Long.valueOf(0); +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#getSetIsEmpty(java.lang.String) +// */ +// @Override +// @Transactional +// public Boolean getSetIsEmpty( final String key ) { +// Long result = template.opsForSet().size(key);// ? Long.valueOf(1) : Long.valueOf(0); +// return (result <= 0); +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#removeSetValueAndGetSize(java.lang.String, java.lang.Object) +// */ +// @Override +// @Transactional +// public Long removeSetValueAndGetSize( final String key, final Object value ) { +// SetOperations setOps = template.opsForSet(); +// setOps.remove(key, value); +// return setOps.size(key); +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#removeSetsValue(java.util.Collection, java.lang.Object) +// */ +// @Override +// public void removeSetsValue( final Collection keys, final Object value ) { +// SetOperations setOps = template.opsForSet(); +// Iterator itrKeys = keys.iterator(); +// while(itrKeys.hasNext()) { +// Object nextKey = itrKeys.next(); +// try { +// setOps.remove((String) nextKey, value); +// } catch(Exception e) { +// log.warn("Invalid Key", e); +// } +// } +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#removeSetsValue(java.lang.String, java.util.Collection, java.lang.Object) +// */ +// @Override +// public void removeSetsValue( final String keysPrefix, final Collection keys, final Object value ) { +// SetOperations setOps = template.opsForSet(); +// Iterator itrKeys = keys.iterator(); +// while(itrKeys.hasNext()) { +// Object nextKey = itrKeys.next(); +// try { +// setOps.remove(keysPrefix + (String) nextKey, value); +// } catch(Exception e) { +// log.warn("Invalid Key", e); +// } +// } +// } +// +//// private static long keysCount = 0; +//// public Set keys(String pattern) { +//// Set result = template.keys(pattern); +//// log.info("Keys: " + ++keysCount); +//// +//// return result; +//// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#hasKey(java.lang.String) +// */ +// @Override +// public Boolean hasKey(String key) { +// Boolean result = template.hasKey(key); +// return result; +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#hasHashKey(java.lang.String, java.lang.String) +// */ +// @Override +// public Boolean hasHashKey(String key, String hashKey) { +// Boolean result = template.opsForHash().hasKey(key, hashKey); +// return result; +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#swapHashKey(java.lang.String, java.lang.String, java.lang.String) +// */ +// @Override +// @Transactional +// public void swapHashKey( final String key, final String oldHashKey, final String newHashValue ) { +// HashOperations hashOps = template.opsForHash(); +// if (hashOps.hasKey(key, oldHashKey)) { +// hashOps.put(key, newHashValue, hashOps.get(key, oldHashKey)); +// hashOps.delete(key, oldHashKey); +// } +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#renameIfAbsent(java.lang.String, java.lang.String) +// */ +// @Override +// public Boolean renameIfAbsent(String oldKey, String newKey) { +// return template.renameIfAbsent(oldKey, newKey); +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#rename(java.lang.String, java.lang.String) +// */ +// @Override +// public void rename(String oldKey, String newKey) { +// template.rename(oldKey, newKey); +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#setUnionAndStore(java.lang.String, java.lang.String, java.lang.String) +// */ +// @Override +// public Long setUnionAndStore(String key, String otherKey, String destKey) { +// return template.opsForSet().unionAndStore(key, otherKey, destKey); +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#setUnionAndStore(java.lang.String, java.util.Collection, java.lang.String) +// */ +// @Override +// public Long setUnionAndStore(String key, Collection otherKeys, String destKey) { +// return template.opsForSet().unionAndStore(key, otherKeys, destKey); +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisDataStore#removeSetValues(java.lang.String, java.util.Collection) +// */ +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#removeSetValues(java.lang.String, java.util.Collection) +// */ +// @Override +// @Transactional +// public void removeSetValues( final String key, final Collection values ) { +// SetOperations setOps = template.opsForSet(); +// +// Iterator itrValues = values.iterator(); +// while(itrValues.hasNext()) { +// try { +// setOps.remove(key, itrValues.next()); +// } catch(Exception e) { +// log.error("Unable to remove Set Value", e); +// } +// } +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisDataStore#getSetValue(java.lang.String) +// */ +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#getSetValue(java.lang.String) +// */ +// @Override +// public Set getSetValue( final String key ) { +// return template.opsForSet().members(key); +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#replaceSet(java.lang.String, java.util.Collection) +// */ +// @Override +// @Transactional +// public Long replaceSet( final String key, Collection values ) { +// template.delete(key); +// if (values != null) { +// return template.opsForSet().add(key, values.toArray()); +// } +// else { +// return Long.valueOf(0); +// } +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisDataStore#swapSetValue(java.lang.String, java.lang.String, java.lang.String) +// */ +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#swapSetValue(java.lang.String, java.lang.String, java.lang.String) +// */ +// @Override +// @Transactional +// public void swapSetValue( final String key, final String oldValue, final String newValue ) { +// SetOperations setOps = template.opsForSet(); +// if (setOps.isMember(key, oldValue)) { +// setOps.remove(key, oldValue); +// setOps.add(key, newValue); +// } +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#getSetIsMember(java.lang.String, java.lang.Object) +// */ +// @Override +// public Boolean getSetIsMember( final String key, final Object object ) { +// return template.opsForSet().isMember(key, object); +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#getSetsContainsMembers(java.util.Collection, java.lang.Object[]) +// */ +// @Override +// @Transactional +// public Set getSetsContainsMembers( final Collection keys, final Object[] membersToCheck ) { +// SetOperations setOps = template.opsForSet(); +// String randomKey = "UserTokenCheck:" + System.currentTimeMillis(); +// setOps.add(randomKey, membersToCheck); +// +// Set intersectingMembers = new HashSet(); +// Iterator itrKeys = keys.iterator(); +// while (itrKeys.hasNext()) { +// String nextKey = itrKeys.next(); +// intersectingMembers.addAll(setOps.intersect(randomKey, nextKey)); +// } +// template.delete(randomKey); +// return intersectingMembers; +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#getSetIntersect(java.lang.String, java.util.Collection) +// */ +// @Override +// public Set getSetIntersect( final String key, Collection otherKeys ) { +// return template.opsForSet().intersect(key, otherKeys); +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#getSetIntersect(java.lang.String, java.lang.String) +// */ +// @Override +// public Set getSetIntersect( final String key, String otherKey ) { +// return template.opsForSet().intersect(key, otherKey); +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#getSetUnion(java.lang.String, java.util.Collection) +// */ +// @Override +// public Set getSetUnion( final String key, final Collection otherKeys ) { +// return template.opsForSet().union(key, otherKeys); +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#getSetUnion(java.lang.String, java.lang.String) +// */ +// @Override +// public Set getSetUnion( final String key, final String otherKey ) { +// return template.opsForSet().union(key, otherKey); +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#deleteKey(java.lang.String) +// */ +// @Override +// public void deleteKey( final String key ) { +// template.delete(key); +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#deleteKeys(java.util.Collection) +// */ +// @Override +// public void deleteKeys( final Collection keys ) { +// if (keys != null && !keys.isEmpty()) { +// template.delete(keys); +// } +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#setSetValue(java.lang.String, java.lang.Object) +// */ +// @Override +// @Transactional +// public Long setSetValue(String key, Object value) { +// return template.opsForSet().add(key, value);// ? Long.valueOf(1) : Long.valueOf(0); +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#getSet(java.lang.String) +// */ +// @Override +// public SetOperations getSet(String key) { +// return template.opsForSet(); +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#getTemplate() +// */ +// @Override +// public RedisTemplate getTemplate() { +// return template; +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#keys(java.lang.String) +// */ +// @Override +// public Collection keys(String pattern) { +// return template.keys(pattern); +// } +// +// /* (non-Javadoc) +// * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#removeKeysValue(java.lang.String, java.lang.Object) +// */ +// @Override +// @Transactional +// public void removeKeysValue(String pattern, Object value) { +// Set keys = template.keys(pattern); +// Iterator itrKeys = keys.iterator(); +// +// while(itrKeys.hasNext()) { +// try { +// template.opsForSet().remove(itrKeys.next(), value); +// } catch(Exception e) { +// log.error("Error removing KeysValue", e); +// } +// } +// } +// +//} diff --git a/src/main/java/com/percero/agents/sync/datastore/RedisDataStore.java b/src/main/java/com/percero/agents/sync/datastore/RedisCacheDataStore.java similarity index 53% rename from src/main/java/com/percero/agents/sync/datastore/RedisDataStore.java rename to src/main/java/com/percero/agents/sync/datastore/RedisCacheDataStore.java index cfbcb4d..5fbf1f9 100644 --- a/src/main/java/com/percero/agents/sync/datastore/RedisDataStore.java +++ b/src/main/java/com/percero/agents/sync/datastore/RedisCacheDataStore.java @@ -6,38 +6,49 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.TimeUnit; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.HashOperations; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.SetOperations; import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; -import com.percero.agents.sync.access.KeyValuePair; import com.percero.agents.sync.services.PerceroRedisTemplate; import edu.emory.mathcs.backport.java.util.Collections; -@Component -public class RedisDataStore { +public class RedisCacheDataStore implements ICacheDataStore { - private static Logger log = Logger.getLogger(RedisDataStore.class); + private static Logger log = Logger.getLogger(RedisCacheDataStore.class); @Autowired - private PerceroRedisTemplate template; + protected PerceroRedisTemplate template; + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#expire(java.lang.String, long, java.util.concurrent.TimeUnit) + */ + @Override public Boolean expire( final String key, final long timeout, final TimeUnit timeUnit ) { return expire(key, timeout, timeUnit, false); } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#expire(java.util.Collection, long, java.util.concurrent.TimeUnit) + */ + @Override public Boolean expire( final Collection keys, final long timeout, final TimeUnit timeUnit ) { return expire(keys, timeout, timeUnit, false); } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#expire(java.lang.String, long, java.util.concurrent.TimeUnit, java.lang.Boolean) + */ + @Override public Boolean expire( final String key, final long timeout, final TimeUnit timeUnit, Boolean forceNow ) { if (forceNow) { return template.expire(key, timeout, timeUnit); @@ -48,6 +59,10 @@ public Boolean expire( final String key, final long timeout, final TimeUnit time } } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#expire(java.util.Collection, long, java.util.concurrent.TimeUnit, java.lang.Boolean) + */ + @Override public Boolean expire( final Collection keys, final long timeout, final TimeUnit timeUnit, Boolean forceNow ) { if (forceNow) { Iterator itrKeys = keys.iterator(); @@ -71,6 +86,10 @@ public Boolean expire( final Collection keys, final long timeout, final @SuppressWarnings("unchecked") private Map expiresToBeWritten = Collections.synchronizedMap(new HashMap()); + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#postExpires() + */ + @Override @Scheduled(fixedRate=600000) // 10 * 60 * 1000 -> Ten Minutes. public void postExpires() { log.info("Posting " + expiresToBeWritten.size() + " expire" + (expiresToBeWritten.size() == 1 ? "" : "s")); @@ -112,19 +131,35 @@ public PendingExpire(String key, long timeout, TimeUnit timeUnit) { } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#getValue(java.lang.String) + */ + @Override public Object getValue( final String key ) { return template.opsForValue().get( key ); } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#getValues(java.util.Collection) + */ + @Override public List getValues( final Collection keys ) { return template.opsForValue().multiGet(keys); } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#setValue(java.lang.String, java.lang.Object) + */ + @Override public void setValue( final String key, final Object value ) { template.opsForValue().set( key, value ); } // public void setValues( final KeyValuePair[] pairs ) { + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#setValues(java.util.Map) + */ + @Override public void setValues( final Map keysAndValuesMap ) { template.opsForValue().multiSet(keysAndValuesMap); // if (pairs != null) { @@ -134,83 +169,163 @@ public void setValues( final Map keysAndValuesMap ) { // } } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#addSetValue(java.lang.String, java.lang.Object) + */ + @Override @Transactional public Long addSetValue( final String key, final Object value ) { return template.opsForSet().add(key, value);// ? Long.valueOf(1) : Long.valueOf(0); } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#lpushListValue(java.lang.String, java.lang.Object) + */ + @Override @Transactional public Long lpushListValue( final String key, final Object value ) { return template.opsForList().leftPush(key, value); } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#listAll(java.lang.String) + */ + @Override @Transactional public List listAll( final String key) { return listRange(key, Long.valueOf(0), Long.valueOf(-1)); } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#listRange(java.lang.String, java.lang.Long, java.lang.Long) + */ + @Override @Transactional public List listRange( final String key, final Long start, final Long end ) { return template.opsForList().range(key, start, end); } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#listIndex(java.lang.String, java.lang.Long) + */ + @Override @Transactional public Object listIndex( final String key, final Long index ) { return template.opsForList().index(key, index); } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#listIndex(java.lang.String) + */ + @Override @Transactional public Object listIndex( final String key ) { return template.opsForList().index(key, Long.valueOf(0)); } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#getHashValue(java.lang.String, java.lang.String) + */ + @Override @Transactional public Object getHashValue( final String key, final String hashKey ) { return template.opsForHash().get(key, hashKey); } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#getHashKeys(java.lang.String) + */ + @Override @Transactional - public Set getHashKeys( final String key ) { - return template.opsForHash().keys(key); + public Set getHashKeys( final String key ) { + Set keysObjects = template.opsForHash().keys(key); + Set results = new HashSet(keysObjects.size()); + for(Object nextKey : keysObjects) { + results.add((String)nextKey); + } + return results; } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#getHashEntries(java.lang.String) + */ + @Override @Transactional - public Map getHashEntries( final String key ) { - return template.opsForHash().entries(key); + public Map getHashEntries( final String key ) { + Map entries = template.opsForHash().entries(key); + Map result = new HashMap(); + Iterator> itrEntries = entries.entrySet().iterator(); + while (itrEntries.hasNext()) { + Entry nextEntry = itrEntries.next(); + result.put((String) nextEntry.getKey(), nextEntry.getValue()); + } + return result; } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#getHashSize(java.lang.String) + */ + @Override @Transactional public Long getHashSize( final String key ) { return template.opsForHash().size(key); } + + @Transactional + public Long getSetSize( final String key ) { + return template.opsForSet().size(key); + } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#deleteHashKey(java.lang.String, java.lang.Object) + */ + @Override @Transactional - public void deleteHashKey( final String key, final Object hashKey ) { + public void deleteHashKey( final String key, final String hashKey ) { template.opsForHash().delete(key, hashKey); } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#setHashValue(java.lang.String, java.lang.String, java.lang.Object) + */ + @Override @Transactional public void setHashValue( final String key, final String hashKey, final Object value ) { template.opsForHash().put(key, hashKey, value); } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#setAllHashValues(java.lang.String, java.util.Map) + */ + @Override @Transactional public void setAllHashValues( final String key, final Map hashMap ) { template.opsForHash().putAll(key, hashMap); } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#removeSetValue(java.lang.String, java.lang.Object) + */ + @Override @Transactional public Long removeSetValue( final String key, final Object value ) { - Long result = template.opsForSet().remove(key, value);// ? Long.valueOf(1) : Long.valueOf(0); - return result; + return template.opsForSet().remove(key, value);// ? Long.valueOf(1) : Long.valueOf(0); } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#getSetIsEmpty(java.lang.String) + */ + @Override @Transactional public Boolean getSetIsEmpty( final String key ) { Long result = template.opsForSet().size(key);// ? Long.valueOf(1) : Long.valueOf(0); return (result <= 0); } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#removeSetValueAndGetSize(java.lang.String, java.lang.Object) + */ + @Override @Transactional public Long removeSetValueAndGetSize( final String key, final Object value ) { SetOperations setOps = template.opsForSet(); @@ -218,6 +333,10 @@ public Long removeSetValueAndGetSize( final String key, final Object value ) { return setOps.size(key); } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#removeSetsValue(java.util.Collection, java.lang.Object) + */ + @Override public void removeSetsValue( final Collection keys, final Object value ) { SetOperations setOps = template.opsForSet(); Iterator itrKeys = keys.iterator(); @@ -231,6 +350,10 @@ public void removeSetsValue( final Collection keys, final Obje } } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#removeSetsValue(java.lang.String, java.util.Collection, java.lang.Object) + */ + @Override public void removeSetsValue( final String keysPrefix, final Collection keys, final Object value ) { SetOperations setOps = template.opsForSet(); Iterator itrKeys = keys.iterator(); @@ -244,7 +367,7 @@ public void removeSetsValue( final String keysPrefix, final Collection keys(String pattern) { // Set result = template.keys(pattern); // log.info("Keys: " + ++keysCount); @@ -252,16 +375,28 @@ public void removeSetsValue( final String keysPrefix, final Collection hashOps = template.opsForHash(); @@ -271,22 +406,45 @@ public void swapHashKey( final String key, final String oldHashKey, final String } } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#renameIfAbsent(java.lang.String, java.lang.String) + */ + @Override public Boolean renameIfAbsent(String oldKey, String newKey) { return template.renameIfAbsent(oldKey, newKey); } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#rename(java.lang.String, java.lang.String) + */ + @Override public void rename(String oldKey, String newKey) { template.rename(oldKey, newKey); } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#setUnionAndStore(java.lang.String, java.lang.String, java.lang.String) + */ + @Override public Long setUnionAndStore(String key, String otherKey, String destKey) { return template.opsForSet().unionAndStore(key, otherKey, destKey); } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#setUnionAndStore(java.lang.String, java.util.Collection, java.lang.String) + */ + @Override public Long setUnionAndStore(String key, Collection otherKeys, String destKey) { return template.opsForSet().unionAndStore(key, otherKeys, destKey); } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisDataStore#removeSetValues(java.lang.String, java.util.Collection) + */ + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#removeSetValues(java.lang.String, java.util.Collection) + */ + @Override @Transactional public void removeSetValues( final String key, final Collection values ) { SetOperations setOps = template.opsForSet(); @@ -301,10 +459,21 @@ public void removeSetValues( final String key, final Collection getSetValue( final String key ) { return template.opsForSet().members(key); } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#replaceSet(java.lang.String, java.util.Collection) + */ + @Override @Transactional public Long replaceSet( final String key, Collection values ) { template.delete(key); @@ -316,6 +485,13 @@ public Long replaceSet( final String key, Collection values ) } } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisDataStore#swapSetValue(java.lang.String, java.lang.String, java.lang.String) + */ + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#swapSetValue(java.lang.String, java.lang.String, java.lang.String) + */ + @Override @Transactional public void swapSetValue( final String key, final String oldValue, final String newValue ) { SetOperations setOps = template.opsForSet(); @@ -325,10 +501,18 @@ public void swapSetValue( final String key, final String oldValue, final String } } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#getSetIsMember(java.lang.String, java.lang.Object) + */ + @Override public Boolean getSetIsMember( final String key, final Object object ) { return template.opsForSet().isMember(key, object); } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#getSetsContainsMembers(java.util.Collection, java.lang.Object[]) + */ + @Override @Transactional public Set getSetsContainsMembers( final Collection keys, final Object[] membersToCheck ) { SetOperations setOps = template.opsForSet(); @@ -345,30 +529,105 @@ public Set getSetsContainsMembers( final Collection ke return intersectingMembers; } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#getSetIntersect(java.lang.String, java.util.Collection) + */ + @Override public Set getSetIntersect( final String key, Collection otherKeys ) { return template.opsForSet().intersect(key, otherKeys); } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#getSetIntersect(java.lang.String, java.lang.String) + */ + @Override public Set getSetIntersect( final String key, String otherKey ) { return template.opsForSet().intersect(key, otherKey); } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#getSetUnion(java.lang.String, java.util.Collection) + */ + @Override public Set getSetUnion( final String key, final Collection otherKeys ) { return template.opsForSet().union(key, otherKeys); } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#getSetUnion(java.lang.String, java.lang.String) + */ + @Override public Set getSetUnion( final String key, final String otherKey ) { return template.opsForSet().union(key, otherKey); } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#deleteKey(java.lang.String) + */ + @Override public void deleteKey( final String key ) { template.delete(key); } + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#deleteKeys(java.util.Collection) + */ + @Override public void deleteKeys( final Collection keys ) { if (keys != null && !keys.isEmpty()) { template.delete(keys); } } + + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#setSetValue(java.lang.String, java.lang.Object) + */ + @Override + @Transactional + public Long setSetValue(String key, Object value) { + return template.opsForSet().add(key, value);// ? Long.valueOf(1) : Long.valueOf(0); + } + + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#getSet(java.lang.String) + */ + @Override + public SetOperations getSet(String key) { + return template.opsForSet(); + } + + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#getTemplate() + */ + @Override + public RedisTemplate getTemplate() { + return template; + } + + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#keys(java.lang.String) + */ + @Override + public Collection keys(String pattern) { + return template.keys(pattern); + } + + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisCacheDataStore#removeKeysValue(java.lang.String, java.lang.Object) + */ + @Override + @Transactional + public void removeKeysValue(String pattern, Object value) { + Set keys = template.keys(pattern); + Iterator itrKeys = keys.iterator(); + + while(itrKeys.hasNext()) { + try { + template.opsForSet().remove(itrKeys.next(), value); + } catch(Exception e) { + log.error("Error removing KeysValue", e); + } + } + } } diff --git a/src/main/java/com/percero/agents/sync/datastore/RedisClusterCacheDataStore.java b/src/main/java/com/percero/agents/sync/datastore/RedisClusterCacheDataStore.java new file mode 100644 index 0000000..a357f8c --- /dev/null +++ b/src/main/java/com/percero/agents/sync/datastore/RedisClusterCacheDataStore.java @@ -0,0 +1,136 @@ +package com.percero.agents.sync.datastore; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import org.springframework.data.redis.core.SetOperations; +import org.springframework.stereotype.Component; + +@Component +public class RedisClusterCacheDataStore extends RedisCacheDataStore /*implements IRedisClusterCacheDataStore*/ { + + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisDataStore#setUnionAndStore(java.lang.String, java.lang.String, java.lang.String) + */ + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisClusterCacheDataStore#setUnionAndStore(java.lang.String, java.lang.String, java.lang.String) + */ + @Override + public Long setUnionAndStore(String key, String otherKey, String destKey) { + SetOperations setOps = template.opsForSet(); + Set existingSet = setOps.members(key); + existingSet.addAll(setOps.members(otherKey)); + + Iterator itr = existingSet.iterator(); + while(itr.hasNext()) { + setOps.add(destKey, itr.next()); + } + + return setOps.size(destKey); + } + + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisDataStore#setUnionAndStore(java.lang.String, java.util.Collection, java.lang.String) + */ + @Override + public Long setUnionAndStore(String key, Collection otherKeys, String destKey) { + SetOperations setOps = template.opsForSet(); + Set existingSet = setOps.members(key); + + Iterator itrKeys = otherKeys.iterator(); + while(itrKeys.hasNext()) { + String nextKey = itrKeys.next(); + + existingSet.addAll(setOps.members(nextKey)); + + Iterator itr = existingSet.iterator(); + while(itr.hasNext()) { + setOps.add(destKey, itr.next()); + } + } + + return setOps.size(destKey); + } + + + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisDataStore#getSetIntersect(java.lang.String, java.util.Collection) + */ + @Override + public Set getSetIntersect( final String key, Collection otherKeys ) { + SetOperations setOps = template.opsForSet(); + + Set result = setOps.members(key); + + Iterator itrKeys = otherKeys.iterator(); + + while(itrKeys.hasNext()) { + String nextKey = itrKeys.next(); + Set otherSet = setOps.members(nextKey); + + Iterator itr = result.iterator(); + + while(itr.hasNext()) { + Object nextObject = itr.next(); + if (!otherSet.contains(nextObject)) { + itr.remove(); + } + } + } + + return result; + } + + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisDataStore#getSetIntersect(java.lang.String, java.lang.String) + */ + @Override + public Set getSetIntersect( final String key, String otherKey ) { + SetOperations setOps = template.opsForSet(); + + Set theSet = setOps.members(key); + Set otherSet = setOps.members(otherKey); + Set result = new HashSet(); + + Iterator itr = theSet.iterator(); + + while(itr.hasNext()) { + Object nextObject = itr.next(); + if (otherSet.contains(nextObject)) { + result.add(nextObject); + } + } + + return result; + } + + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisDataStore#getSetUnion(java.lang.String, java.util.Collection) + */ + @Override + public Set getSetUnion( final String key, final Collection otherKeys ) { + SetOperations setOps = template.opsForSet(); + + Set theSet = setOps.members(key); + Iterator itr = otherKeys.iterator(); + while(itr.hasNext()) { + theSet.addAll(setOps.members(itr.next())); + } + return theSet; + } + + /* (non-Javadoc) + * @see com.percero.agents.sync.datastore.IRedisDataStore#getSetUnion(java.lang.String, java.lang.String) + */ + @Override + public Set getSetUnion( final String key, final String otherKey ) { + SetOperations setOps = template.opsForSet(); + + Set theSet = setOps.members(key); + theSet.addAll(setOps.members(otherKey)); + return theSet; + } + +} diff --git a/src/main/java/com/percero/agents/sync/datastore/RedisClusterDataStore.java b/src/main/java/com/percero/agents/sync/datastore/RedisClusterDataStore.java deleted file mode 100644 index f61e85a..0000000 --- a/src/main/java/com/percero/agents/sync/datastore/RedisClusterDataStore.java +++ /dev/null @@ -1,257 +0,0 @@ -package com.percero.agents.sync.datastore; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Set; -import java.util.concurrent.TimeUnit; - -import org.apache.log4j.Logger; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.core.SetOperations; -import org.springframework.stereotype.Component; -import org.springframework.transaction.annotation.Transactional; - -import com.percero.agents.sync.services.PerceroRedisTemplate; - -@Component -public class RedisClusterDataStore { - - private static Logger log = Logger.getLogger(RedisClusterDataStore.class); - - @Autowired - private PerceroRedisTemplate template; - - public Boolean expire( final String key, final long timeout, final TimeUnit timeUnit ) { - return template.expire(key, timeout, timeUnit); - } - - public Object getValue( final String key ) { - return template.opsForValue().get( key ); - } - - public void setValue( final String key, final Object value ) { - template.opsForValue().set( key, value ); - } - - @Transactional - public Long setSetValue( final String key, final Object value ) { - return template.opsForSet().add(key, value);// ? Long.valueOf(1) : Long.valueOf(0); - } - - @Transactional - public Long lpushListValue( final String key, final Object value ) { - return template.opsForList().leftPush(key, value); - } - - @Transactional - public List listAll( final String key) { - return listRange(key, Long.valueOf(0), Long.valueOf(-1)); - } - - @Transactional - public List listRange( final String key, final Long start, final Long end ) { - return template.opsForList().range(key, start, end); - } - - @Transactional - public Object listIndex( final String key, final Long index ) { - return template.opsForList().index(key, index); - } - - @Transactional - public Object listIndex( final String key ) { - return template.opsForList().index(key, Long.valueOf(0)); - } - - @Transactional - public void setHashValue( final String key, final String hashKey, final Object value ) { - template.opsForHash().put(key, hashKey, value); - } - - @Transactional - public Long removeSetValue( final String key, final Object value ) { - return template.opsForSet().remove(key, value);// ? Long.valueOf(1) : Long.valueOf(0); - } - - public void removeSetsValue( final Collection keys, final Object value ) { - SetOperations setOps = template.opsForSet(); - Iterator itrKeys = keys.iterator(); - while(itrKeys.hasNext()) { - String nextKey = itrKeys.next(); - setOps.remove(nextKey, value); - } - } - - public SetOperations getSet( final String key ) { - return template.opsForSet(); - } - - public RedisTemplate getTemplate() { - return template; - } - - public Collection keys(String pattern) { - return template.keys(pattern); - } - - public Boolean renameIfAbsent(String oldKey, String newKey) { - return template.renameIfAbsent(oldKey, newKey); - } - - public void rename(String oldKey, String newKey) { - template.rename(oldKey, newKey); - } - - public Long setUnionAndStore(String key, String otherKey, String destKey) { - SetOperations setOps = template.opsForSet(); - Set existingSet = setOps.members(key); - existingSet.addAll(setOps.members(otherKey)); - - Iterator itr = existingSet.iterator(); - while(itr.hasNext()) { - setOps.add(destKey, itr.next()); - } - - return setOps.size(destKey); - } - - public Long setUnionAndStore(String key, Collection otherKeys, String destKey) { - SetOperations setOps = template.opsForSet(); - Set existingSet = setOps.members(key); - - Iterator itrKeys = otherKeys.iterator(); - while(itrKeys.hasNext()) { - String nextKey = itrKeys.next(); - - existingSet.addAll(setOps.members(nextKey)); - - Iterator itr = existingSet.iterator(); - while(itr.hasNext()) { - setOps.add(destKey, itr.next()); - } - } - - return setOps.size(destKey); - } - - @Transactional - public void removeKeysValue( final String pattern, final Object value ) { - Set keys = template.keys(pattern); - Iterator itrKeys = keys.iterator(); - - while(itrKeys.hasNext()) { - try { - template.opsForSet().remove(itrKeys.next(), value); - } catch(Exception e) { - log.error("Error removing KeysValue", e); - } - } - } - - @Transactional - public void removeSetValues( final String key, final Collection values ) { - SetOperations setOps = template.opsForSet(); - - Iterator itrValues = values.iterator(); - while(itrValues.hasNext()) { - try { - setOps.remove(key, itrValues.next()); - } catch(Exception e) { - log.error("Unable to remove Set Value", e); - } - } - } - - public Set getSetValue( final String key ) { - template.opsForSet().members(key); - return template.opsForSet().members(key); - } - - @Transactional - public void swapSetValue( final String key, final String oldValue, final String newValue ) { - SetOperations setOps = template.opsForSet(); - if (setOps.isMember(key, oldValue)) { - setOps.remove(key, oldValue); - setOps.add(key, newValue); - } - } - - public Boolean getSetIsMember( final String key, final Object object ) { - - return template.opsForSet().isMember(key, object); - } - - public Set getSetIntersect( final String key, Collection otherKeys ) { - SetOperations setOps = template.opsForSet(); - - Set result = setOps.members(key); - - Iterator itrKeys = otherKeys.iterator(); - - while(itrKeys.hasNext()) { - String nextKey = itrKeys.next(); - Set otherSet = setOps.members(nextKey); - - Iterator itr = result.iterator(); - - while(itr.hasNext()) { - Object nextObject = itr.next(); - if (!otherSet.contains(nextObject)) { - itr.remove(); - } - } - } - - return result; - } - - public Set getSetIntersect( final String key, String otherKey ) { - SetOperations setOps = template.opsForSet(); - - Set theSet = setOps.members(key); - Set otherSet = setOps.members(otherKey); - Set result = new HashSet(); - - Iterator itr = theSet.iterator(); - - while(itr.hasNext()) { - Object nextObject = itr.next(); - if (otherSet.contains(nextObject)) { - result.add(nextObject); - } - } - - return result; - } - - public Set getSetUnion( final String key, final Collection otherKeys ) { - SetOperations setOps = template.opsForSet(); - - Set theSet = setOps.members(key); - Iterator itr = otherKeys.iterator(); - while(itr.hasNext()) { - theSet.addAll(setOps.members(itr.next())); - } - return theSet; - } - - public Set getSetUnion( final String key, final String otherKey ) { - SetOperations setOps = template.opsForSet(); - - Set theSet = setOps.members(key); - theSet.addAll(setOps.members(otherKey)); - return theSet; - } - - public void deleteKey( final String key ) { - template.delete(key); - } - - public void deleteKeys( final Collection keys ) { - if (keys.size() > 0) - template.delete(keys); - } -} diff --git a/src/main/java/com/percero/agents/sync/exceptions/SyncDataException.java b/src/main/java/com/percero/agents/sync/exceptions/SyncDataException.java index 2bd4344..fba1fa9 100644 --- a/src/main/java/com/percero/agents/sync/exceptions/SyncDataException.java +++ b/src/main/java/com/percero/agents/sync/exceptions/SyncDataException.java @@ -32,6 +32,10 @@ public SyncDataException(String name, Integer code) { super(name, code); } + public SyncDataException(Exception e) { + super(e); + } + public SyncDataException() { super(); } diff --git a/src/main/java/com/percero/agents/sync/exceptions/SyncException.java b/src/main/java/com/percero/agents/sync/exceptions/SyncException.java index 67eb048..2894a78 100644 --- a/src/main/java/com/percero/agents/sync/exceptions/SyncException.java +++ b/src/main/java/com/percero/agents/sync/exceptions/SyncException.java @@ -12,6 +12,12 @@ public class SyncException extends Exception { public static final Integer INVALID_CLIENT_USER_CODE = -102; public static final String RUN_PROCESS_ERROR = "runProcessError"; public static final Integer RUN_PROCESS_ERROR_CODE = -103; + public static final String MISSING_MAPPED_CLASS_ERROR = "missingMappedClass"; + public static final Integer MISSING_MAPPED_CLASS_ERROR_CODE = -104; + public static final String METHOD_UNSUPPORTED = "methodUnsupported"; + public static final Integer METHOD_UNSUPPORTED_CODE = -105; + public static final String METHOD_NOT_IMPLEMENTED = "methodNotImplemented"; + public static final Integer METHOD_NOT_IMPLEMENTED_CODE = -106; public SyncException(String name, Integer code, String desc, Throwable t) { @@ -32,6 +38,10 @@ public SyncException(String name, Integer code) { this.setCode(code); } + public SyncException(Exception e) { + super(e); + } + public SyncException() { super(); } diff --git a/src/main/java/com/percero/agents/sync/helpers/PostDeleteHelper.java b/src/main/java/com/percero/agents/sync/helpers/PostDeleteHelper.java index abb1644..f089aa4 100644 --- a/src/main/java/com/percero/agents/sync/helpers/PostDeleteHelper.java +++ b/src/main/java/com/percero/agents/sync/helpers/PostDeleteHelper.java @@ -1,14 +1,5 @@ package com.percero.agents.sync.helpers; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import org.apache.log4j.Logger; -import org.codehaus.jackson.map.ObjectMapper; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - import com.percero.agents.sync.access.IAccessManager; import com.percero.agents.sync.services.IPushSyncHelper; import com.percero.agents.sync.services.ISyncAgentService; @@ -16,6 +7,14 @@ import com.percero.agents.sync.vo.PushDeleteResponse; import com.percero.agents.sync.vo.RemovedClassIDPair; import com.percero.framework.vo.IPerceroObject; +import org.apache.log4j.Logger; +import org.codehaus.jackson.map.ObjectMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; @Component public class PostDeleteHelper { @@ -63,25 +62,27 @@ public void postDeleteObject(IPerceroObject theObject, String pusherUserId, Stri log.debug("PostDeleteHelper for " + theObject.toString() + " from clientId " + (pusherClientId == null ? "NULL" : pusherClientId)); ClassIDPair pair = new ClassIDPair(theObject.getID(), theObject.getClass().getCanonicalName()); - + postDeleteObject(pair, pusherUserId, pusherClientId, pushToUser); + } + + public void postDeleteObject(ClassIDPair pair, String pusherUserId, String pusherClientId, boolean pushToUser) throws Exception { // Remove all UpdateJournals for the objects. accessManager.removeUpdateJournalsByObject(pair); // Notify interested users that this object has been deleted. RemovedClassIDPair removedPair = new RemovedClassIDPair(); - removedPair.setClassName(theObject.getClass().getName()); - removedPair.setID(theObject.getID()); + removedPair.setClassName(pair.getClassName()); + removedPair.setID(pair.getID()); + + List clientIds = accessManager.getObjectAccessJournals(pair.getClassName(), pair.getID()); - List clientIds = accessManager.getObjectAccessJournals(theObject - .getClass().getName(), theObject.getID()); - // Now remove the AccessJournals for this Object. accessManager.removeAccessJournalsByObject(pair); // Now remove the ObjectModJournals for this Object. accessManager.removeObjectModJournalsByObject(pair); // Now remove the HistoricalObjects for this Object. accessManager.removeHistoricalObjectsByObject(pair); - + // Now run past the ChangeWatcher. accessManager.checkChangeWatchers(pair, null, null); // Remove ChangeWatchers associated with this object. @@ -105,7 +106,6 @@ protected void pushObjectDeleteJournals(Collection clientIds, String cla pushDeleteResponse = new PushDeleteResponse(); pushDeleteResponse.setObjectList(new ArrayList()); -// pushDeleteResponse.setClientId(nextClientId); pushDeleteResponse.getObjectList().add(removedPair); pushDeleteResponse.setObjectJson(objectJson); diff --git a/src/main/java/com/percero/agents/sync/helpers/PostPutHelper.java b/src/main/java/com/percero/agents/sync/helpers/PostPutHelper.java index 36ead6e..50c5a0d 100644 --- a/src/main/java/com/percero/agents/sync/helpers/PostPutHelper.java +++ b/src/main/java/com/percero/agents/sync/helpers/PostPutHelper.java @@ -4,9 +4,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.Iterator; -import java.util.List; import java.util.Map; -import java.util.Set; import org.apache.log4j.Logger; import org.codehaus.jackson.map.ObjectMapper; @@ -84,7 +82,7 @@ public void postPutObject(ClassIDPair pair, String pusherUserId, String pusherCl // Now run past the ChangeWatcher. - if (changedFields == null) { + if (changedFields == null || changedFields.isEmpty()) { accessManager.checkChangeWatchers(pair, null, null); } else { @@ -132,7 +130,7 @@ public void pushObjectUpdateJournals(Collection clientIds, ClassIDPair c PushUpdateResponse pushUpdateResponse = new PushUpdateResponse(); pushUpdateResponse.setObjectList(new ArrayList()); - BaseDataObject object = dataProvider.systemGetById(classIdPair); + BaseDataObject object = (BaseDataObject) dataProvider.findById(classIdPair, null); if (object != null) { pushUpdateResponse.getObjectList().add(object); diff --git a/src/main/java/com/percero/agents/sync/helpers/RedisPostClientHelper.java b/src/main/java/com/percero/agents/sync/helpers/RedisPostClientHelper.java index 2f3934b..000f027 100644 --- a/src/main/java/com/percero/agents/sync/helpers/RedisPostClientHelper.java +++ b/src/main/java/com/percero/agents/sync/helpers/RedisPostClientHelper.java @@ -2,7 +2,6 @@ import java.util.Collection; import java.util.Iterator; -import java.util.Set; import java.util.concurrent.TimeUnit; import org.apache.log4j.Logger; @@ -12,7 +11,7 @@ import com.percero.agents.sync.access.IAccessManager; import com.percero.agents.sync.access.RedisKeyUtils; -import com.percero.agents.sync.datastore.RedisDataStore; +import com.percero.agents.sync.datastore.ICacheDataStore; @Component public class RedisPostClientHelper { @@ -20,7 +19,7 @@ public class RedisPostClientHelper { private static final Logger log = Logger.getLogger(RedisPostClientHelper.class); @Autowired - private RedisDataStore redisDataStore; + private ICacheDataStore cacheDataStore; @Autowired IAccessManager accessManager; @@ -34,11 +33,11 @@ public void setUserDeviceTimeout(Long value) { @Transactional public void postClient(String clientId) throws Exception { // Get the client's userId. - String userId = (String) redisDataStore.getValue(RedisKeyUtils.client(clientId)); + String userId = (String) cacheDataStore.getValue(RedisKeyUtils.client(clientId)); if (userId != null && userId.length() > 0) { // Timeout Client's User ID. - redisDataStore.expire(RedisKeyUtils.client(clientId), userDeviceTimeout, TimeUnit.SECONDS); + cacheDataStore.expire(RedisKeyUtils.client(clientId), userDeviceTimeout, TimeUnit.SECONDS); // Remove from NonPersistent Clients //redisDataStore.removeSetValue(RedisKeyUtils.clientsNonPersistent(), clientId); @@ -47,13 +46,13 @@ public void postClient(String clientId) throws Exception { //redisDataStore.removeSetValue(RedisKeyUtils.clientsPersistent(), clientId); // Timeout Client User - redisDataStore.expire(RedisKeyUtils.clientUser(userId), userDeviceTimeout, TimeUnit.SECONDS); + cacheDataStore.expire(RedisKeyUtils.clientUser(userId), userDeviceTimeout, TimeUnit.SECONDS); // Timeout UpdateJournals for this Client. - redisDataStore.expire(RedisKeyUtils.updateJournal(clientId), userDeviceTimeout, TimeUnit.SECONDS); + cacheDataStore.expire(RedisKeyUtils.updateJournal(clientId), userDeviceTimeout, TimeUnit.SECONDS); // Timeout DeleteJournals for this Client. - redisDataStore.expire(RedisKeyUtils.deleteJournal(clientId), userDeviceTimeout, TimeUnit.SECONDS); + cacheDataStore.expire(RedisKeyUtils.deleteJournal(clientId), userDeviceTimeout, TimeUnit.SECONDS); // // Timeout TransactionJournals for this Client. // Collection transJournalKeys = redisDataStore.keys(RedisKeyUtils.transactionJournal(clientId, "*")); @@ -65,13 +64,13 @@ public void postClient(String clientId) throws Exception { // Timeout Client's UserDevice. // Collection userDeviceKeys = redisDataStore.keys(RedisKeyUtils.userDevice(userId, "*")); // TODO: This expire no longer works!!! - Collection userDeviceKeys = redisDataStore.getHashKeys(RedisKeyUtils.userDeviceHash(userId)); - Iterator itrUserDevices = userDeviceKeys.iterator(); + Collection userDeviceKeys = cacheDataStore.getHashKeys(RedisKeyUtils.userDeviceHash(userId)); + Iterator itrUserDevices = userDeviceKeys.iterator(); while (itrUserDevices.hasNext()) { - String nextKey = (String) itrUserDevices.next(); - if (clientId.equals(redisDataStore.getValue(nextKey))) { - redisDataStore.expire(RedisKeyUtils.deviceHash(nextKey), userDeviceTimeout, TimeUnit.SECONDS); - redisDataStore.expire(nextKey, userDeviceTimeout, TimeUnit.SECONDS); + String nextKey = itrUserDevices.next(); + if (clientId.equals(cacheDataStore.getValue(nextKey))) { + cacheDataStore.expire(RedisKeyUtils.deviceHash(nextKey), userDeviceTimeout, TimeUnit.SECONDS); + cacheDataStore.expire(nextKey, userDeviceTimeout, TimeUnit.SECONDS); } } } diff --git a/src/main/java/com/percero/agents/sync/hibernate/SyncHibernateUtils.java b/src/main/java/com/percero/agents/sync/hibernate/SyncHibernateUtils.java index ac32a24..7175938 100644 --- a/src/main/java/com/percero/agents/sync/hibernate/SyncHibernateUtils.java +++ b/src/main/java/com/percero/agents/sync/hibernate/SyncHibernateUtils.java @@ -613,7 +613,7 @@ private static boolean hasAccess(Object ob, String userId, IMappedClassManager m if (isValidReadQuery) { // There is a ReadQuery, so process that. Query readFilter = s.createQuery(mappedClass.getReadQuery().getQuery()); - mappedClass.getReadQuery().setQueryParameters(readFilter, ob, userId); + mappedClass.getReadQuery().setQueryParameters(readFilter.getQueryString(), ob, userId); Number readFilterResult = (Number) readFilter.uniqueResult(); if (readFilterResult == null || readFilterResult.intValue() <= 0) hasAccess = false; @@ -639,7 +639,7 @@ private static boolean hasFieldAccess(Object ob, String userId, MappedField mapp ob = getShell(ob); // There is a ReadQuery, so process that. Query readFilter = s.createQuery(mappedField.getReadQuery().getQuery()); - mappedField.getReadQuery().setQueryParameters(readFilter, ob, userId); + mappedField.getReadQuery().setQueryParameters(readFilter.getQueryString(), ob, userId); Number readFilterResult = (Number) readFilter.uniqueResult(); if (readFilterResult == null || readFilterResult.intValue() <= 0) hasAccess = false; diff --git a/src/main/java/com/percero/agents/sync/jobs/ProcessorResult.java b/src/main/java/com/percero/agents/sync/jobs/ProcessorResult.java new file mode 100644 index 0000000..7f577c9 --- /dev/null +++ b/src/main/java/com/percero/agents/sync/jobs/ProcessorResult.java @@ -0,0 +1,82 @@ +package com.percero.agents.sync.jobs; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Created by jonnysamps on 8/31/15. + */ +public class ProcessorResult { + private int total; + private int numFailed; + + private boolean success = true; + private Map details = new HashMap(); + private Map> failures = new HashMap>(); + + public int getTotal() { + return total; + } + + public int getNumFailed(){ + return numFailed; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public Map getDetails() { + return details; + } + + public void addResult(String type, boolean wasSuccess, String message){ + total++; + if(!wasSuccess) { + success = false; + numFailed++; + if(!failures.containsKey(type)) failures.put(type, new ArrayList()); + failures.get(type).add(message); + } + else{ + if(!details.containsKey(type)) details.put(type, 0); + details.put(type, details.get(type)+1); + } + } + + /** + * @see ProcessorResult.addResult(String, boolean, String) + * @param type + */ + public void addResult(String type){ + this.addResult(type, true, null); + } + + /** + * Some niceties for logging + * @return + */ + public String toString(){ + StringBuilder sb = new StringBuilder(); + sb.append("Overall Success :"); sb.append(isSuccess()); sb.append("\n"); + sb.append("Total results :"); sb.append(getTotal()); sb.append("\n"); + sb.append("Total failures :"); sb.append(getNumFailed()); sb.append("\n"); + sb.append("Failures by type:"); sb.append("\n"); + for(String key : failures.keySet()){ + sb.append(" "); sb.append(key); sb.append(":"); sb.append("\n"); + int i = 1; + for(String message : failures.get(key)){ + sb.append(" "); sb.append(i); sb.append(": "); sb.append(message); sb.append("\n"); + i++; + } + } + + return sb.toString(); + } +} diff --git a/src/main/java/com/percero/agents/sync/jobs/UpdateTableConnectionFactory.java b/src/main/java/com/percero/agents/sync/jobs/UpdateTableConnectionFactory.java new file mode 100644 index 0000000..8c3fa0f --- /dev/null +++ b/src/main/java/com/percero/agents/sync/jobs/UpdateTableConnectionFactory.java @@ -0,0 +1,80 @@ +package com.percero.agents.sync.jobs; + +import com.mchange.v2.c3p0.ComboPooledDataSource; +import org.apache.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.beans.PropertyVetoException; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +/** + * Created by jonnysamps on 9/2/15. + */ +@Component +public class UpdateTableConnectionFactory { + + private static Logger logger = Logger.getLogger(UpdateTableConnectionFactory.class); + + @Autowired + @Value("$pf{updateTable.driverClassName:com.mysql.jdbc.Driver}") + private String driverClassName; + public void setDriverClassName(String val){ + this.driverClassName = val; + } + + @Autowired + @Value("$pf{updateTable.username}") + private String username; + public void setUsername(String val){ + this.username = val; + } + + @Autowired + @Value("$pf{updateTable.password}") + private String password; + public void setPassword(String val){ + this.password = val; + } + + @Autowired + @Value("$pf{updateTable.jdbcUrl:jdbc:mysql://localhost/db}") + private String jdbcUrl; + public void setJdbcUrl(String val){ + this.jdbcUrl = val; + } + + private ComboPooledDataSource cpds; + @PostConstruct + public void init() throws PropertyVetoException{ + try { + cpds = new ComboPooledDataSource(); + cpds.setDriverClass(driverClassName); //loads the jdbc driver + cpds.setJdbcUrl(jdbcUrl); + cpds.setUser(username); + cpds.setPassword(password); + + // the settings below are optional -- c3p0 can work with defaults + cpds.setMinPoolSize(5); + cpds.setAcquireIncrement(5); + + }catch(PropertyVetoException pve){ + logger.error(pve.getMessage(), pve); + throw pve; + } + } + + public Connection getConnection() throws SQLException{ + try{ + return cpds.getConnection(); + }catch(SQLException e){ + logger.error(e.getMessage(), e); + throw e; + } + } +} diff --git a/src/main/java/com/percero/agents/sync/jobs/UpdateTablePoller.java b/src/main/java/com/percero/agents/sync/jobs/UpdateTablePoller.java new file mode 100644 index 0000000..323452e --- /dev/null +++ b/src/main/java/com/percero/agents/sync/jobs/UpdateTablePoller.java @@ -0,0 +1,49 @@ +package com.percero.agents.sync.jobs; + +import org.apache.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +/** + * Created by jonnysamps on 8/31/15. + */ +@Component +public class UpdateTablePoller { + + private static Logger logger = Logger.getLogger(UpdateTablePoller.class); + + private String[] tableNames = new String[0]; + @Autowired + @Value("$pf{updateTable.tableNames}") + public void setTableNames(String val){ + tableNames = val.split(","); + } + + @Autowired + UpdateTableProcessorFactory updateTableProcessorFactory; + + public boolean enabled = true; + + /** + * Run every minute + */ + @Scheduled(fixedDelay=5000, initialDelay=10000) // Every 5 seconds + public void pollUpdateTables(){ + if(enabled) + for(String tableName : tableNames){ + UpdateTableProcessor processor = updateTableProcessorFactory.getProcessor(tableName); + ProcessorResult result = processor.process(); + if(result.isSuccess()){ + logger.debug("Update table processor ("+tableName+") finished successfully. Total rows ("+result.getTotal()+")"); + } + else{ + logger.warn("Update table processor ("+tableName+") failed. Details:"); + logger.warn(result); + } + } + } + + +} diff --git a/src/main/java/com/percero/agents/sync/jobs/UpdateTableProcessor.java b/src/main/java/com/percero/agents/sync/jobs/UpdateTableProcessor.java new file mode 100644 index 0000000..b13745e --- /dev/null +++ b/src/main/java/com/percero/agents/sync/jobs/UpdateTableProcessor.java @@ -0,0 +1,562 @@ +package com.percero.agents.sync.jobs; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Random; +import java.util.Set; + +import javax.persistence.Table; + +import org.apache.log4j.Logger; +import org.joda.time.DateTime; + +import com.percero.agents.sync.access.IAccessManager; +import com.percero.agents.sync.cache.CacheManager; +import com.percero.agents.sync.helpers.PostDeleteHelper; +import com.percero.agents.sync.helpers.PostPutHelper; +import com.percero.agents.sync.metadata.IMappedClassManager; +import com.percero.agents.sync.metadata.MappedClass; +import com.percero.agents.sync.metadata.MappedClassManagerFactory; +import com.percero.agents.sync.metadata.MappedField; +import com.percero.agents.sync.metadata.MappedFieldPerceroObject; +import com.percero.agents.sync.services.DataProviderManager; +import com.percero.agents.sync.services.IDataProvider; +import com.percero.agents.sync.vo.BaseDataObject; +import com.percero.agents.sync.vo.ClassIDPair; +import com.percero.framework.bl.IManifest; +import com.percero.framework.vo.IPerceroObject; +import com.percero.framework.vo.PerceroList; + +/** + * Responsible for querying an update table and processing the rows. + * Created by Jonathan Samples on 8/31/15. + */ +public class UpdateTableProcessor { + + private static Logger logger = Logger.getLogger(UpdateTableProcessor.class); + + private static final int EXPIRATION_TIME = 1000*60*30; // 30 minutes + private String tableName; + private UpdateTableConnectionFactory connectionFactory; + private PostDeleteHelper postDeleteHelper; + private PostPutHelper postPutHelper; + private IManifest manifest; + private CacheManager cacheManager; + private DataProviderManager dataProviderManager; + private IAccessManager accessManager; + + public UpdateTableProcessor(String tableName, + UpdateTableConnectionFactory connectionFactory, + IManifest manifest, + PostDeleteHelper postDeleteHelper, + PostPutHelper postPutHelper, + CacheManager cacheManager, + DataProviderManager dataProviderManager, + IAccessManager accessManager) + { + this.tableName = tableName; + this.connectionFactory = connectionFactory; + this.postDeleteHelper = postDeleteHelper; + this.postPutHelper = postPutHelper; + this.manifest = manifest; + this.cacheManager = cacheManager; + this.dataProviderManager= dataProviderManager; + this.accessManager = accessManager; + } + + + /** + * Update table schema looks like this + * + * `ID` int(11) NOT NULL AUTO_INCREMENT, + * `tableName` varchar(255) DEFAULT NULL, + * `rowID` varchar(255) DEFAULT NULL, + * `type` enum('INSERT','UPDATE','DELETE') NOT NULL DEFAULT 'UPDATE', + * `lockID` int(11) DEFAULT NULL, + * `lockDate` datetime DEFAULT NULL, + * `timestamp` timestamp DEFAULT CURRENT_TIMESTAMP + * + * @return + */ + public ProcessorResult process(){ + ProcessorResult result = new ProcessorResult(); + + while(true) { + UpdateTableRow row = getRow(); + if(row == null) break; + + try { + if (processRow(row)) { + result.addResult(row.getType().toString()); + deleteRow(row); + } else { + result.addResult(row.getType().toString(), false, ""); + } + }catch(Exception e){ + logger.warn("Failed to process update: "+ e.getMessage(), e); + result.addResult(row.getType().toString(), false, e.getMessage()); + } + + } + + return result; + } + + private boolean processRow(UpdateTableRow row) throws Exception{ + boolean result = true; + + if(row.getRowId() != null) + switch (row.getType()){ + case DELETE: + result = processDeleteSingle(row); + break; + case UPDATE: + result = processUpdateSingle(row); + break; + case INSERT: + result = processInsertSingle(row); + break; + default: // Don't know how to process + result = true; + break; + } + else + switch (row.getType()){ + case DELETE: + result = processDeleteTable(row); + break; + case UPDATE: + result = processUpdateTable(row); + break; + case INSERT: + result = processInsertTable(row); + break; + default: // Don't know how to process + result = true; + break; + } + + return result; + } + + /** + * Process a single record update + * @param row + * @return + */ + @SuppressWarnings("rawtypes") + private boolean processUpdateSingle(UpdateTableRow row) throws Exception{ + Class clazz = getClassForTableName(row.getTableName()); + String className = clazz.getCanonicalName(); + + IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); + MappedClass mappedClass = mcm.getMappedClassByClassName(className); + IDataProvider dataProvider = dataProviderManager.getDataProviderByName(mappedClass.dataProviderName); + + ClassIDPair pair = new ClassIDPair(row.getRowId(), className); + handleUpdateClassIdPair(dataProvider, pair); + + // We need to update ALL referencing objects in the case that a + // relationship was updated. Since we don't have the OLD object, we have + // no way of telling what may have changed. + updateReferences(className); + return true; + } + + /** + * Process a whole table with updates + * @param row + * @return + */ + @SuppressWarnings("rawtypes") + private boolean processUpdateTable(UpdateTableRow row) throws Exception{ + Class clazz = getClassForTableName(row.getTableName()); + String className = clazz.getCanonicalName(); + + // If there are any clients that have asked for all objects in a class then we have to push everything + if(accessManager.getNumClientsInterestedInWholeClass(className) > 0) { + + IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); + MappedClass mappedClass = mcm.getMappedClassByClassName(className); + IDataProvider dataProvider = dataProviderManager.getDataProviderByName(mappedClass.dataProviderName); + + Integer pageNumber = 0; + Integer pageSize = 25; + Integer total = -1; + + while (total < 0 || pageNumber * pageSize <= total) { + PerceroList objectsToUpdate = dataProvider.getAllByName(className, pageNumber, pageSize, true, null); + pageNumber++; + total = objectsToUpdate.getTotalLength(); + if (total <= 0) { + break; + } + + Set objectIds = new HashSet(objectsToUpdate.size()); + Iterator itrObjectsToUpdate = objectsToUpdate.iterator(); + while (itrObjectsToUpdate.hasNext()) { + IPerceroObject nextObjectToUpdate = itrObjectsToUpdate.next(); + objectIds.add(nextObjectToUpdate.getID()); + } + + processUpdates(className, objectIds); + } + } + else { + processUpdates(className, accessManager.getClassAccessJournalIDs(className)); + } + + updateReferences(className); + + return true; + } + + /** + * Takes a class and list of ids that need to be pushed out + * @param className + * @param Ids + * @throws Exception + */ + private void processUpdates(String className, Collection Ids) throws Exception{ + IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); + MappedClass mappedClass = mcm.getMappedClassByClassName(className); + IDataProvider dataProvider = dataProviderManager.getDataProviderByName(mappedClass.dataProviderName); + + for(String ID : Ids) { + ClassIDPair pair = new ClassIDPair(ID, className); + handleUpdateClassIdPair(dataProvider, pair); + } + } + + private void handleUpdateClassIdPair(IDataProvider dataProvider, ClassIDPair pair) throws Exception { + IPerceroObject object = dataProvider.findById(pair, null, true); + + if (object != null) { + cacheManager.updateCachedObject(object, null); + postPutHelper.postPutObject(pair, null, null, true, null); + } + } + + /** + * process a single record insert + * @param row + * @return + */ + @SuppressWarnings("rawtypes") + private boolean processInsertSingle(UpdateTableRow row) throws Exception{ + Class clazz = getClassForTableName(row.getTableName()); + String className = clazz.getCanonicalName(); + + // We do not use PostCreateHelper here because we are going to do all + // that extra work for the whole class in updateReferences. + postPutHelper.postPutObject(new ClassIDPair(row.getRowId(), className), null, null, true, null); + + updateReferences(className); + return true; + } + + /** + * Process a whole table with inserts + * @param row + * @return + */ + @SuppressWarnings("rawtypes") + private boolean processInsertTable(UpdateTableRow row) throws Exception { + Class clazz = getClassForTableName(row.getTableName()); + String className = clazz.getCanonicalName(); + + // if any client needs all of this class then the only choice we have is to push everything + if(accessManager.getNumClientsInterestedInWholeClass(className) > 0 /* || true */){ + Set allClassIdPairs = getAllClassIdPairsForTable(row.getTableName()); + for(ClassIDPair classIdPair : allClassIdPairs) { + // We do not use PostCreateHelper here because we are going to + // do all that extra work for the whole class in + // updateReferences. + postPutHelper.postPutObject(classIdPair,null, null, true, null); + } + } + + updateReferences(className); + + return true; + } + + /** + * Process a single record delete + * @param row + * @return + */ + @SuppressWarnings("rawtypes") + private boolean processDeleteSingle(UpdateTableRow row) throws Exception{ + Class clazz = getClassForTableName(row.getTableName()); + String className = clazz.getCanonicalName(); + + // See if this object is in the cache. If so, it will help us know which related objects to update. + IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); + MappedClass mappedClass = mcm.getMappedClassByClassName(className); + IDataProvider dataProvider = dataProviderManager.getDataProviderByName(mappedClass.dataProviderName); + ClassIDPair pair = new ClassIDPair(row.getRowId(), className); + IPerceroObject cachedObject = dataProvider.findById(pair, null, false); // We are hoping to find this object in the cache... + + handleDeletedObject(cachedObject, clazz, className, row.getRowId()); + + updateReferences(className); + return true; + } + + /** + * process a whole table with deletes + * @param row + * @return + */ + @SuppressWarnings("rawtypes") + private boolean processDeleteTable(UpdateTableRow row) throws Exception{ + Class clazz = getClassForTableName(row.getTableName()); + String className = clazz.getCanonicalName(); + + IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); + MappedClass mappedClass = mcm.getMappedClassByClassName(className); + IDataProvider dataProvider = dataProviderManager.getDataProviderByName(mappedClass.dataProviderName); + + // Get the list of ALL ID's of this class type that have been accessed. + Set accessedIds = accessManager.getClassAccessJournalIDs(className); + + // TODO: If ID "0", then that means someone wants to know about ALL + // records of this type. How do we do this? + + // Get a list of ALL ID's of this class type. + Set allClassIdPairs = getAllClassIdPairsForTable(row.getTableName()); + + // Remove ALL existing/current ID's from our list of accessed ID's. + for(ClassIDPair nextClassIdPair : allClassIdPairs) { + accessedIds.remove(nextClassIdPair.getID()); + } + + // Now we have the list of ID's that have actually been deleted. + for(String id : accessedIds){ + // Find the cached object first so that it is NOT removed if the same object is NOT found in the data store. + IPerceroObject cachedObject = dataProvider.findById(new ClassIDPair(id, className), null, false); + // We will know an object has been deleted IFF it does NOT exist in the data store. + IPerceroObject dataStoreObject = dataProvider.findById(new ClassIDPair(id, className), null, true); + + if (dataStoreObject != null) { + // Object has NOT been deleted. + continue; + } + + handleDeletedObject(cachedObject, clazz, className, id); + } + + updateReferences(className); + + return true; + } + + @SuppressWarnings("rawtypes") + private void handleDeletedObject(IPerceroObject cachedObject, Class clazz, String className, String id) throws Exception { + boolean isShellObject = false; + if (cachedObject == null) { + cachedObject = (IPerceroObject) clazz.newInstance(); + cachedObject.setID(id); + isShellObject = true; + } + + cacheManager.handleDeletedObject(cachedObject, className, isShellObject); + + postDeleteHelper.postDeleteObject(new ClassIDPair(id, className), null, null, true); + } + + @SuppressWarnings("rawtypes") + private Set getAllClassIdPairsForTable(String tableName) throws Exception{ + Class clazz = getClassForTableName(tableName); + String className = clazz.getCanonicalName(); + + IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); + MappedClass mappedClass = mcm.getMappedClassByClassName(className); + IDataProvider dataProvider = dataProviderManager.getDataProviderByName(mappedClass.dataProviderName); + Set results = dataProvider.getAllClassIdPairsByName(className); + + return results; + } + + + /** + * Finds all back references to this class and pushes updates to all of them. + * @param className + */ + private void updateReferences(String className){ + IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); + MappedClass mappedClass = mcm.getMappedClassByClassName(className); + // Go through each mapped field and push all objects of that associated + // type (just in case any has a reference to a new row + // in the updated table) + // -- + // TODO: is this right? Is it enough to only check the relationships on + // this class or do we need to look + // through all of the mapped classes? + for(MappedFieldPerceroObject nextMappedField : mappedClass.externalizablePerceroObjectFields) { + try { + // Only care about it if it has a reverse relationship + MappedField mappedField = nextMappedField.getReverseMappedField(); + if (mappedField != null) { + // Find all of this type and push down an update to all + Set ids = accessManager.getClassAccessJournalIDs(mappedField.getMappedClass().className); + + if (ids.contains("0")) { + // If there is a 0 ID in the list, then we need to update ALL records of this type. + Integer pageNumber = 0; + Integer pageSize = 25; + Integer total = -1; + + while (total < 0 || pageNumber * pageSize <= total) { + PerceroList objectsToUpdate = mappedField.getMappedClass().getDataProvider().getAllByName(mappedField.getMappedClass().className, pageNumber, pageSize, true, null); + pageNumber++; + total = objectsToUpdate.getTotalLength(); + if (total <= 0) { + break; + } + + Iterator itrObjectsToUpdate = objectsToUpdate.iterator(); + while (itrObjectsToUpdate.hasNext()) { + IPerceroObject nextObjectToUpdate = itrObjectsToUpdate.next(); + ClassIDPair pair = BaseDataObject.toClassIdPair(nextObjectToUpdate); + Map> changedFields = new HashMap>(); + Collection changedMappedFields = new ArrayList(1); + changedMappedFields.add(mappedField); + changedFields.put(pair, changedMappedFields); + + // Remove from the cache. + cacheManager.deleteObjectFromCache(pair); + postPutHelper.postPutObject(pair, null, null, true, changedFields); + } + } + } + else { + Iterator itrIdsToUpdate = ids.iterator(); + while (itrIdsToUpdate.hasNext()) { + String nextIdToUpdate = itrIdsToUpdate.next(); + ClassIDPair pair = new ClassIDPair(nextIdToUpdate, mappedField.getMappedClass().className); + Map> changedFields = new HashMap>(); + Collection changedMappedFields = new ArrayList(1); + changedMappedFields.add(mappedField); + changedFields.put(pair, changedMappedFields); + + // Remove from the cache. + cacheManager.deleteObjectFromCache(pair); + postPutHelper.postPutObject(pair, null, null, true, changedFields); + } + } + } + } catch(Exception e) { + logger.error("Error in postCreateObject " + mappedClass.className + "." + nextMappedField.getField().getName(), e); + } + } + } + + /** + * Pulls a row off the update table and locks it so that other + * processors don't duplicate the work + * @return + */ + public UpdateTableRow getRow(){ + UpdateTableRow row = null; + + try(Connection conn = connectionFactory.getConnection(); + Statement statement = conn.createStatement()) + { + + Random rand = new Random(); + + int lockId = rand.nextInt(); + long now = System.currentTimeMillis(); + + DateTime expireThreshold = new DateTime(now - EXPIRATION_TIME); + + /** + * First try to lock a row + */ + String sql = "update `:tableName` set lockID=:lockId, lockDate=NOW() " + + "where lockID is null or " + + "lockDate < ':expireThreshold' " + + "order by timestamp limit 1"; + sql = sql.replace(":tableName", tableName); + sql = sql.replace(":lockId", lockId+""); + sql = sql.replace(":expireThreshold", expireThreshold.toString("Y-MM-dd HH:mm:ss")); + + int numUpdated = statement.executeUpdate(sql); + + // Found a row to process + if(numUpdated > 0){ + sql = "select * from :tableName where lockId=:lockId limit 1"; + sql = sql.replace(":tableName", tableName); + sql = sql.replace(":lockId", lockId+""); + + try(ResultSet rs = statement.executeQuery(sql)){ + // If got a row back + if(rs.next()) + row = UpdateTableRow.fromResultSet(rs); + else + logger.warn("Locked a row but couldn't retrieve"); + } + } + + } catch(SQLException e){ + logger.warn(e.getMessage(), e); + } + + return row; + } + + /** + * Deletes the row + * @param row + */ + private void deleteRow(UpdateTableRow row){ + try(Connection conn = connectionFactory.getConnection()){ + String sql = "delete from :tableName where ID=:ID"; + sql = sql.replace(":tableName", tableName); + sql = sql.replace(":ID", row.getID()+""); + Statement statement = conn.createStatement(); + int numUpdated = statement.executeUpdate(sql); + if(numUpdated != 1){ + logger.warn("Expected to delete 1, instead "+numUpdated); + } + }catch(SQLException e){ + logger.warn(e.getMessage(), e); + } + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public Class getClassForTableName(String tableName){ + Class result = null; + + // First look for the @Table annotation + for(Class c : manifest.getClassList()){ + Table table = (Table) c.getAnnotation(Table.class); + if(table != null && tableName.equals(table.name())) { + result = c; + break; + } + } + + // If we didn't find that now look for the simple class name to match + if(result == null){ + for(Class c : manifest.getClassList()){ + if(tableName.equals(c.getSimpleName())) { + result = c; + break; + } + } + } + + return result; + } +} diff --git a/src/main/java/com/percero/agents/sync/jobs/UpdateTableProcessorFactory.java b/src/main/java/com/percero/agents/sync/jobs/UpdateTableProcessorFactory.java new file mode 100644 index 0000000..0bfb0d0 --- /dev/null +++ b/src/main/java/com/percero/agents/sync/jobs/UpdateTableProcessorFactory.java @@ -0,0 +1,45 @@ +package com.percero.agents.sync.jobs; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.percero.agents.sync.access.IAccessManager; +import com.percero.agents.sync.cache.CacheManager; +import com.percero.agents.sync.helpers.PostDeleteHelper; +import com.percero.agents.sync.helpers.PostPutHelper; +import com.percero.agents.sync.services.DataProviderManager; +import com.percero.framework.bl.IManifest; + +/** + * Creates UpdateTableProcessor for the poller + * Created by jonnysamps on 9/4/15. + */ +@Component +public class UpdateTableProcessorFactory { + + @Autowired + UpdateTableConnectionFactory connectionFactory; + + @Autowired + IManifest manifest; + + @Autowired + PostDeleteHelper postDeleteHelper; + + @Autowired + PostPutHelper postPutHelper; + + @Autowired + CacheManager cacheManager; + + @Autowired + DataProviderManager dataProviderManager; + + @Autowired + IAccessManager accessManager; + + public UpdateTableProcessor getProcessor(String tableName){ + return new UpdateTableProcessor(tableName, connectionFactory, manifest, + postDeleteHelper, postPutHelper, cacheManager, dataProviderManager, accessManager); + } +} diff --git a/src/main/java/com/percero/agents/sync/jobs/UpdateTableRow.java b/src/main/java/com/percero/agents/sync/jobs/UpdateTableRow.java new file mode 100644 index 0000000..aa44712 --- /dev/null +++ b/src/main/java/com/percero/agents/sync/jobs/UpdateTableRow.java @@ -0,0 +1,66 @@ +package com.percero.agents.sync.jobs; + +import org.apache.log4j.Logger; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Date; + +/** + * Class to represent a row from the an update table + * Created by jonnysamps on 9/3/15. + */ +public class UpdateTableRow { + + private static Logger logger = Logger.getLogger(UpdateTableRow.class); + + public int ID; + public String tableName; + public String rowId; + public int lockId; + public Date lockDate; + public Date timestamp; + public UpdateTableRowType type; + + public Date getTimestamp() { + return timestamp; + } + + public int getID() { + return ID; + } + + public String getTableName() { + return tableName; + } + + public String getRowId() { + return rowId; + } + + public int getLockId() { + return lockId; + } + + public Date getLockDate() { + return lockDate; + } + + public UpdateTableRowType getType() { + return type; + } + + + public static UpdateTableRow fromResultSet(ResultSet resultSet) throws SQLException{ + UpdateTableRow row = new UpdateTableRow(); + row.ID = resultSet.getInt("ID"); + row.tableName = resultSet.getString("tableName"); + row.rowId = resultSet.getString("rowId"); + row.lockId = resultSet.getInt("lockId"); + row.lockDate = resultSet.getDate("lockDate"); + row.type = UpdateTableRowType.valueOf(resultSet.getString("type")); + row.timestamp = resultSet.getDate("timestamp"); + + return row; + } +} diff --git a/src/main/java/com/percero/agents/sync/jobs/UpdateTableRowType.java b/src/main/java/com/percero/agents/sync/jobs/UpdateTableRowType.java new file mode 100644 index 0000000..e2318a8 --- /dev/null +++ b/src/main/java/com/percero/agents/sync/jobs/UpdateTableRowType.java @@ -0,0 +1,8 @@ +package com.percero.agents.sync.jobs; + +/** + * Created by jonnysamps on 9/3/15. + */ +public enum UpdateTableRowType { + UPDATE, INSERT, DELETE +} diff --git a/src/main/java/com/percero/agents/sync/metadata/IMappedClassManager.java b/src/main/java/com/percero/agents/sync/metadata/IMappedClassManager.java index 55cfac1..f522792 100644 --- a/src/main/java/com/percero/agents/sync/metadata/IMappedClassManager.java +++ b/src/main/java/com/percero/agents/sync/metadata/IMappedClassManager.java @@ -1,7 +1,10 @@ package com.percero.agents.sync.metadata; +import java.util.Collection; + public interface IMappedClassManager { - public void addMappedClass(MappedClass theMappedClass); - public MappedClass getMappedClassByClassName(String aClassName); + void addMappedClass(MappedClass theMappedClass); + MappedClass getMappedClassByClassName(String aClassName); + Collection getAllMappedClasses(); } diff --git a/src/main/java/com/percero/agents/sync/metadata/JpqlQuery.java b/src/main/java/com/percero/agents/sync/metadata/JpqlQuery.java index a5ef179..90e94cd 100644 --- a/src/main/java/com/percero/agents/sync/metadata/JpqlQuery.java +++ b/src/main/java/com/percero/agents/sync/metadata/JpqlQuery.java @@ -1,7 +1,24 @@ package com.percero.agents.sync.metadata; +import java.util.Date; +import java.util.Iterator; +import java.util.Map; + +import org.apache.log4j.Logger; +import org.hibernate.Query; +import org.hibernate.engine.query.NamedParameterDescriptor; +import org.hibernate.engine.query.ParameterMetadata; +import org.hibernate.impl.SessionFactoryImpl; +import org.hibernate.impl.SessionImpl; +import org.hibernate.type.TimestampType; +import org.hibernate.type.Type; + public class JpqlQuery extends MappedQuery { + private static final Logger log = Logger.getLogger(JpqlQuery.class); + + private ParameterMetadata parameterMetadata = null; + @Override public void setQuery(String value) { int index = value.indexOf("jpql:"); @@ -9,4 +26,78 @@ public void setQuery(String value) { value = value.substring(index + 5); super.setQuery(value); } + + public Query createQuery(Object theObject, String userId, Object[] params, + SessionImpl s) throws Exception { + String queryString = getQuery(); + queryString = setQueryParameters(queryString, theObject, userId, params); + queryString = setParameterMetadata(queryString, theObject, userId, params, s); + Query theQuery = s.createQuery(queryString); + + return theQuery; + } + + private String setParameterMetadata(String theQuery, Object theObject, String userId, Object[] params, SessionImpl s) { + // Get Parameter MetaData. + if (params != null && params.length > 0) { + if (parameterMetadata == null) { + try { + parameterMetadata = ((SessionFactoryImpl)s.getSessionFactory()).getQueryPlanCache().getHQLQueryPlan(theQuery, false, (s).getEnabledFilters()).getParameterMetadata(); + } catch(Exception e) { + log.warn("Unable to get ParameterMetaData from Query:\n" + theQuery, e); + } + } + + for(Object nextParam : params) { + if (nextParam instanceof Map) { + Map nextMapParam = (Map) nextParam; + Iterator itr = nextMapParam.entrySet().iterator(); + while(itr.hasNext()) { + Boolean paramSet = false; + try { + Map.Entry pairs = (Map.Entry) itr.next(); + Object key = pairs.getKey(); + Object value = pairs.getValue(); + + if (key instanceof String) { + String strKey = (String) key; + if (parameterMetadata != null) { + NamedParameterDescriptor npd = parameterMetadata.getNamedParameterDescriptor(strKey); + if (npd != null) { + Type expectedType = npd.getExpectedType(); + + if (expectedType instanceof TimestampType) { + Date dateValue = new Date((Long)value); + theQuery = theQuery.replaceAll(":" + strKey, "'" + dateValue + "'"); + } else { + theQuery = theQuery.replaceAll(":" + (String) key, "\"" + value + "\""); + } + paramSet = true; + } + } + + // Last ditch effort to set this parameter. + if (!paramSet) + theQuery = theQuery.replaceAll(":" + (String) key, "\"" + value + "\""); + } + } catch(Exception e) { + log.warn("Unable to apply parameter to filter", e); + } + } + } else if (nextParam instanceof String) { + String nextStringParam = (String) nextParam; + try { + String[] paramSplit = nextStringParam.split(":"); + String key = paramSplit[0]; + String value = paramSplit[1]; + theQuery = theQuery.replaceAll(":" + key, "\"" + value + "\""); + } catch(Exception e) { + log.warn("Unable to apply parameter to filter", e); + } + } + } + } + + return theQuery; + } } diff --git a/src/main/java/com/percero/agents/sync/metadata/MappedClass.java b/src/main/java/com/percero/agents/sync/metadata/MappedClass.java index dfc3feb..c8c23c4 100644 --- a/src/main/java/com/percero/agents/sync/metadata/MappedClass.java +++ b/src/main/java/com/percero/agents/sync/metadata/MappedClass.java @@ -19,6 +19,7 @@ import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; +import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; @@ -46,6 +47,10 @@ import com.percero.agents.sync.metadata.annotations.PropertyInterfaces; import com.percero.agents.sync.metadata.annotations.RelationshipInterface; import com.percero.agents.sync.metadata.annotations.RelationshipInterfaces; +import com.percero.agents.sync.services.DataProviderManager; +import com.percero.agents.sync.services.IDataProvider; +import com.percero.agents.sync.vo.BaseDataObject; +import com.percero.agents.sync.vo.ClassIDPair; import com.percero.framework.bl.IManifest; import com.percero.framework.bl.ManifestHelper; import com.percero.framework.metadata.IMappedClass; @@ -160,12 +165,38 @@ else if (getReadAllQuery() != null) { public Boolean queriesInitialized = false; public Boolean relationshipsInitialized = false; // private Boolean isInitializing = false; + public String dataProviderName = ""; + public String getDataProviderName() { + return dataProviderName; + } + public void setDataProviderName(String dataProviderName) { + this.dataProviderName = dataProviderName; + + // Reset the DataProvider + if (dataProvider != null) { + dataProvider = null; + } + } + public String className = ""; public String tableName = ""; + public String tableSchema = ""; + public MappedField idMappedField = null; public List requiredFields = Collections.synchronizedList(new ArrayList()); + public Set toManyFields = Collections.synchronizedSet(new HashSet()); - public Set toOneFields = Collections.synchronizedSet(new HashSet()); + public Set toOneFields = Collections.synchronizedSet(new HashSet()); + + private Set sourceMappedFields = Collections.synchronizedSet(new HashSet()); + public Set getSourceMappedFields() { + return sourceMappedFields; + } + private Set targetMappedFields = Collections.synchronizedSet(new HashSet()); + public Set getTargetMappedFields() { + return targetMappedFields; + } + public Set propertyFields = Collections.synchronizedSet(new HashSet()); public List entityFields = Collections.synchronizedList(new ArrayList()); public List mapFields = Collections.synchronizedList(new ArrayList()); @@ -184,6 +215,15 @@ public List getChildMappedClasses() { } + IDataProvider dataProvider = null; + public IDataProvider getDataProvider() { + if (dataProvider == null) { + dataProvider = DataProviderManager.getInstance().getDataProviderByName(dataProviderName); + } + return dataProvider; + } + + /** * readAccessRightsFieldReferences holds all MappedFields that have some sort of * readAccessRights associated with that field. This means that this field needs @@ -195,7 +235,7 @@ public List getChildMappedClasses() { public Set getReadAccessRightsFieldReferences() { return readAccessRightsFieldReferences; } - public List uniqueConstraints = new ArrayList(); + public List> uniqueConstraints = new ArrayList>(); public List queries = new ArrayList(); public Boolean hasGeneratedId = false; public Boolean hasNonLazyLoadProperties = false; @@ -428,17 +468,36 @@ else if (implementsInterface(nextFieldClass, List.class)) OneToMany oneToMany = (OneToMany) theGetter.getAnnotation(OneToMany.class); if (oneToMany == null) oneToMany = (OneToMany) nextField.getAnnotation(OneToMany.class); - if (oneToMany != null) { toManyFields.add(nextMappedField); + getTargetMappedFields().add(nextMappedField); } + ManyToOne manyToOne = (ManyToOne) theGetter.getAnnotation(ManyToOne.class); + if (manyToOne == null) + manyToOne = (ManyToOne) nextField.getAnnotation(ManyToOne.class); + if (manyToOne != null) { + getSourceMappedFields().add((MappedFieldPerceroObject) nextMappedField); + } + + OneToOne oneToOne = (OneToOne) theGetter.getAnnotation(OneToOne.class); + if (oneToOne == null) + oneToOne = (OneToOne) nextField.getAnnotation(OneToOne.class); + if (oneToOne != null) { + if (StringUtils.hasText(oneToOne.mappedBy())) { + getTargetMappedFields().add(nextMappedField); + } + else { + getSourceMappedFields().add((MappedFieldPerceroObject) nextMappedField); + } + } + Boolean isPropertyField = true; Entity nextEntity = (Entity) nextField.getType().getAnnotation(Entity.class); if (nextEntity != null) { entityFields.add(nextField); - toOneFields.add(nextMappedField); + toOneFields.add( (MappedFieldPerceroObject) nextMappedField ); isPropertyField = false; } @@ -464,7 +523,11 @@ else if (implementsInterface(nextFieldClass, List.class)) if (id == null) id = (Id) nextField.getAnnotation(Id.class); if (id != null) { - uniqueConstraints.add(nextMappedField); + idMappedField = nextMappedField; + + List uniqueConstraintList = new ArrayList(1); + uniqueConstraintList.add(nextMappedField); + uniqueConstraints.add(uniqueConstraintList); // Check to see if this class has a Generated ID. GeneratedValue generatedValue = (GeneratedValue) nextField.getAnnotation(GeneratedValue.class); @@ -475,14 +538,26 @@ else if (implementsInterface(nextFieldClass, List.class)) if (column == null) column = (Column) nextField.getAnnotation(Column.class); if (column != null) { - if (column.unique()) - uniqueConstraints.add(nextMappedField); + if (column.unique()) { + List uniqueConstraintList = new ArrayList(1); + uniqueConstraintList.add(nextMappedField); + uniqueConstraints.add(uniqueConstraintList); + } if (column.name() != null && column.name().trim().length() > 0) nextMappedField.setColumnName(column.name()); else nextMappedField.setColumnName(nextField.getName()); } + + JoinColumn joinColumn = (JoinColumn) theGetter.getAnnotation(JoinColumn.class); + if (joinColumn == null) + joinColumn = (JoinColumn) nextField.getAnnotation(JoinColumn.class); + if (joinColumn != null) { + if (StringUtils.hasText(joinColumn.name())) { + nextMappedField.setJoinColumnName(joinColumn.name()); + } + } // Get NamedQueries for handling Access Rights. AccessRights accessRights = (AccessRights) nextField.getAnnotation(AccessRights.class); @@ -504,11 +579,18 @@ else if (implementsInterface(nextFieldClass, List.class)) nextMappedField.updateQuery.setQuery(nextAccessRight.query()); } else*/ if (nextAccessRight.type().equalsIgnoreCase("readQuery")) { - if (nextAccessRight.query().indexOf("jpql:") >= 0) + if (nextAccessRight.query().indexOf("jpql:") >= 0) { nextMappedField.setReadQuery(new JpqlQuery()); - else - nextMappedField.setReadQuery(new MappedQuery()); - nextMappedField.getReadQuery().setQuery(nextAccessRight.query()); + nextMappedField.getReadQuery().setQuery(nextAccessRight.query()); + } + else if (nextAccessRight.query().indexOf("sql:") >= 0) { + nextMappedField.setReadQuery(new SqlQuery(nextAccessRight.query().substring(nextAccessRight.query().indexOf("sql:")+4))); + } + else { + // Whatever type of Query this is, it is not supported. + continue; +// nextMappedField.setReadQuery(new MappedQuery()); + } nextMappedField.setHasReadAccessRights(true); readAccessRightsFieldReferences.add(nextMappedField); } /*else if (nextAccessRight.type().equalsIgnoreCase("deleteQuery")) { @@ -521,11 +603,18 @@ else if (implementsInterface(nextFieldClass, List.class)) // Add to queries list. IMappedQuery nextQuery = null; - if (nextAccessRight.query().indexOf("jpql:") >= 0) + if (nextAccessRight.query().indexOf("jpql:") >= 0) { nextQuery = new JpqlQuery(); - else - nextQuery = new MappedQuery(); - nextQuery.setQuery(nextAccessRight.query()); + nextQuery.setQuery(nextAccessRight.query()); + } + else if (nextAccessRight.query().indexOf("sql:") >= 0) { + nextQuery = new SqlQuery(nextAccessRight.query().substring(nextAccessRight.query().indexOf("sql:")+4)); + } + else { + // Unsupported Query type + continue; +// nextQuery = new MappedQuery(); + } nextQuery.setQueryName(nextAccessRight.type()); nextMappedField.queries.add(nextQuery); @@ -623,10 +712,17 @@ public void initializeQueries() { } } + if (!StringUtils.hasText(tableSchema)) { + Table table = (Table) tempClazz.getAnnotation(Table.class); + if (table != null && StringUtils.hasText(table.schema())) { + tableSchema = table.schema(); + } + } + if (!StringUtils.hasText(dataProviderName)) { DataProvider dataProvider = (DataProvider) tempClazz.getAnnotation(DataProvider.class); if (dataProvider != null) { - dataProviderName = dataProvider.name(); + setDataProviderName(dataProvider.name()); } } @@ -658,9 +754,9 @@ public void initializeQueries() { } else if (nextNamedQuery.name().equalsIgnoreCase("readQuery")) { readQuery = QueryFactory.createQuery(nextNamedQuery.query()); - Iterator itrToOneFields = toOneFields.iterator(); + Iterator itrToOneFields = toOneFields.iterator(); while(itrToOneFields.hasNext()) { - MappedField nextMappedField = itrToOneFields.next(); + MappedFieldPerceroObject nextMappedField = itrToOneFields.next(); MappedClass referencedMappedClass = mcm.getMappedClassByClassName(nextMappedField.getField().getType().getCanonicalName()); // Need to find the corresponding field. for(MappedField nextRefMappedField : referencedMappedClass.toManyFields) { @@ -754,6 +850,9 @@ public void initializeRelationships() { oneToMany = (OneToMany) nextMappedField.getField().getAnnotation(OneToMany.class); if (oneToMany != null) { +// // This must be a source MappedField +// sourceMappedFields.add((MappedFieldPerceroObject) nextMappedField); + //toManyFields.add(nextMappedField); ParameterizedType listType = (ParameterizedType) nextMappedField.getField().getGenericType(); Class listClass = (Class) listType.getActualTypeArguments()[0]; @@ -765,16 +864,34 @@ public void initializeRelationships() { nextMappedField.setHasReadAccessRights(true); readAccessRightsFieldReferences.add(nextMappedField); } + } ManyToOne manyToOne = (ManyToOne) nextMappedField.getGetter().getAnnotation(ManyToOne.class); if (manyToOne == null) manyToOne = (ManyToOne) nextMappedField.getField().getAnnotation(ManyToOne.class); +// if (manyToOne != null) { +// // This must be a target MappedField +// targetMappedFields.add(nextMappedField); +// } + OneToOne oneToOne = (OneToOne) nextMappedField.getGetter().getAnnotation(OneToOne.class); if (oneToOne == null) oneToOne = (OneToOne) nextMappedField.getField().getAnnotation(OneToOne.class); +// if (oneToOne != null) { +// // Not sure if this is source or target, let's find out... +// if(StringUtils.hasText(oneToOne.mappedBy())) { +// // This must be a target MappedField +// targetMappedFields.add(nextMappedField); +// } +// else { +// // This must be a source MappedField +// sourceMappedFields.add((MappedFieldPerceroObject) nextMappedField); +// } +// } + if (manyToOne != null && !manyToOne.optional() || oneToOne != null && !oneToOne.optional()) { @@ -1128,6 +1245,132 @@ else if ((this.className == null || this.className.trim().length() == 0) || (mcO } } + + + + /*************************************** + * Helper Methods + ***************************************/ + public Map getRelatedClassIdPairMappedFieldMap(IPerceroObject perceroObject, Boolean isShellObject) throws Exception { + Map results = new HashMap(); + + Iterator itrToOneFieldsToUpdate = toOneFields.iterator(); + while (itrToOneFieldsToUpdate.hasNext()) { + MappedFieldPerceroObject nextToOneField = itrToOneFieldsToUpdate.next(); + + // If no reverse mapped field, then nothing to do. + if (nextToOneField.getReverseMappedField() != null) { + + // If no PerceroObject, then we need to retrieve ALL objects of this type. + if (perceroObject == null) { + MappedClass reverseMappedClass = nextToOneField.getReverseMappedField().getMappedClass(); + IDataProvider reverseDataProvider = reverseMappedClass.getDataProvider(); + List relatedObjects = reverseDataProvider.getAllByName(reverseMappedClass.className, null, null, false, null); + Iterator itrRelatedObjects = relatedObjects.iterator(); + while (itrRelatedObjects.hasNext()) { + IPerceroObject nextRelatedObject = itrRelatedObjects.next(); + results.put(BaseDataObject.toClassIdPair(nextRelatedObject), nextToOneField.getReverseMappedField()); + } + } + else if (isShellObject) { + // If this is a Shell Object, then we need to ask the IDataProvider to get the related objects. + List relatedObjects = getDataProvider().findAllRelatedObjects(perceroObject, nextToOneField, true, null); + Iterator itrRelatedObjects = relatedObjects.iterator(); + while (itrRelatedObjects.hasNext()) { + IPerceroObject nextRelatedObject = itrRelatedObjects.next(); + results.put(BaseDataObject.toClassIdPair(nextRelatedObject), nextToOneField.getReverseMappedField()); + } + } + else { + IPerceroObject toOneObject = (IPerceroObject) nextToOneField.getGetter().invoke(perceroObject); + if (toOneObject != null) { + results.put(BaseDataObject.toClassIdPair(toOneObject), nextToOneField.getReverseMappedField()); + } + } + } + } + + Iterator itrToManyFieldsToUpdate = toManyFields.iterator(); + while (itrToManyFieldsToUpdate.hasNext()) { + MappedField nextToManyField = itrToManyFieldsToUpdate.next(); + if (nextToManyField instanceof MappedFieldPerceroObject) { + MappedFieldPerceroObject nextPerceroObjectField = (MappedFieldPerceroObject) nextToManyField; + + if (nextPerceroObjectField.getReverseMappedField() != null) { + + // If no PerceroObject, then we need to retrieve ALL objects of this type. + if (perceroObject == null) { + MappedClass reverseMappedClass = nextToManyField.getReverseMappedField().getMappedClass(); + IDataProvider reverseDataProvider = reverseMappedClass.getDataProvider(); + List relatedObjects = reverseDataProvider.getAllByName(reverseMappedClass.className, null, null, false, null); + Iterator itrRelatedObjects = relatedObjects.iterator(); + while (itrRelatedObjects.hasNext()) { + IPerceroObject nextRelatedObject = itrRelatedObjects.next(); + results.put(BaseDataObject.toClassIdPair(nextRelatedObject), nextToManyField.getReverseMappedField()); + } + } + else if (isShellObject) { + // If this is a Shell Object, then we need to ask the IDataProvider to get the related objects. + List relatedObjects = getDataProvider().findAllRelatedObjects(perceroObject, nextToManyField, true, null); + Iterator itrRelatedObjects = relatedObjects.iterator(); + while (itrRelatedObjects.hasNext()) { + IPerceroObject nextRelatedObject = itrRelatedObjects.next(); + results.put(BaseDataObject.toClassIdPair(nextRelatedObject), nextToManyField.getReverseMappedField()); + } + } + else { + IPerceroObject toOneObject = (IPerceroObject) nextPerceroObjectField.getGetter().invoke(perceroObject); + if (toOneObject != null) { + results.put(BaseDataObject.toClassIdPair(toOneObject), nextPerceroObjectField.getReverseMappedField()); + } + } + } + } + else if (nextToManyField instanceof MappedFieldList) { + MappedFieldList nextListField = (MappedFieldList) nextToManyField; + + if (nextListField.getReverseMappedField() != null) { + + // If no PerceroObject, then we need to retrieve ALL objects of this type. + if (perceroObject == null) { + MappedClass reverseMappedClass = nextToManyField.getReverseMappedField().getMappedClass(); + IDataProvider reverseDataProvider = reverseMappedClass.getDataProvider(); + List relatedObjects = reverseDataProvider.getAllByName(reverseMappedClass.className, null, null, false, null); + Iterator itrRelatedObjects = relatedObjects.iterator(); + while (itrRelatedObjects.hasNext()) { + IPerceroObject nextRelatedObject = itrRelatedObjects.next(); + results.put(BaseDataObject.toClassIdPair(nextRelatedObject), nextToManyField.getReverseMappedField()); + } + } + else if (isShellObject) { + // If this is a Shell Object, then we need to ask the IDataProvider to get the related objects. + List relatedObjects = getDataProvider().findAllRelatedObjects(perceroObject, nextToManyField, true, null); + Iterator itrRelatedObjects = relatedObjects.iterator(); + while (itrRelatedObjects.hasNext()) { + IPerceroObject nextRelatedObject = itrRelatedObjects.next(); + results.put(BaseDataObject.toClassIdPair(nextRelatedObject), nextToManyField.getReverseMappedField()); + } + } + else { + List listObjects = (List) nextListField.getGetter().invoke(perceroObject); + if (listObjects != null && !listObjects.isEmpty()) { + Iterator itrListObjects = listObjects.iterator(); + while (itrListObjects.hasNext()) { + IPerceroObject nextListObject = itrListObjects.next(); + results.put(BaseDataObject.toClassIdPair(nextListObject), nextListField.getReverseMappedField()); + } + } + } + } + } + } + + return results; + } + + + + /*************************************** * Static Helper Methods ***************************************/ @@ -1181,4 +1424,6 @@ public MappedClassMethodPair(MappedClass mappedClass, Method method) { } } + + } diff --git a/src/main/java/com/percero/agents/sync/metadata/MappedClassManager.java b/src/main/java/com/percero/agents/sync/metadata/MappedClassManager.java index 9a46c6d..95598a0 100644 --- a/src/main/java/com/percero/agents/sync/metadata/MappedClassManager.java +++ b/src/main/java/com/percero/agents/sync/metadata/MappedClassManager.java @@ -1,5 +1,6 @@ package com.percero.agents.sync.metadata; +import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Random; @@ -24,4 +25,9 @@ public MappedClass getMappedClassByClassName(String aClassName) { return mc; } + @Override + public Collection getAllMappedClasses() { + return mappedClassesByName.values(); + } + } diff --git a/src/main/java/com/percero/agents/sync/metadata/MappedField.java b/src/main/java/com/percero/agents/sync/metadata/MappedField.java index 7de3f1a..be8e245 100644 --- a/src/main/java/com/percero/agents/sync/metadata/MappedField.java +++ b/src/main/java/com/percero/agents/sync/metadata/MappedField.java @@ -24,6 +24,7 @@ public class MappedField { private Method getter; private Method setter; private String columnName; + private String joinColumnName; private Boolean useLazyLoading = true; public List queries = new ArrayList(); private Boolean hasReadAccessRights = false; @@ -91,6 +92,14 @@ public void setColumnName(String columnName) { this.columnName = columnName; } + public String getJoinColumnName() { + return joinColumnName; + } + + public void setJoinColumnName(String joinColumnName) { + this.joinColumnName = joinColumnName; + } + public Boolean getUseLazyLoading() { return true; // TODO: Lazy Loading is always used. Need better method to NOT use lazy-loading. @@ -161,6 +170,11 @@ public Object getValue(Object anObject) throws IllegalArgumentException, return result; } + public Boolean isValueSetForQuery(Object anObject) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { + Object value = getValue(anObject); + return (value != null); + } + public Boolean compareObjects(Object objectA, Object objectB) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { Object valueA = getValue(objectA); diff --git a/src/main/java/com/percero/agents/sync/metadata/MappedFieldBool.java b/src/main/java/com/percero/agents/sync/metadata/MappedFieldBool.java index 301a5e2..7cf5b8b 100644 --- a/src/main/java/com/percero/agents/sync/metadata/MappedFieldBool.java +++ b/src/main/java/com/percero/agents/sync/metadata/MappedFieldBool.java @@ -22,6 +22,12 @@ public void writeExternalField(ObjectOutput output, Object anObject) output.writeBoolean(booleanValue); } + @Override + public Boolean isValueSetForQuery(Object anObject) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { + Boolean value = (Boolean) getValue(anObject); + return (value != null); + } + public Boolean compareObjects(Object objectA, Object objectB) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { boolean valueA = (Boolean) getValue(objectA); diff --git a/src/main/java/com/percero/agents/sync/metadata/MappedFieldBoolean.java b/src/main/java/com/percero/agents/sync/metadata/MappedFieldBoolean.java index 6478fc4..297321c 100644 --- a/src/main/java/com/percero/agents/sync/metadata/MappedFieldBoolean.java +++ b/src/main/java/com/percero/agents/sync/metadata/MappedFieldBoolean.java @@ -22,6 +22,12 @@ public void writeExternalField(ObjectOutput output, Object anObject) output.writeBoolean(booleanValue); } + @Override + public Boolean isValueSetForQuery(Object anObject) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { + Boolean value = (Boolean) getValue(anObject); + return (value != null); + } + public Boolean compareObjects(Object objectA, Object objectB) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { Boolean valueA = (Boolean) getValue(objectA); diff --git a/src/main/java/com/percero/agents/sync/metadata/MappedFieldDate.java b/src/main/java/com/percero/agents/sync/metadata/MappedFieldDate.java index ccbdbb2..0b204cd 100644 --- a/src/main/java/com/percero/agents/sync/metadata/MappedFieldDate.java +++ b/src/main/java/com/percero/agents/sync/metadata/MappedFieldDate.java @@ -21,6 +21,12 @@ public void writeJsonField(JsonObject jsonObject, Object anObject) jsonObject.addProperty(getField().getName(), gson.toJson(timestamp)); } + @Override + public Boolean isValueSetForQuery(Object anObject) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { + Date value = (Date) getValue(anObject); + return (value != null && value.getTime() > 0); + } + public Boolean compareObjects(Object objectA, Object objectB) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { Date valueA = (Date) getValue(objectA); diff --git a/src/main/java/com/percero/agents/sync/metadata/MappedFieldDouble.java b/src/main/java/com/percero/agents/sync/metadata/MappedFieldDouble.java index edf1238..ee1ee8c 100644 --- a/src/main/java/com/percero/agents/sync/metadata/MappedFieldDouble.java +++ b/src/main/java/com/percero/agents/sync/metadata/MappedFieldDouble.java @@ -21,6 +21,12 @@ public void writeExternalField(ObjectOutput output, Object anObject) double doubleValue = ((Double) getGetter().invoke(anObject)).doubleValue(); output.writeDouble(doubleValue); } + + @Override + public Boolean isValueSetForQuery(Object anObject) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { + Double value = (Double) getValue(anObject); + return (value != null && value.doubleValue() != 0.0); + } public Boolean compareObjects(Object objectA, Object objectB) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { diff --git a/src/main/java/com/percero/agents/sync/metadata/MappedFieldFloat.java b/src/main/java/com/percero/agents/sync/metadata/MappedFieldFloat.java index 6ae626a..07df975 100644 --- a/src/main/java/com/percero/agents/sync/metadata/MappedFieldFloat.java +++ b/src/main/java/com/percero/agents/sync/metadata/MappedFieldFloat.java @@ -22,6 +22,12 @@ public void writeExternalField(ObjectOutput output, Object anObject) output.writeFloat(floatValue); } + @Override + public Boolean isValueSetForQuery(Object anObject) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { + Float value = (Float) getValue(anObject); + return (value != null && value.floatValue() != 0.0); + } + public Boolean compareObjects(Object objectA, Object objectB) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { Float valueA = (Float) getValue(objectA); diff --git a/src/main/java/com/percero/agents/sync/metadata/MappedFieldInt.java b/src/main/java/com/percero/agents/sync/metadata/MappedFieldInt.java index ee2e4c7..e6406f8 100644 --- a/src/main/java/com/percero/agents/sync/metadata/MappedFieldInt.java +++ b/src/main/java/com/percero/agents/sync/metadata/MappedFieldInt.java @@ -34,6 +34,12 @@ public void writeExternalField(ObjectOutput output, Object anObject) output.writeInt(intValue); } + @Override + public Boolean isValueSetForQuery(Object anObject) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { + Integer value = (Integer) getValue(anObject); + return (value != null && value.intValue() != 0); + } + public Boolean compareObjects(Object objectA, Object objectB) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { Integer valueA = (Integer) getValue(objectA); diff --git a/src/main/java/com/percero/agents/sync/metadata/MappedFieldInteger.java b/src/main/java/com/percero/agents/sync/metadata/MappedFieldInteger.java index 4df1320..e80d83d 100644 --- a/src/main/java/com/percero/agents/sync/metadata/MappedFieldInteger.java +++ b/src/main/java/com/percero/agents/sync/metadata/MappedFieldInteger.java @@ -38,6 +38,12 @@ public void writeExternalField(ObjectOutput output, Object anObject) output.writeInt(intValue); } + @Override + public Boolean isValueSetForQuery(Object anObject) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { + Integer value = (Integer) getValue(anObject); + return (value != null && value.intValue() != 0); + } + public Boolean compareObjects(Object objectA, Object objectB) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { Integer valueA = (Integer) getValue(objectA); diff --git a/src/main/java/com/percero/agents/sync/metadata/MappedFieldList.java b/src/main/java/com/percero/agents/sync/metadata/MappedFieldList.java index caf33cd..27d1587 100644 --- a/src/main/java/com/percero/agents/sync/metadata/MappedFieldList.java +++ b/src/main/java/com/percero/agents/sync/metadata/MappedFieldList.java @@ -44,6 +44,13 @@ public void writeExternalField(ObjectOutput output, Object anObject) output.writeObject(objectArray); } + @SuppressWarnings("rawtypes") + @Override + public Boolean isValueSetForQuery(Object anObject) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { + List value = (List) getValue(anObject); + return (value != null && !value.isEmpty()); + } + public Boolean compareObjects(Object objectA, Object objectB) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { List valueA = (List) getValue(objectA); diff --git a/src/main/java/com/percero/agents/sync/metadata/MappedFieldMap.java b/src/main/java/com/percero/agents/sync/metadata/MappedFieldMap.java index f94c3e6..bec6223 100644 --- a/src/main/java/com/percero/agents/sync/metadata/MappedFieldMap.java +++ b/src/main/java/com/percero/agents/sync/metadata/MappedFieldMap.java @@ -27,4 +27,12 @@ public void writeExternalField(ObjectOutput output, Object anObject) Object[] objectArray = DataExternalizer.getClassIDPairArray(mapObject.values()); output.writeObject(objectArray); } + + @SuppressWarnings("rawtypes") + @Override + public Boolean isValueSetForQuery(Object anObject) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { + Map value = (Map) getValue(anObject); + return (value != null && !value.isEmpty()); + } + } diff --git a/src/main/java/com/percero/agents/sync/metadata/MappedFieldPerceroObject.java b/src/main/java/com/percero/agents/sync/metadata/MappedFieldPerceroObject.java index dbe8a74..009aac9 100644 --- a/src/main/java/com/percero/agents/sync/metadata/MappedFieldPerceroObject.java +++ b/src/main/java/com/percero/agents/sync/metadata/MappedFieldPerceroObject.java @@ -17,6 +17,21 @@ public class MappedFieldPerceroObject extends MappedField { private static final Logger log = Logger.getLogger(MappedFieldPerceroObject.class); + private Boolean targetEntity = null; + public Boolean isTargetEntity() { + if (targetEntity == null) { + targetEntity = this.getMappedClass().getTargetMappedFields().contains(this); + } + return targetEntity; + } + + private Boolean sourceEntity = null; + public Boolean isSourceEntity() { + if (sourceEntity == null) { + sourceEntity = this.getMappedClass().getSourceMappedFields().contains(this); + } + return sourceEntity; + } @Override public void writeJsonField(JsonObject jsonObject, Object anObject) @@ -68,10 +83,21 @@ public void writeExternalField(ObjectOutput output, Object anObject) output.writeObject(classIdPair); } + public IPerceroObject getPerceroObjectValue(Object anObject) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { + IPerceroObject result = (IPerceroObject) getValue(anObject); + return result; + } + + @Override + public Boolean isValueSetForQuery(Object anObject) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { + IPerceroObject value = getPerceroObjectValue(anObject); + return (value != null && StringUtils.hasText(value.getID())); + } + public Boolean compareObjects(Object objectA, Object objectB) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { - IPerceroObject valueA = (IPerceroObject) getValue(objectA); - IPerceroObject valueB = (IPerceroObject) getValue(objectB); + IPerceroObject valueA = getPerceroObjectValue(objectA); + IPerceroObject valueB = getPerceroObjectValue(objectB); if (valueA == null && valueB == null) return true; diff --git a/src/main/java/com/percero/agents/sync/metadata/MappedFieldString.java b/src/main/java/com/percero/agents/sync/metadata/MappedFieldString.java index beefa4f..31370af 100644 --- a/src/main/java/com/percero/agents/sync/metadata/MappedFieldString.java +++ b/src/main/java/com/percero/agents/sync/metadata/MappedFieldString.java @@ -5,6 +5,8 @@ import java.io.ObjectOutput; import java.lang.reflect.InvocationTargetException; +import org.springframework.util.StringUtils; + import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -37,6 +39,12 @@ public void writeExternalField(ObjectOutput output, Object anObject) output.writeUTF(stringValue); } + @Override + public Boolean isValueSetForQuery(Object anObject) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { + String value = (String) getValue(anObject); + return StringUtils.hasText(value); + } + public Boolean compareObjects(Object objectA, Object objectB) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { String valueA = (String) getValue(objectA); diff --git a/src/main/java/com/percero/agents/sync/metadata/MappedQuery.java b/src/main/java/com/percero/agents/sync/metadata/MappedQuery.java index a303d51..7b990bd 100644 --- a/src/main/java/com/percero/agents/sync/metadata/MappedQuery.java +++ b/src/main/java/com/percero/agents/sync/metadata/MappedQuery.java @@ -1,25 +1,15 @@ package com.percero.agents.sync.metadata; import java.util.ArrayList; -import java.util.Date; +import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.log4j.Logger; -import org.hibernate.Query; -import org.hibernate.SessionFactory; -import org.hibernate.engine.query.NamedParameterDescriptor; import org.hibernate.engine.query.ParamLocationRecognizer; -import org.hibernate.engine.query.ParameterMetadata; -import org.hibernate.impl.QueryImpl; -import org.hibernate.impl.SessionFactoryImpl; -import org.hibernate.impl.SessionImpl; -import org.hibernate.type.TimestampType; -import org.hibernate.type.Type; import org.springframework.util.StringUtils; -import com.percero.agents.sync.services.SyncAgentService; import com.percero.framework.metadata.IMappedQuery; import com.percero.framework.vo.IPerceroObject; @@ -47,10 +37,10 @@ public void setQueryName(String value) { private boolean queryParameterNamesSet = false; private boolean useUserId = false; private boolean useId = false; - private ParameterMetadata parameterMetadata = null; + private boolean useIds = false; @SuppressWarnings("rawtypes") - public boolean getUseId() { + protected void initialize() { if (!queryParameterNamesSet && StringUtils.hasText(query)) { try { @@ -66,6 +56,8 @@ public boolean getUseId() { useUserId = true; else if (nextNamedParameter.equalsIgnoreCase("id")) useId = true; + else if (nextNamedParameter.equalsIgnoreCase("ids")) + useIds = true; } queryParameterNamesSet = true; @@ -73,38 +65,42 @@ else if (nextNamedParameter.equalsIgnoreCase("id")) // Do nothing. } } - + } + + public boolean getUseId() { + initialize(); return this.useId; } - public void setQueryParameters(Query theQuery, Object theObject, String userId) throws Exception { - setQueryParameters(theQuery, theObject, userId, null, null); + public boolean getUseIds() { + initialize(); + return this.useIds; + } + + public String setQueryParameters(String theQuery, Object theObject, String userId) throws Exception { + return setQueryParameters(theQuery, theObject, userId, null); } @SuppressWarnings("rawtypes") - public void setQueryParameters(Query theQuery, Object theObject, String userId, Object[] params, SessionImpl s) throws Exception { - if (!queryParameterNamesSet) - { - for(String nextNamedParameter : theQuery.getNamedParameters()) { - if (nextNamedParameter.equalsIgnoreCase("userId")) - useUserId = true; - else if (nextNamedParameter.equalsIgnoreCase("id")) - useId = true; - } + public String setQueryParameters(String theQuery, Object theObject, String userId, Object[] params) throws Exception { + if (!queryParameterNamesSet) { + useUserId = theQuery.contains(":userId"); + useId = theQuery.contains(":id"); + useIds = theQuery.contains(":ids"); queryParameterNamesSet = true; } if (useId) { if (theObject instanceof String) { try { - theQuery.setString("id", (String)theObject); + theQuery = theQuery.replaceAll(":id", "\"" + (String)theObject + "\""); } catch(Exception e) { log.warn("Unable to set ID for MappedQuery from String"); } } else if (theObject instanceof IPerceroObject) { try { - theQuery.setString("id", ((IPerceroObject)theObject).getID()); + theQuery = theQuery.replaceAll(":id", "\"" + ((IPerceroObject)theObject).getID() + "\""); } catch(Exception e) { log.warn("Unable to set ID for MappedQuery from IPerceroObject"); } @@ -117,7 +113,7 @@ else if (theObject instanceof Object[]) String[] nextParamSplit = ((String) nextParam).split(":"); if (nextParamSplit.length == 2) { if (nextParamSplit[0].equalsIgnoreCase("id")) { - theQuery.setString("id", nextParamSplit[1]); + theQuery = theQuery.replaceAll(":id", "\"" + nextParamSplit[1] + "\""); break; } } @@ -127,7 +123,7 @@ else if (theObject instanceof Object[]) } else if (nextParam instanceof IPerceroObject) { try { - theQuery.setString("id", ((IPerceroObject)nextParam).getID()); + theQuery = theQuery.replaceAll(":id", "\"" + ((IPerceroObject)nextParam).getID() + "\""); } catch(Exception e) { log.warn("Unable to set ID for MappedQuery from IPerceroObject"); } @@ -135,72 +131,114 @@ else if (nextParam instanceof IPerceroObject) { } } } - if (useUserId) { - try { - theQuery.setString("userId", userId); - } catch(Exception e) { - log.warn("Unable to set UserID for MappedQuery"); + if (useIds) { + if (theObject instanceof String) { + try { + theQuery = theQuery.replaceAll(":ids", "'" + (String)theObject + "'"); + } catch(Exception e) { + log.warn("Unable to set IDs for MappedQuery from String"); + } } - } - - // Get Parameter MetaData. - if (params != null && params.length > 0) { - if (parameterMetadata == null) { + else if (theObject instanceof IPerceroObject) { try { - parameterMetadata = ((SessionFactoryImpl)s.getSessionFactory()).getQueryPlanCache().getHQLQueryPlan(theQuery.getQueryString(), false, (s).getEnabledFilters()).getParameterMetadata(); + theQuery = theQuery.replaceAll(":ids", "'" + ((IPerceroObject)theObject).getID() + "'"); } catch(Exception e) { - log.warn("Unable to get ParameterMetaData from Query:\n" + theQuery.getQueryString(), e); + log.warn("Unable to set IDs for MappedQuery from IPerceroObject"); } } - - for(Object nextParam : params) { - if (nextParam instanceof Map) { - Map nextMapParam = (Map) nextParam; - Iterator itr = nextMapParam.entrySet().iterator(); - while(itr.hasNext()) { - Boolean paramSet = false; + else if (theObject instanceof Object[]) { + Object[] theObjectArray = (Object[]) theObject; + + String idsListString = null; + + for(Object nextParam : theObjectArray) { + if (nextParam instanceof String) { try { - Map.Entry pairs = (Map.Entry) itr.next(); - Object key = pairs.getKey(); - Object value = pairs.getValue(); - - if (key instanceof String) { - String strKey = (String) key; - if (parameterMetadata != null) { - NamedParameterDescriptor npd = parameterMetadata.getNamedParameterDescriptor(strKey); - if (npd != null) { - Type expectedType = npd.getExpectedType(); - - if (expectedType instanceof TimestampType) { - Date dateValue = new Date((Long)value); - theQuery.setDate(strKey, dateValue); - } else { - theQuery.setParameter((String) key, value); - } - paramSet = true; + String[] nextParamSplit = ((String) nextParam).split(":"); + if (nextParamSplit.length == 2) { + if (nextParamSplit[0].equalsIgnoreCase("id")) { + if (idsListString != null) { + idsListString += ","; } + else { + idsListString = ""; + } + idsListString += nextParamSplit[1]; + break; + } + } + } catch(Exception e) { + log.warn("Unable to set IDs for MappedQuery from String"); + } + } + else if (nextParam instanceof IPerceroObject) { + try { + if (idsListString != null) { + idsListString += ","; + } + else { + idsListString = ""; + } + idsListString += ((IPerceroObject)nextParam).getID(); + } catch(Exception e) { + log.warn("Unable to set IDs for MappedQuery from IPerceroObject"); + } + } + } + + theQuery = theQuery.replaceAll(":ids", idsListString); + } + else if (theObject instanceof Collection) { + Collection theObjectCollection = (Collection) theObject; + + String idsListString = null; + + for(Object nextParam : theObjectCollection) { + if (nextParam instanceof String) { + try { + String[] nextParamSplit = ((String) nextParam).split(":"); + if (nextParamSplit.length == 2) { + if (nextParamSplit[0].equalsIgnoreCase("id")) { + if (idsListString != null) { + idsListString += ","; + } + else { + idsListString = ""; + } + idsListString += nextParamSplit[1]; + break; } - - // Last ditch effort to set this parameter. - if (!paramSet) - theQuery.setParameter((String) key, value); } } catch(Exception e) { - log.warn("Unable to apply parameter to filter", e); + log.warn("Unable to set IDs for MappedQuery from String"); } } - } else if (nextParam instanceof String) { - String nextStringParam = (String) nextParam; - try { - String[] paramSplit = nextStringParam.split(":"); - String key = paramSplit[0]; - String value = paramSplit[1]; - theQuery.setParameter(key, value); - } catch(Exception e) { - log.warn("Unable to apply parameter to filter", e); + else if (nextParam instanceof IPerceroObject) { + try { + if (idsListString != null) { + idsListString += ","; + } + else { + idsListString = ""; + } + idsListString += ((IPerceroObject)nextParam).getID(); + } catch(Exception e) { + log.warn("Unable to set IDs for MappedQuery from IPerceroObject"); + } } } + + theQuery = theQuery.replaceAll(":ids", idsListString); + } + } + if (useUserId) { + try { + theQuery = theQuery.replaceAll(":userId", "\"" + userId + "\""); + } catch(Exception e) { + log.warn("Unable to set UserID for MappedQuery"); } } + + return theQuery; } } diff --git a/src/main/java/com/percero/agents/sync/metadata/QueryFactory.java b/src/main/java/com/percero/agents/sync/metadata/QueryFactory.java index 87a64e5..2a5afeb 100644 --- a/src/main/java/com/percero/agents/sync/metadata/QueryFactory.java +++ b/src/main/java/com/percero/agents/sync/metadata/QueryFactory.java @@ -5,12 +5,18 @@ public class QueryFactory { public static IMappedQuery createQuery(String query){ IMappedQuery result = null; - if (query.indexOf("jpql:") >= 0) + if (query.indexOf("jpql:") >= 0) { result = new JpqlQuery(); - else - result = new MappedQuery(); + result.setQuery(query); + } + else if (query.indexOf("sql:") >= 0) { + result = new SqlQuery(query.substring(query.indexOf("sql:")+4)); + } + else { + // Unsupported Query type + return null; + } - result.setQuery(query); return result; } } diff --git a/src/main/java/com/percero/agents/sync/metadata/SqlQuery.java b/src/main/java/com/percero/agents/sync/metadata/SqlQuery.java index 463991b..f176a76 100644 --- a/src/main/java/com/percero/agents/sync/metadata/SqlQuery.java +++ b/src/main/java/com/percero/agents/sync/metadata/SqlQuery.java @@ -1,8 +1,94 @@ package com.percero.agents.sync.metadata; +import org.apache.log4j.Logger; + public class SqlQuery extends MappedQuery { + private static final Logger log = Logger.getLogger(SqlQuery.class); + public SqlQuery(String query){ + super(); super.setQuery(query); } + + public SqlQuery(){ + super(); + } + +// ParameterMetadata parameterMetadata = null; + +// public Query createQuery(Object theObject, String userId, Object[] params, +// SessionImpl s) throws Exception { +// String queryString = getQuery(); +// queryString = setQueryParameters(queryString, theObject, userId, params); +// queryString = setParameterMetadata(queryString, theObject, userId, params, s); +// Query theQuery = s.createQuery(queryString); +// +// return theQuery; +// } + +// private String setParameterMetadata(String theQuery, Object theObject, String userId, Object[] params, SessionImpl s) { +// // Get Parameter MetaData. +// if (params != null && params.length > 0) { +// if (parameterMetadata == null) { +// try { +// NativeSQLQuerySpecification querySpec = null; +// parameterMetadata = ((SessionFactoryImpl)s.getSessionFactory()).getQueryPlanCache().getNativeSQLQueryPlan(querySpec).getParameterMetadata(); +// } catch(Exception e) { +// log.warn("Unable to get ParameterMetaData from Query:\n" + theQuery, e); +// } +// } +// +// for(Object nextParam : params) { +// if (nextParam instanceof Map) { +// Map nextMapParam = (Map) nextParam; +// Iterator itr = nextMapParam.entrySet().iterator(); +// while(itr.hasNext()) { +// Boolean paramSet = false; +// try { +// Map.Entry pairs = (Map.Entry) itr.next(); +// Object key = pairs.getKey(); +// Object value = pairs.getValue(); +// +// if (key instanceof String) { +// String strKey = (String) key; +// if (parameterMetadata != null) { +// NamedParameterDescriptor npd = parameterMetadata.getNamedParameterDescriptor(strKey); +// if (npd != null) { +// Type expectedType = npd.getExpectedType(); +// +// if (expectedType instanceof TimestampType) { +// Date dateValue = new Date((Long)value); +// theQuery = theQuery.replaceAll(":" + strKey, "'" + dateValue + "'"); +// } else { +// theQuery = theQuery.replaceAll(":" + (String) key, "\"" + value + "\""); +// } +// paramSet = true; +// } +// } +// +// // Last ditch effort to set this parameter. +// if (!paramSet) +// theQuery = theQuery.replaceAll(":" + (String) key, "\"" + value + "\""); +// } +// } catch(Exception e) { +// log.warn("Unable to apply parameter to filter", e); +// } +// } +// } else if (nextParam instanceof String) { +// String nextStringParam = (String) nextParam; +// try { +// String[] paramSplit = nextStringParam.split(":"); +// String key = paramSplit[0]; +// String value = paramSplit[1]; +// theQuery = theQuery.replaceAll(":" + key, "\"" + value + "\""); +// } catch(Exception e) { +// log.warn("Unable to apply parameter to filter", e); +// } +// } +// } +// } +// +// return theQuery; +// } } diff --git a/src/main/java/com/percero/agents/sync/services/AccessorService.java b/src/main/java/com/percero/agents/sync/services/AccessorService.java index 0f8a3df..80c94ac 100644 --- a/src/main/java/com/percero/agents/sync/services/AccessorService.java +++ b/src/main/java/com/percero/agents/sync/services/AccessorService.java @@ -121,7 +121,7 @@ public Accessor getReadAccessor(String userId, Class theClass, String classId, I if (hasReadAccess) { if (mappedClass.getReadQuery() != null && StringUtils.hasText(mappedClass.getReadQuery().getQuery())){ Query readFilter = s.createQuery(mappedClass.getReadQuery().getQuery()); - mappedClass.getReadQuery().setQueryParameters(readFilter, theObject, userId); + mappedClass.getReadQuery().setQueryParameters(readFilter.getQueryString(), theObject, userId); List readFilterResult = readFilter.list(); if (readFilterResult == null || readFilterResult.size() == 0) hasReadAccess = false; @@ -159,7 +159,7 @@ public Map getReadAccessors(List userIds, Class theCla if (hasReadAccess) { if (mappedClass.getReadQuery() != null && StringUtils.hasText(mappedClass.getReadQuery().getQuery())){ Query readFilter = s.createQuery(mappedClass.getReadQuery().getQuery()); - mappedClass.getReadQuery().setQueryParameters(readFilter, theObject, userId); + mappedClass.getReadQuery().setQueryParameters(readFilter.getQueryString(), theObject, userId); List readFilterResult = readFilter.list(); if (readFilterResult == null || readFilterResult.size() == 0) hasReadAccess = false; @@ -214,7 +214,7 @@ public Accessor getUpdateAccessor(String userId, Class theClass, String classId, if (mappedClass != null) { if (mappedClass.getUpdateQuery() != null && StringUtils.hasText(mappedClass.getUpdateQuery().getQuery())){ Query updateFilter = s.createQuery(mappedClass.getUpdateQuery().getQuery()); - mappedClass.getUpdateQuery().setQueryParameters(updateFilter, theObject, userId); + mappedClass.getUpdateQuery().setQueryParameters(updateFilter.getQueryString(), theObject, userId); List updateFilterResult = updateFilter.list(); if (updateFilterResult == null || updateFilterResult.size() == 0) hasUpdateAccess = false; @@ -269,7 +269,7 @@ public Accessor getCreateAccessor(String userId, Class theClass, String classId, if (mappedClass != null) { if (mappedClass.getCreateQuery() != null && StringUtils.hasText(mappedClass.getCreateQuery().getQuery())){ Query createFilter = s.createQuery(mappedClass.getCreateQuery().getQuery()); - mappedClass.getCreateQuery().setQueryParameters(createFilter, theObject, userId); + mappedClass.getCreateQuery().setQueryParameters(createFilter.getQueryString(), theObject, userId); List createFilterResult = createFilter.list(); if (createFilterResult == null || createFilterResult.size() == 0) hasCreateAccess = false; @@ -324,7 +324,7 @@ public Accessor getDeleteAccessor(String userId, Class theClass, String classId, if (mappedClass != null) { if (mappedClass.getDeleteQuery() != null && StringUtils.hasText(mappedClass.getDeleteQuery().getQuery())){ Query deleteFilter = s.createQuery(mappedClass.getDeleteQuery().getQuery()); - mappedClass.getDeleteQuery().setQueryParameters(deleteFilter, theObject, userId); + mappedClass.getDeleteQuery().setQueryParameters(deleteFilter.getQueryString(), theObject, userId); List deleteFilterResult = deleteFilter.list(); if (deleteFilterResult == null || deleteFilterResult.size() == 0) hasDeleteAccess = false; diff --git a/src/main/java/com/percero/agents/sync/services/DAODataProvider.java b/src/main/java/com/percero/agents/sync/services/DAODataProvider.java new file mode 100644 index 0000000..fe25bb5 --- /dev/null +++ b/src/main/java/com/percero/agents/sync/services/DAODataProvider.java @@ -0,0 +1,1279 @@ +package com.percero.agents.sync.services; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +import javassist.ClassPool; +import javassist.CtClass; +import javassist.CtField; +import javassist.CtMethod; + +import org.apache.log4j.Logger; +import org.codehaus.jackson.map.ObjectMapper; +import org.hibernate.PropertyValueException; +import org.hibernate.Query; +import org.hibernate.type.Type; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import com.percero.agents.sync.access.RedisKeyUtils; +import com.percero.agents.sync.dao.DAORegistry; +import com.percero.agents.sync.dao.IDataAccessObject; +import com.percero.agents.sync.datastore.ICacheDataStore; +import com.percero.agents.sync.exceptions.SyncDataException; +import com.percero.agents.sync.exceptions.SyncException; +import com.percero.agents.sync.metadata.IMappedClassManager; +import com.percero.agents.sync.metadata.MappedClass; +import com.percero.agents.sync.metadata.MappedClassManagerFactory; +import com.percero.agents.sync.metadata.MappedField; +import com.percero.agents.sync.metadata.MappedFieldList; +import com.percero.agents.sync.metadata.MappedFieldPerceroObject; +import com.percero.agents.sync.vo.BaseDataObject; +import com.percero.agents.sync.vo.ClassIDPair; +import com.percero.agents.sync.vo.ClassIDPairs; +import com.percero.agents.sync.vo.IJsonObject; +import com.percero.framework.vo.IPerceroObject; +import com.percero.framework.vo.PerceroList; + +@Component +public class DAODataProvider implements IDataProvider { + + // TODO: Better manage Hibernate Sessions (opening and closing). + + private static final Logger log = Logger.getLogger(DAODataProvider.class); + + private static DAODataProvider instance = null; + + public static DAODataProvider getInstance() { +// if (instance == null) { +// instance = new DAODataProvider(); +// instance.initialize(); +// } + return instance; + } + + public DAODataProvider() { + instance = this; + initialize(); + } + + public void initialize() + { + // Do nothing. + } + + public String getName() { + return "daoDataProvider"; + } + + @Autowired + IDataProviderManager dataProviderManager; + public void setDataProviderManager(IDataProviderManager value) { + dataProviderManager = value; + } + + @Autowired + ICacheDataStore cacheDataStore; + + @Autowired + Long cacheTimeout = Long.valueOf(60 * 60 * 24 * 14); // Two weeks + + @Autowired + ObjectMapper safeObjectMapper; + + + + @SuppressWarnings({ "unchecked" }) + // TODO: @Transactional(readOnly=true) + public PerceroList getAllByName(String className, Integer pageNumber, Integer pageSize, Boolean returnTotal, String userId) throws Exception { + IDataAccessObject dao = (IDataAccessObject) DAORegistry.getInstance().getDataAccessObject(className); + PerceroList results = dao.getAll(pageNumber, pageSize, returnTotal, userId, false); + + if (results != null && !results.isEmpty()) { + Iterator itrResults = results.iterator(); + while (itrResults.hasNext()) { + IPerceroObject nextResult = itrResults.next(); + try { + populateToManyRelationships(nextResult, true, null); + populateToOneRelationships(nextResult, true, null); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + throw new SyncDataException(e); + } catch (IllegalAccessException e) { + e.printStackTrace(); + throw new SyncDataException(e); + } catch (InvocationTargetException e) { + e.printStackTrace(); + throw new SyncDataException(e); + } + } + } + + putObjectsInRedisCache(results); + + // Now clean the objects for the user. + List cleanedObjects = cleanObject(results, userId); + results.clear(); + results.addAll(cleanedObjects); + return results; + } + + @SuppressWarnings("unchecked") + public Set getAllClassIdPairsByName(String className) throws Exception { + IDataAccessObject dao = (IDataAccessObject) DAORegistry.getInstance().getDataAccessObject(className); + PerceroList allObjects = dao.getAll(null, null, false, null, true); + + Set results = new HashSet(allObjects == null ? 0 : allObjects.size()); + + if (allObjects != null && !allObjects.isEmpty()) { + Iterator itrResults = allObjects.iterator(); + while (itrResults.hasNext()) { + IPerceroObject nextResult = itrResults.next(); + results.add(BaseDataObject.toClassIdPair(nextResult)); + } + } + + return results; + } + + @SuppressWarnings({ "unchecked" }) + // TODO: @Transactional(readOnly=true) + public Integer countAllByName(String className, String userId) throws Exception { + IDataAccessObject dao = (IDataAccessObject) DAORegistry.getInstance().getDataAccessObject(className); + Integer result = dao.countAll(userId); + return result; + } + + @SuppressWarnings("unchecked") + public List runQuery(MappedClass mappedClass, String queryName, Object[] queryArguments, String userId) throws SyncException { + IDataAccessObject dao = (IDataAccessObject) DAORegistry.getInstance().getDataAccessObject(mappedClass.className); + List result = dao.runQuery(queryName, queryArguments, userId); + return result; + } + + // TODO: This method has not been tested and is most likely broken. + @SuppressWarnings({ "rawtypes", "unchecked" }) + protected static List processQueryResults(String resultClassName, Query updateFilter, List updateFilterResult) throws Exception { + String[] returnAliases = updateFilter.getReturnAliases(); + Type[] returnTypes = updateFilter.getReturnTypes(); + + String[] fieldNames = new String[returnAliases.length]; + String[] getMethodNames = new String[returnAliases.length]; + String[] setMethodNames = new String[returnAliases.length]; + Class[] fieldClasses = new Class[returnAliases.length]; + + Class clazz = null; + ClassPool pool = null; + CtClass evalClass = null; + + if (returnAliases.length > 1) { + try { + clazz = MappedClass.forName(resultClassName); + } catch(Exception e) { + // Class must not yet exist, so let's create it. + } + + if (clazz == null) { + pool = ClassPool.getDefault(); + evalClass = pool.makeClass(resultClassName); + } + + // Create a new Class based on the result set. + for(int i = 0; i < returnAliases.length; i++) { + Type nextType = returnTypes[i]; + String nextTypeCanonicalName = nextType.getReturnedClass().getCanonicalName(); + String nextFieldName = returnAliases[i]; + try { + Integer.parseInt(nextFieldName); + nextFieldName = "field" + i; + } catch(NumberFormatException nfe) { + // Do nothing. Simply means the field name is not a Number. + } + String nextUpperFieldName = nextFieldName.substring(0, 1).toUpperCase() + nextFieldName.substring(1); + + fieldNames[i] = nextFieldName; + getMethodNames[i] = "get" + nextUpperFieldName; + setMethodNames[i] = "set" + nextUpperFieldName; + fieldClasses[i] = nextType.getReturnedClass(); + + if (evalClass != null) { + evalClass.addField(CtField.make("private " + fieldClasses[i].getCanonicalName() + " " + nextFieldName + ";", evalClass)); + evalClass.addMethod(CtMethod.make("public void " + setMethodNames[i] + "(" + fieldClasses[i].getCanonicalName() + " value) {this." + nextFieldName + " = value;}", evalClass)); + evalClass.addMethod(CtMethod.make("public " + nextTypeCanonicalName +" " + getMethodNames[i] + "() {return this." + nextFieldName + ";}", evalClass)); + } + } + + if (clazz == null && evalClass != null) { + clazz = evalClass.toClass(); + } + } + + List results = new ArrayList(); + + // Now populate the newly created objects. + for(Object nextResult : (List)updateFilterResult) { + + if (nextResult instanceof Object[]) { + Object nextObject = clazz.newInstance(); + for(int i = 0; i < returnAliases.length; i++) { + Class[] formalParams = new Class[] { fieldClasses[i] }; + Method setMethod = clazz.getDeclaredMethod(setMethodNames[i], formalParams); + setMethod.invoke(nextObject, ((Object[])nextResult)[i]); + } + + results.add(nextObject); + } else + results.add(nextResult); + } + + return results; + } + + public IPerceroObject findById(ClassIDPair classIdPair, String userId) { + return findById(classIdPair, userId, false); + } + @SuppressWarnings("unchecked") + public IPerceroObject findById(ClassIDPair classIdPair, String userId, Boolean ignoreCache) { + + try { + IPerceroObject result = null; + if (!ignoreCache) { + result = retrieveFromRedisCache(classIdPair); + } + + if (result == null) { + + IDataAccessObject dao = (IDataAccessObject) DAORegistry.getInstance().getDataAccessObject(classIdPair.getClassName()); + // Retrieve results BEFORE applying access rules so that our cached value represents the full object. + result = dao.retrieveObject(classIdPair, null, false); + populateToManyRelationships(result, true, null); + populateToOneRelationships(result, true, null); + + // Now put the object in the cache. + if (result != null) { + putObjectInRedisCache(result); + } + else { + // Not necessarily a problem but could be helpful when debugging. + log.debug("Unable to retrieve object from database: " + classIdPair.toJson()); + } + } + else { + // (Re)Set the expiration. + setObjectExpiration(result); + } + + result = cleanObject(result, userId); + + return result; + } catch(Exception e) { + log.error("Unable to findById: "+classIdPair.toJson(), e); + } + + return null; + } + + + private void putObjectInRedisCache(IPerceroObject perceroObject) { + // Now put the object in the cache. + if (cacheTimeout > 0 && perceroObject != null) { + String key = RedisKeyUtils.classIdPair(perceroObject.getClass().getCanonicalName(), perceroObject.getID()); + cacheDataStore.setValue(key, ((BaseDataObject)perceroObject).toJson()); + setObjectExpiration(key); + } + } + + private void putObjectsInRedisCache(List results) { + if (cacheTimeout > 0) { + Map mapJsonObjectStrings = new HashMap(results.size()); + Iterator itrDatabaseObjects = results.iterator(); + while (itrDatabaseObjects.hasNext()) { + IPerceroObject nextDatabaseObject = itrDatabaseObjects.next(); + String nextCacheKey = RedisKeyUtils.classIdPair(nextDatabaseObject.getClass().getCanonicalName(), nextDatabaseObject.getID()); + + mapJsonObjectStrings.put(nextCacheKey, ((BaseDataObject)nextDatabaseObject).toJson()); + } + + // Store the objects in redis. + cacheDataStore.setValues(mapJsonObjectStrings); + // (Re)Set the expiration. + cacheDataStore.expire(mapJsonObjectStrings.keySet(), cacheTimeout, TimeUnit.SECONDS); + } + } + + private void setObjectExpiration(IPerceroObject perceroObject) { + setObjectExpiration(RedisKeyUtils.classIdPair(perceroObject.getClass().getCanonicalName(), perceroObject.getID())); + } + + private void setObjectExpiration(String key) { + // (Re)Set the expiration. + if (cacheTimeout > 0 && key != null) { + cacheDataStore.expire(key, cacheTimeout, TimeUnit.SECONDS); + } + } + + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private IPerceroObject retrieveFromRedisCache(ClassIDPair classIdPair) throws Exception { + IPerceroObject result = null; + if (cacheTimeout > 0) { + IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); + Class theClass = MappedClass.forName(classIdPair.getClassName()); + MappedClass mc = mcm.getMappedClassByClassName(classIdPair.getClassName()); + + String key = RedisKeyUtils.classIdPair(classIdPair.getClassName(), classIdPair.getID()); + String jsonObjectString = (String) cacheDataStore.getValue(key); + if (jsonObjectString != null) { + if (IJsonObject.class.isAssignableFrom(theClass)) { + IJsonObject jsonObject = (IJsonObject) theClass.newInstance(); + jsonObject.fromJson(jsonObjectString); + result = (IPerceroObject) jsonObject; + } + else { + result = (IPerceroObject) safeObjectMapper.readValue(jsonObjectString, theClass); + } + } + else { + // Check MappedClass' child classes. + Iterator itrChildMappedClasses = mc.childMappedClasses.iterator(); + while (itrChildMappedClasses.hasNext()) { + MappedClass nextChildMc = itrChildMappedClasses.next(); + key = RedisKeyUtils.classIdPair(nextChildMc.className, classIdPair.getID()); + jsonObjectString = (String) cacheDataStore.getValue(key); + if (jsonObjectString != null) { + result = (IPerceroObject) safeObjectMapper.readValue(jsonObjectString, theClass); + return result; + } + } + } + } + + return result; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private Map retrieveFromRedisCache(ClassIDPairs classIdPairs, Boolean pleaseSetTimeout) throws Exception { + Map result = new HashMap(classIdPairs.getIds().size()); + + if (cacheTimeout > 0) { + IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); + Class theClass = MappedClass.forName(classIdPairs.getClassName()); + MappedClass mc = mcm.getMappedClassByClassName(classIdPairs.getClassName()); + + Set keys = new HashSet(classIdPairs.getIds().size()); + Iterator itrIds = classIdPairs.getIds().iterator(); + while (itrIds.hasNext()) { + String nextId = itrIds.next(); + String nextKey = RedisKeyUtils.classIdPair(classIdPairs.getClassName(), nextId); + keys.add(nextKey); + + // Check MappedClass' child classes. + Iterator itrChildMappedClasses = mc.childMappedClasses.iterator(); + while (itrChildMappedClasses.hasNext()) { + MappedClass nextChildMc = itrChildMappedClasses.next(); + if (nextChildMc.clazz == BaseDataObject.class) { + // Reached the top level, so break. + break; + } + nextKey = RedisKeyUtils.classIdPair(nextChildMc.className, nextId); + keys.add(nextKey); + } + } + +// String key = RedisKeyUtils.classIdPair(classIdPair.getClassName(), classIdPair.getID()); + List jsonObjectStrings = cacheDataStore.getValues(keys); + Iterator itrJsonObjectStrings = jsonObjectStrings.iterator(); + while (itrJsonObjectStrings.hasNext()) { + String jsonObjectString = (String) itrJsonObjectStrings.next(); + if (jsonObjectString != null) { + if (IJsonObject.class.isAssignableFrom(theClass)) { + IJsonObject jsonObject = (IJsonObject) theClass.newInstance(); + jsonObject.fromJson(jsonObjectString); + result.put( (((IPerceroObject) jsonObject).getID()), (IPerceroObject) jsonObject ); + } + else { + IPerceroObject nextPerceroObject = (IPerceroObject) safeObjectMapper.readValue(jsonObjectString, theClass); + result.put( nextPerceroObject.getID(), nextPerceroObject); + } + + } +// else { +// // Check MappedClass' child classes. +// Iterator itrChildMappedClasses = mc.childMappedClasses.iterator(); +// while (itrChildMappedClasses.hasNext()) { +// MappedClass nextChildMc = itrChildMappedClasses.next(); +// key = RedisKeyUtils.classIdPair(nextChildMc.className, classIdPair.getID()); +// jsonObjectString = (String) redisDataStore.getValue(key); +// if (jsonObjectString != null) { +// result = (IPerceroObject) safeObjectMapper.readValue(jsonObjectString, theClass); +// return result; +// } +// } +// } + } + + if (pleaseSetTimeout) { + cacheDataStore.expire(keys, cacheTimeout, TimeUnit.SECONDS); + } + } + + return result; + } + + @SuppressWarnings({ }) + public Boolean getReadAccess(ClassIDPair classIdPair, String userId) { + IDataAccessObject dao = DAORegistry.getInstance().getDataAccessObject(classIdPair.getClassName()); + return dao.hasReadAccess(classIdPair, userId); + } + + @SuppressWarnings({ }) + public Boolean getDeleteAccess(ClassIDPair classIdPair, String userId) { + IDataAccessObject dao = DAORegistry.getInstance().getDataAccessObject(classIdPair.getClassName()); + return dao.hasDeleteAccess(classIdPair, userId); + } + + public List findByIds(ClassIDPairs classIdPairs, String userId) { + return findByIds(classIdPairs, userId, false); + } + @SuppressWarnings({ }) + public List findByIds(ClassIDPairs classIdPairs, String userId, Boolean ignoreCache) { + IDataAccessObject dao = DAORegistry.getInstance().getDataAccessObject(classIdPairs.getClassName()); + List results = new ArrayList(); + + try { + // Copy the ClassIDPairs to find object since we remove any of the + // ID's from the list that we find in the cache. + ClassIDPairs classIdPairsCopy = new ClassIDPairs(); + classIdPairsCopy.setClassName(classIdPairs.getClassName()); + List idsToFind = new ArrayList(classIdPairs.getIds().size()); + idsToFind.addAll(classIdPairs.getIds()); + classIdPairsCopy.setIds(idsToFind); + + Map cachedResults = null; + if (!ignoreCache) { + cachedResults = retrieveFromRedisCache(classIdPairs, true); + if (cachedResults != null &&!cachedResults.isEmpty()) { + // Add the cached results + + Iterator itrCachedResults = cachedResults.values().iterator(); + while (itrCachedResults.hasNext()) { + IPerceroObject nextCachedResult = itrCachedResults.next(); + if (nextCachedResult != null) { + idsToFind.remove(nextCachedResult.getID()); + results.add(nextCachedResult); + setObjectExpiration(nextCachedResult); + } + } + } + } + + List daoObjects = null; + if (classIdPairsCopy.getIds() != null && !classIdPairsCopy.getIds().isEmpty()) { + daoObjects = dao.retrieveObjects(classIdPairsCopy, userId, false); + + for(IPerceroObject nextResult : daoObjects) { + populateToManyRelationships(nextResult, true, null); + populateToOneRelationships(nextResult, true, null); + } + + putObjectsInRedisCache(daoObjects); // Only need to put objects in cache that were not already found in cache. + results.addAll(daoObjects); + } + + // Now clean the objects for the user. + results = cleanObject(results, userId); + + } catch(Exception e) { + log.error(e); + e.printStackTrace(); + } + + return results; + } + + + @SuppressWarnings("unchecked") + public List findByExample(IPerceroObject theQueryObject, List excludeProperties, String userId, Boolean shellOnly) throws SyncException { + IDataAccessObject dao = (IDataAccessObject) DAORegistry.getInstance().getDataAccessObject(theQueryObject.getClass().getCanonicalName()); + List results = dao.findByExample(theQueryObject, excludeProperties, userId, shellOnly); + + if (results != null && !results.isEmpty()) { + Iterator itrResults = results.iterator(); + while (itrResults.hasNext()) { + IPerceroObject nextResult = itrResults.next(); + try { + populateToManyRelationships(nextResult, true, null); + populateToOneRelationships(nextResult, true, null); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + throw new SyncDataException(e); + } catch (IllegalAccessException e) { + e.printStackTrace(); + throw new SyncDataException(e); + } catch (InvocationTargetException e) { + e.printStackTrace(); + throw new SyncDataException(e); + } + } + } + + putObjectsInRedisCache(results); + + // Now clean the objects for the user. + results = cleanObject(results, userId); + + return results; + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public T createObject(T perceroObject, String userId) throws SyncException { + + try { + IDataAccessObject dao = (IDataAccessObject) DAORegistry.getInstance().getDataAccessObject(perceroObject.getClass().getCanonicalName()); + // Make sure object has an ID. + IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); + MappedClass mappedClass = mcm.getMappedClassByClassName(perceroObject.getClass().getName()); + if (!mappedClass.hasGeneratedId && !StringUtils.hasText(perceroObject.getID())) + perceroObject.setID(UUID.randomUUID().toString()); + else { + // Check to see if item already exists. + IPerceroObject existingObject = dao.retrieveObject(BaseDataObject.toClassIdPair(perceroObject), null, false); + if (existingObject != null) + { + populateToManyRelationships(perceroObject, true, null); + populateToOneRelationships(perceroObject, true, null); + return (T) cleanObject(perceroObject, userId); + } + } + + perceroObject = (T) dao.createObject(perceroObject, userId); + if (perceroObject == null) { + return perceroObject; + } + populateToManyRelationships(perceroObject, true, null); + populateToOneRelationships(perceroObject, true, null); + + // Now update the cache. + // TODO: Field-level updates could be REALLY useful here. Would avoid A TON of UNNECESSARY work... + if (cacheTimeout > 0) { + String key = RedisKeyUtils.classIdPair(perceroObject.getClass().getCanonicalName(), perceroObject.getID()); + cacheDataStore.setValue(key, ((BaseDataObject)perceroObject).toJson()); + cacheDataStore.expire(key, cacheTimeout, TimeUnit.SECONDS); + + Set keysToDelete = new HashSet(); + MappedClass nextMappedClass = mcm.getMappedClassByClassName(perceroObject.getClass().getName()); + Iterator itrToManyFields = nextMappedClass.toManyFields.iterator(); + while(itrToManyFields.hasNext()) { + MappedField nextMappedField = itrToManyFields.next(); + Object fieldObject = nextMappedField.getGetter().invoke(perceroObject); + if (fieldObject != null) { + if (fieldObject instanceof IPerceroObject) { + String nextKey = RedisKeyUtils.classIdPair(fieldObject.getClass().getCanonicalName(), ((IPerceroObject)fieldObject).getID()); + keysToDelete.add(nextKey); + } + else if (fieldObject instanceof Collection) { + Iterator itrFieldObject = ((Collection) fieldObject).iterator(); + while(itrFieldObject.hasNext()) { + Object nextListObject = itrFieldObject.next(); + if (nextListObject instanceof IPerceroObject) { + String nextKey = RedisKeyUtils.classIdPair(nextListObject.getClass().getCanonicalName(), ((IPerceroObject)nextListObject).getID()); + keysToDelete.add(nextKey); + } + } + } + } + } + Iterator itrToOneFields = mappedClass.toOneFields.iterator(); + while(itrToOneFields.hasNext()) { + MappedFieldPerceroObject nextMappedField = itrToOneFields.next(); + Object fieldObject = nextMappedField.getGetter().invoke(perceroObject); + if (fieldObject != null) { + if (fieldObject instanceof IPerceroObject) { + String nextKey = RedisKeyUtils.classIdPair(fieldObject.getClass().getCanonicalName(), ((IPerceroObject)fieldObject).getID()); + keysToDelete.add(nextKey); + } + else if (fieldObject instanceof Collection) { + Iterator itrFieldObject = ((Collection) fieldObject).iterator(); + while(itrFieldObject.hasNext()) { + Object nextListObject = itrFieldObject.next(); + if (nextListObject instanceof IPerceroObject) { + String nextKey = RedisKeyUtils.classIdPair(nextListObject.getClass().getCanonicalName(), ((IPerceroObject)nextListObject).getID()); + keysToDelete.add(nextKey); + } + } + } + } + } + + if (!keysToDelete.isEmpty()) { + cacheDataStore.deleteKeys(keysToDelete); + // TODO: Do we simply delete the key? Or do we refetch the object here and update the key? + //redisDataStore.setValue(nextKey, ((BaseDataObject)perceroObject).toJson()); + } + } + + return (T) cleanObject(perceroObject, userId); + } + catch(PropertyValueException pve) { + log.error("Error creating object", pve); + + SyncDataException sde = new SyncDataException(SyncDataException.MISSING_REQUIRED_FIELD, SyncDataException.MISSING_REQUIRED_FIELD_CODE, "Missing required field " + pve.getPropertyName()); + sde.fieldName = pve.getPropertyName(); + throw sde; + } + catch(Exception e) { + log.error("Error creating object", e); + + SyncDataException sde = new SyncDataException(SyncDataException.CREATE_OBJECT_ERROR, SyncDataException.CREATE_OBJECT_ERROR_CODE); + throw sde; + } + } + + + //////////////////////////////////////////////////// + // PUT + //////////////////////////////////////////////////// + @SuppressWarnings("unchecked") + public T putObject(T perceroObject, Map> changedFields, String userId) throws SyncException { + IDataAccessObject dao = (IDataAccessObject) DAORegistry.getInstance().getDataAccessObject(perceroObject.getClass().getCanonicalName()); + perceroObject = (T) dao.updateObject(perceroObject, changedFields, userId); + + try { + populateToManyRelationships(perceroObject, true, null); + populateToOneRelationships(perceroObject, true, null); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + throw new SyncException(e); + } catch (IllegalAccessException e) { + e.printStackTrace(); + throw new SyncException(e); + } catch (InvocationTargetException e) { + e.printStackTrace(); + throw new SyncException(e); + } + // Now update the cache. + if (cacheTimeout > 0) { + // TODO: Also need to update the caches of anything object that is related to this object. + String key = RedisKeyUtils.classIdPair(perceroObject.getClass().getCanonicalName(), perceroObject.getID()); + if (cacheDataStore.hasKey(key)) { + cacheDataStore.setValue(key, ((BaseDataObject)perceroObject).toJson()); + } + + // Iterate through each changed object and reset the cache for that object. + if (changedFields != null) { + Iterator itrChangedFieldKeyset = changedFields.keySet().iterator(); + Set keysToDelete = new HashSet(); + while (itrChangedFieldKeyset.hasNext()) { + ClassIDPair thePair = itrChangedFieldKeyset.next(); + if (!thePair.comparePerceroObject(perceroObject)) { + String nextKey = RedisKeyUtils.classIdPair(thePair.getClassName(), thePair.getID()); + keysToDelete.add(nextKey); + } + } + + if (!keysToDelete.isEmpty()) { + cacheDataStore.deleteKeys(keysToDelete); + // TODO: Do we simply delete the key? Or do we refetch the object here and update the key? + //redisDataStore.setValue(nextKey, ((BaseDataObject)perceroObject).toJson()); + } + } + else { + // No changedFields? We should never get here? + IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); + MappedClass mappedClass = mcm.getMappedClassByClassName(perceroObject.getClass().getName()); + Iterator itrToManyFields = mappedClass.toManyFields.iterator(); + while(itrToManyFields.hasNext()) { + MappedField nextMappedField = itrToManyFields.next(); + Object fieldObject = null; + try { + fieldObject = nextMappedField.getGetter().invoke(perceroObject); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + if (fieldObject != null) { + if (fieldObject instanceof IPerceroObject) { + String nextKey = RedisKeyUtils.classIdPair(fieldObject.getClass().getCanonicalName(), ((IPerceroObject)fieldObject).getID()); + if (cacheDataStore.hasKey(nextKey)) { + cacheDataStore.deleteKey(nextKey); + // TODO: Do we simply delete the key? Or do we refetch the object here and update the key? + //redisDataStore.setValue(nextKey, ((BaseDataObject)perceroObject).toJson()); + } + } + else if (fieldObject instanceof Collection) { + Iterator itrFieldObject = ((Collection) fieldObject).iterator(); + while(itrFieldObject.hasNext()) { + Object nextListObject = itrFieldObject.next(); + if (nextListObject instanceof IPerceroObject) { + String nextKey = RedisKeyUtils.classIdPair(nextListObject.getClass().getCanonicalName(), ((IPerceroObject)nextListObject).getID()); + if (cacheDataStore.hasKey(nextKey)) { + cacheDataStore.deleteKey(nextKey); + // TODO: Do we simply delete the key? Or do we refetch the object here and update the key? + //redisDataStore.setValue(nextKey, ((BaseDataObject)perceroObject).toJson()); + } + } + } + } + } + } + Iterator itrToOneFields = mappedClass.toOneFields.iterator(); + while(itrToOneFields.hasNext()) { + MappedFieldPerceroObject nextMappedField = itrToOneFields.next(); + Object fieldObject = null; + try { + fieldObject = nextMappedField.getGetter().invoke(perceroObject); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + if (fieldObject != null) { + if (fieldObject instanceof IPerceroObject) { + String nextKey = RedisKeyUtils.classIdPair(fieldObject.getClass().getCanonicalName(), ((IPerceroObject)fieldObject).getID()); + if (cacheDataStore.hasKey(nextKey)) { + cacheDataStore.deleteKey(nextKey); + // TODO: Do we simply delete the key? Or do we refetch the object here and update the key? + //redisDataStore.setValue(nextKey, ((BaseDataObject)perceroObject).toJson()); + } + } + else if (fieldObject instanceof Collection) { + Iterator itrFieldObject = ((Collection) fieldObject).iterator(); + while(itrFieldObject.hasNext()) { + Object nextListObject = itrFieldObject.next(); + if (nextListObject instanceof IPerceroObject) { + String nextKey = RedisKeyUtils.classIdPair(nextListObject.getClass().getCanonicalName(), ((IPerceroObject)nextListObject).getID()); + if (cacheDataStore.hasKey(nextKey)) { + cacheDataStore.deleteKey(nextKey); + // TODO: Do we simply delete the key? Or do we refetch the object here and update the key? + //redisDataStore.setValue(nextKey, ((BaseDataObject)perceroObject).toJson()); + } + } + } + } + } + } + } + } + + return (T) cleanObject(perceroObject, userId); + } + + + //////////////////////////////////////////////////// + // DELETE + //////////////////////////////////////////////////// + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Boolean deleteObject(ClassIDPair theClassIdPair, String userId) throws SyncException { + + IDataAccessObject dao = (IDataAccessObject) DAORegistry.getInstance().getDataAccessObject(theClassIdPair.getClassName()); + IPerceroObject perceroObject = dao.retrieveObject(theClassIdPair, null, false); // Retrieve the full object so we can update the cache if the delete is successful. + Boolean result = dao.deleteObject(theClassIdPair, userId); + + try { + MappedClass mappedClass = MappedClassManagerFactory.getMappedClassManager().getMappedClassByClassName(perceroObject.getClass().getCanonicalName()); + if (mappedClass == null) { + log.warn("Missing MappedClass for " + perceroObject.getClass().getCanonicalName()); + throw new SyncException(SyncException.MISSING_MAPPED_CLASS_ERROR, SyncException.MISSING_MAPPED_CLASS_ERROR_CODE ); + } + + // Now delete from cache. + // Now update the cache. + // TODO: Field-level updates could be REALLY useful here. Would avoid A TON of UNNECESSARY work... + if (result && cacheTimeout > 0) { + Set keysToDelete = new HashSet(); + + String key = RedisKeyUtils.classIdPair(perceroObject.getClass().getCanonicalName(), perceroObject.getID()); + keysToDelete.add(key); + + Iterator itrToManyFields = mappedClass.toManyFields.iterator(); + while(itrToManyFields.hasNext()) { + MappedField nextMappedField = itrToManyFields.next(); + Object fieldObject = nextMappedField.getGetter().invoke(perceroObject); + if (fieldObject != null) { + if (fieldObject instanceof IPerceroObject) { + String nextKey = RedisKeyUtils.classIdPair(fieldObject.getClass().getCanonicalName(), ((IPerceroObject)fieldObject).getID()); + keysToDelete.add(nextKey); + } + else if (fieldObject instanceof Collection) { + Iterator itrFieldObject = ((Collection) fieldObject).iterator(); + while(itrFieldObject.hasNext()) { + Object nextListObject = itrFieldObject.next(); + if (nextListObject instanceof IPerceroObject) { + String nextKey = RedisKeyUtils.classIdPair(nextListObject.getClass().getCanonicalName(), ((IPerceroObject)nextListObject).getID()); + keysToDelete.add(nextKey); + } + } + } + } + } + Iterator itrToOneFields = mappedClass.toOneFields.iterator(); + while(itrToOneFields.hasNext()) { + MappedFieldPerceroObject nextMappedField = itrToOneFields.next(); + Object fieldObject = nextMappedField.getGetter().invoke(perceroObject); + if (fieldObject != null) { + if (fieldObject instanceof IPerceroObject) { + String nextKey = RedisKeyUtils.classIdPair(fieldObject.getClass().getCanonicalName(), ((IPerceroObject)fieldObject).getID()); + keysToDelete.add(nextKey); + } + else if (fieldObject instanceof Collection) { + Iterator itrFieldObject = ((Collection) fieldObject).iterator(); + while(itrFieldObject.hasNext()) { + Object nextListObject = itrFieldObject.next(); + if (nextListObject instanceof IPerceroObject) { + String nextKey = RedisKeyUtils.classIdPair(nextListObject.getClass().getCanonicalName(), ((IPerceroObject)nextListObject).getID()); + keysToDelete.add(nextKey); + } + } + } + } + } + + if (!keysToDelete.isEmpty()) { + cacheDataStore.deleteKeys(keysToDelete); + } + } + + } catch(Exception e) { + if (perceroObject != null) { + log.error("Unable to delete record from database: " + perceroObject.getClass().getCanonicalName() + ":" + perceroObject.getID(), e); + } + else { + log.error("Unable to delete record from database: NULL Object", e); + } + throw new SyncDataException(SyncDataException.DELETE_OBJECT_ERROR, SyncDataException.DELETE_OBJECT_ERROR_CODE); + } + + return result; + } + + + //////////////////////////////////////////////////// + // CLEAN + //////////////////////////////////////////////////// + @SuppressWarnings("unchecked") + public IPerceroObject cleanObject(IPerceroObject perceroObject, String userId) throws SyncException { + IDataAccessObject dao = (IDataAccessObject) DAORegistry.getInstance().getDataAccessObject(perceroObject.getClass().getCanonicalName()); + perceroObject = dao.cleanObjectForUser(perceroObject, userId); + return perceroObject; + } + + @Override + @SuppressWarnings({ "rawtypes", "unchecked" }) + public List cleanObject(List perceroObjects, String userId) throws SyncException { + try { + List results = new ArrayList(perceroObjects.size()); + + Map> classPairs = new HashMap>(); + + Iterator itrPerceroObjects = perceroObjects.iterator(); + while (itrPerceroObjects.hasNext()) { + IPerceroObject nextPerceroObject = itrPerceroObjects.next(); + if ( ((BaseDataObject)nextPerceroObject).getIsClean()) { + results.add(nextPerceroObject); + } + else { + List classObjects = classPairs.get(nextPerceroObject.getClass()); + if (classObjects == null) { + classObjects = new ArrayList(); + classPairs.put(nextPerceroObject.getClass(), classObjects); + } + classObjects.add(nextPerceroObject); + } + } + + Iterator>> itrClassPairs = classPairs.entrySet().iterator(); + while (itrClassPairs.hasNext()) { + Entry> nextEntrySet = itrClassPairs.next(); + IDataAccessObject dao = (IDataAccessObject) DAORegistry.getInstance().getDataAccessObject(nextEntrySet.getKey().getCanonicalName()); + + List nextClassObjects = nextEntrySet.getValue(); + if (nextClassObjects != null && !nextClassObjects.isEmpty()) { + Iterator itrClassObjects = nextClassObjects.iterator(); + while (itrClassObjects.hasNext()) { + IPerceroObject nextObject = itrClassObjects.next(); + IPerceroObject nextResult = dao.cleanObjectForUser(nextObject, userId); + if (nextResult != null) { + results.add(nextResult); + } + } + } + } + + return results; + } catch(Exception e) { + throw new SyncException(e); + } + } + + + @SuppressWarnings("rawtypes") + public Map> getChangedMappedFields(IPerceroObject newObject) { + Map> result = new HashMap>(); + Collection baseObjectResult = null; + ClassIDPair basePair = new ClassIDPair(newObject.getID(), newObject.getClass().getCanonicalName()); + + String className = newObject.getClass().getCanonicalName(); + IPerceroObject oldObject = findById(new ClassIDPair(newObject.getID(), className), null); + IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); + MappedClass mc = mcm.getMappedClassByClassName(className); + Iterator itrMappedFields = mc.externalizableFields.iterator(); + while (itrMappedFields.hasNext()) { + MappedField nextMappedField = itrMappedFields.next(); + try { + Boolean fieldIsEqual = nextMappedField.compareObjects(oldObject, newObject); + if (!fieldIsEqual) { + if (baseObjectResult == null) { + baseObjectResult = new HashSet(); + result.put(basePair, baseObjectResult); + } + baseObjectResult.add(nextMappedField); + + // If this is a relationship field, then need to grab the old and new values. + if (nextMappedField.getReverseMappedField() != null) { + if (nextMappedField instanceof MappedFieldPerceroObject) { + MappedFieldPerceroObject nextMappedFieldPerceroObject = (MappedFieldPerceroObject) nextMappedField; + + IPerceroObject oldReversePerceroObject = (IPerceroObject) nextMappedFieldPerceroObject.getGetter().invoke(oldObject); + if (oldReversePerceroObject != null) { + ClassIDPair oldReversePair = new ClassIDPair(oldReversePerceroObject.getID(), oldReversePerceroObject.getClass().getCanonicalName()); + Collection oldReverseChangedFields = result.get(oldReversePair); + if (oldReverseChangedFields == null) { + oldReverseChangedFields = new HashSet(); + result.put(oldReversePair, oldReverseChangedFields); + } + oldReverseChangedFields.add(nextMappedField.getReverseMappedField()); + } + + IPerceroObject newReversePerceroObject = (IPerceroObject) nextMappedFieldPerceroObject.getGetter().invoke(newObject); + if (newReversePerceroObject != null) { + ClassIDPair newReversePair = new ClassIDPair(newReversePerceroObject.getID(), newReversePerceroObject.getClass().getCanonicalName()); + Collection changedFields = result.get(newReversePair); + if (changedFields == null) { + changedFields = new HashSet(); + result.put(newReversePair, changedFields); + } + changedFields.add(nextMappedField.getReverseMappedField()); + } + } + else if (nextMappedField instanceof MappedFieldList) { + MappedFieldList nextMappedFieldList = (MappedFieldList) nextMappedField; + + List oldReverseList = (List) nextMappedFieldList.getGetter().invoke(oldObject); + if (oldReverseList == null) + oldReverseList = new ArrayList(); + + List newReverseList = (List) nextMappedFieldList.getGetter().invoke(newObject); + if (newReverseList == null) + newReverseList = new ArrayList(); + + // Compare each item in the lists. + Collection oldChangedList = retrieveObjectsNotInCollection(oldReverseList, newReverseList); + Iterator itrOldChangedList = oldChangedList.iterator(); + while (itrOldChangedList.hasNext()) { + BaseDataObject nextOldChangedObject = (BaseDataObject) itrOldChangedList.next(); + ClassIDPair nextOldReversePair = BaseDataObject.toClassIdPair(nextOldChangedObject); + + // Old object is not in new list, so add to list of changed fields. + Collection changedFields = result.get(nextOldReversePair); + if (changedFields == null) { + changedFields = new HashSet(); + result.put(nextOldReversePair, changedFields); + } + changedFields.add(nextMappedField.getReverseMappedField()); + } + + Collection newChangedList = retrieveObjectsNotInCollection(newReverseList, oldReverseList); + Iterator itrNewChangedList = newChangedList.iterator(); + while (itrNewChangedList.hasNext()) { + BaseDataObject nextNewChangedObject = (BaseDataObject) itrNewChangedList.next(); + ClassIDPair nextNewReversePair = BaseDataObject.toClassIdPair(nextNewChangedObject); + + // Old object is not in new list, so add to list of changed fields. + Collection changedFields = result.get(nextNewReversePair); + if (changedFields == null) { + changedFields = new HashSet(); + result.put(nextNewReversePair, changedFields); + } + changedFields.add(nextMappedField.getReverseMappedField()); + } + } + } + } + } catch(Exception e) { + log.warn("Error getting changed field: " + nextMappedField.getField().getName(), e); + baseObjectResult.add(nextMappedField); + } + } + + return result; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private Collection retrieveObjectsNotInCollection(Collection baseList, Collection compareToList) { + Collection result = new HashSet(); + Iterator itrBaseList = baseList.iterator(); + Iterator itrCompareToList = null; + boolean matchFound = false; + + while (itrBaseList.hasNext()) { + BaseDataObject nextBasePerceroObject = (BaseDataObject) itrBaseList.next(); + ClassIDPair nextBasePair = BaseDataObject.toClassIdPair(nextBasePerceroObject); + nextBasePerceroObject = (BaseDataObject) findById(nextBasePair, null); + + itrCompareToList = compareToList.iterator(); + matchFound = false; + while (itrCompareToList.hasNext()) { + BaseDataObject nextCompareToPerceroObject = (BaseDataObject) itrCompareToList.next(); + nextCompareToPerceroObject = (BaseDataObject) findById(BaseDataObject.toClassIdPair(nextCompareToPerceroObject), null); + + if (nextBasePerceroObject.getClass() == nextCompareToPerceroObject.getClass() && nextBasePerceroObject.getID().equalsIgnoreCase(nextCompareToPerceroObject.getID())) { + matchFound = true; + break; + } + } + + if (!matchFound) { + result.add(nextBasePerceroObject); + } + } + + return result; + } + + + /* (non-Javadoc) + * @see com.percero.agents.sync.services.IDataProvider#findAllRelatedObjects(com.percero.framework.vo.IPerceroObject, com.percero.agents.sync.metadata.MappedField, java.lang.Boolean, java.lang.String) + */ + +// @Override + @SuppressWarnings("unchecked") + public List findAllRelatedObjects(IPerceroObject perceroObject, MappedField mappedField, Boolean shellOnly, String userId) throws SyncException { + List result = new ArrayList(); + + if (!StringUtils.hasText(perceroObject.getID())) { + // No valid ID on the object, so can't search for it. + return result; + } + + if (mappedField.getMappedClass().getSourceMappedFields().contains(mappedField)) { + // This object is the source. + IDataAccessObject dao = (IDataAccessObject) DAORegistry.getInstance().getDataAccessObject(mappedField.getMappedClass().className); + IPerceroObject thisObject = dao.retrieveObject(BaseDataObject.toClassIdPair(perceroObject), userId, false); + IPerceroObject relatedObject; + try { + relatedObject = (IPerceroObject) mappedField.getGetter().invoke(thisObject); + } catch (Exception e) { + throw new SyncException(e); + } + if (relatedObject != null) { + if (!shellOnly) { + MappedClass relatedMappedClass = MappedClassManagerFactory.getMappedClassManager().getMappedClassByClassName(relatedObject.getClass().getCanonicalName()); + relatedObject = relatedMappedClass.getDataProvider().findById(BaseDataObject.toClassIdPair(relatedObject), userId); + } + result.add(relatedObject); + } + result.add(relatedObject); + } + else { + // This object is the target. + // The reverse mapped field should be the MappedField on the target object, the one that this MUST be the data provider for. + MappedField reverseMappedField = mappedField.getReverseMappedField(); + if (reverseMappedField == null) { + // No reverse mapped field, meaning there is nothing to do. + return result; + } + + IDataProvider dataProvider = reverseMappedField.getMappedClass().getDataProvider(); + result = dataProvider.getAllByRelationship(reverseMappedField, BaseDataObject.toClassIdPair(perceroObject), shellOnly, userId); + } + + return result; + } + + @SuppressWarnings("unchecked") + public List getAllByRelationship(MappedField mappedField, ClassIDPair targetClassIdPair, Boolean shellOnly, String userId) throws SyncException { + if (mappedField.getMappedClass().getSourceMappedFields().contains(mappedField)) { + // This object is the source. + IDataAccessObject dao = (IDataAccessObject) DAORegistry.getInstance().getDataAccessObject(mappedField.getMappedClass().className); + return dao.retrieveAllByRelationship(mappedField, targetClassIdPair, shellOnly, userId); + } + else { + // This object is the target. + if (mappedField.getReverseMappedField() != null) { + IDataProvider dataProvider = mappedField.getReverseMappedField().getMappedClass().getDataProvider(); + try { + IPerceroObject targetObject = (IPerceroObject) Class.forName(targetClassIdPair.getClassName()).newInstance(); + return dataProvider.findAllRelatedObjects(targetObject, mappedField.getReverseMappedField(), shellOnly, userId); + } catch(Exception e) { + throw new SyncException(e); + } + } + else { + return new ArrayList(0); + } + } + + + } + + + protected boolean isMappedFieldInChangedFields(IPerceroObject perceroObject, MappedField nextMappedField, Map> changedFields) { + Iterator>> itrChangedFields = changedFields.entrySet().iterator(); + while (itrChangedFields.hasNext()) { + Entry> nextChangedFieldEntry = itrChangedFields.next(); + + if (!nextChangedFieldEntry.getKey().comparePerceroObject(perceroObject)) { + // Not the same object. + continue; + } + + for(MappedField nextChangedField : nextChangedFieldEntry.getValue()) { + if ( StringUtils.hasText(nextMappedField.getColumnName()) && nextMappedField.getColumnName().equalsIgnoreCase(nextChangedField.getColumnName()) ) { + // We have found our field. + return true; + } + else if ( StringUtils.hasText(nextMappedField.getJoinColumnName()) && nextMappedField.getJoinColumnName().equalsIgnoreCase(nextChangedField.getJoinColumnName()) ) { + // We have found our field. + return true; + } + } + } + + return false; + } + + + /** + * Populates all the *-TO-MANY relationships on the specified object. All TO-MANY relationships have their + * relationship data stored on the other side of the relationship, so we have to go those dataProvider and + * ask for the objects. + * + * @param perceroObject + * @param userId + * @throws SyncException + * @throws IllegalArgumentException + * @throws IllegalAccessException + * @throws InvocationTargetException + */ + public void populateToManyRelationships(IPerceroObject perceroObject, Boolean shellOnly, + String userId) throws SyncException, IllegalArgumentException, + IllegalAccessException, InvocationTargetException { + if (perceroObject == null || !StringUtils.hasText(perceroObject.getID())) { + // Invalid object. + log.warn("Invalid object in populateToManyRelationships"); + return; + } + + MappedClass mappedClass = MappedClassManagerFactory.getMappedClassManager().getMappedClassByClassName(perceroObject.getClass().getCanonicalName()); + for(MappedField nextToManyMappedField : mappedClass.toManyFields) { + + // TODO: Take into account Access Rights. + List allRelatedObjects = findAllRelatedObjects(perceroObject, nextToManyMappedField, shellOnly, userId); + nextToManyMappedField.getSetter().invoke(perceroObject, allRelatedObjects); + } + } + + public void populateToOneRelationships(IPerceroObject perceroObject, Boolean shellOnly, + String userId) throws SyncException, IllegalArgumentException, + IllegalAccessException, InvocationTargetException { + if (perceroObject == null || !StringUtils.hasText(perceroObject.getID())) { + // Invalid object. + log.warn("Invalid object in populateToOneRelationships"); + return; + } + + MappedClass mappedClass = MappedClassManagerFactory.getMappedClassManager().getMappedClassByClassName(perceroObject.getClass().getCanonicalName()); + for(MappedFieldPerceroObject nextToOneMappedField : mappedClass.toOneFields) { + // If perceroObject is the "owner" of this relationship, then we have all the data necessary here. + if (nextToOneMappedField.isSourceEntity()) { +// if (!shellOnly || true) { +// // We want more than a Shell object, so ask the dataProvider of the mappedField for that object. +// IDataProvider dataProvider = nextToOneMappedField.getReverseMappedField().getMappedClass().getDataProvider(); +// IPerceroObject relatedPerceroObject = nextToOneMappedField.getPerceroObjectValue(perceroObject); +// relatedPerceroObject = dataProvider.systemGetById(BaseDataObject.toClassIdPair(relatedPerceroObject)); +// nextToOneMappedField.getSetter().invoke(perceroObject, relatedPerceroObject); +// +// if (!((BaseDataObject)perceroObject).getIsClean()) { +// ((BaseDataObject)perceroObject).setIsClean(true); +// dataProvider.populateToManyRelationships(relatedPerceroObject, false, userId); +// dataProvider.populateToOneRelationships(relatedPerceroObject, false, userId); +// } +// +// // Make sure the reverse relationship is mapped. +// if (nextToOneMappedField.getReverseMappedField() != null) { +// if (nextToOneMappedField.getReverseMappedField() instanceof MappedFieldPerceroObject) { +// // OneToOne, so just set reverse value. +// nextToOneMappedField.getReverseMappedField().getSetter().invoke(relatedPerceroObject, perceroObject); +// } +// else { +// // OneToMany/ManyToOne, so need to swap out of list. +// List reverseList = (List) nextToOneMappedField.getReverseMappedField().getValue(relatedPerceroObject); +// if (reverseList == null) { +// reverseList = new ArrayList(); +// nextToOneMappedField.getReverseMappedField().getSetter().invoke(relatedPerceroObject, reverseList); +// reverseList.add(perceroObject); +// } +// else { +// boolean objectFound = false; +// for(int i=0; i allRelatedObjects = findAllRelatedObjects(perceroObject, nextToOneMappedField, shellOnly, userId); + IPerceroObject relatedPerceroObject = null; + if (allRelatedObjects != null && !allRelatedObjects.isEmpty()) { + relatedPerceroObject = allRelatedObjects.get(0); + } + nextToOneMappedField.getSetter().invoke(perceroObject, relatedPerceroObject); + } + } + } + + protected void addObjectToCache(IPerceroObject nextPerceroObject) { + String key = RedisKeyUtils.classIdPair(nextPerceroObject.getClass().getCanonicalName(), nextPerceroObject.getID()); + if (cacheTimeout > 0) + cacheDataStore.setValue(key, ((BaseDataObject)nextPerceroObject).toJson()); + + // (Re)Set the expiration. + if (cacheTimeout > 0 && key != null) { + cacheDataStore.expire(key, cacheTimeout, TimeUnit.SECONDS); + } + } + +} diff --git a/src/main/java/com/percero/agents/sync/services/DataProviderManager.java b/src/main/java/com/percero/agents/sync/services/DataProviderManager.java index b64419a..9165e3c 100644 --- a/src/main/java/com/percero/agents/sync/services/DataProviderManager.java +++ b/src/main/java/com/percero/agents/sync/services/DataProviderManager.java @@ -10,6 +10,15 @@ @Component public class DataProviderManager implements IDataProviderManager { + + private static DataProviderManager instance = null; + public static DataProviderManager getInstance() { + return instance; + } + + public DataProviderManager() { + instance = this; + } @Autowired ApplicationContext appContext; @@ -38,10 +47,15 @@ public IDataProvider getDataProviderByName(String aName) { if (!dataProvidersByName.containsKey(aName)) { // Attempt to get the bean from the ApplicationContext. - IDataProvider dataProvider = (IDataProvider) appContext.getBean(aName); - dataProvider.initialize(); - addDataProvider(dataProvider); - return dataProvider; + try { + IDataProvider dataProvider = (IDataProvider) appContext.getBean(aName); + dataProvider.initialize(); + addDataProvider(dataProvider); + return dataProvider; + } catch(Exception e) { + // If no data provider is found, then assume the default. + return defaultDataProvider; + } } else return dataProvidersByName.get(aName); } diff --git a/src/main/java/com/percero/agents/sync/services/HibernateDataProvider.java b/src/main/java/com/percero/agents/sync/services/HibernateDataProvider.java new file mode 100644 index 0000000..b962733 --- /dev/null +++ b/src/main/java/com/percero/agents/sync/services/HibernateDataProvider.java @@ -0,0 +1,1688 @@ +//package com.percero.agents.sync.services; +// +//import java.lang.reflect.Method; +//import java.util.ArrayList; +//import java.util.Collection; +//import java.util.Date; +//import java.util.HashMap; +//import java.util.HashSet; +//import java.util.Iterator; +//import java.util.List; +//import java.util.Map; +//import java.util.Set; +//import java.util.UUID; +//import java.util.concurrent.TimeUnit; +// +//import javassist.ClassPool; +//import javassist.CtClass; +//import javassist.CtField; +//import javassist.CtMethod; +// +//import org.apache.log4j.Logger; +//import org.codehaus.jackson.map.ObjectMapper; +//import org.hibernate.Criteria; +//import org.hibernate.PropertyValueException; +//import org.hibernate.Query; +//import org.hibernate.QueryException; +//import org.hibernate.Session; +//import org.hibernate.SessionFactory; +//import org.hibernate.Transaction; +//import org.hibernate.criterion.Criterion; +//import org.hibernate.criterion.MatchMode; +//import org.hibernate.criterion.Restrictions; +//import org.hibernate.impl.SessionImpl; +//import org.hibernate.type.Type; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.stereotype.Component; +//import org.springframework.util.StringUtils; +// +//import com.percero.agents.sync.access.RedisKeyUtils; +//import com.percero.agents.sync.datastore.ICacheDataStore; +//import com.percero.agents.sync.exceptions.SyncDataException; +//import com.percero.agents.sync.exceptions.SyncException; +//import com.percero.agents.sync.hibernate.AssociationExample; +//import com.percero.agents.sync.hibernate.BaseDataObjectPropertySelector; +//import com.percero.agents.sync.hibernate.SyncHibernateUtils; +//import com.percero.agents.sync.metadata.IMappedClassManager; +//import com.percero.agents.sync.metadata.JpqlQuery; +//import com.percero.agents.sync.metadata.MappedClass; +//import com.percero.agents.sync.metadata.MappedClassManagerFactory; +//import com.percero.agents.sync.metadata.MappedField; +//import com.percero.agents.sync.metadata.MappedFieldList; +//import com.percero.agents.sync.metadata.MappedFieldMap; +//import com.percero.agents.sync.metadata.MappedFieldPerceroObject; +//import com.percero.agents.sync.vo.BaseDataObject; +//import com.percero.agents.sync.vo.ClassIDPair; +//import com.percero.agents.sync.vo.ClassIDPairs; +//import com.percero.agents.sync.vo.IJsonObject; +//import com.percero.agents.sync.vo.IRootObject; +//import com.percero.framework.metadata.IMappedQuery; +//import com.percero.framework.vo.IPerceroObject; +//import com.percero.framework.vo.PerceroList; +// +//@Component +//public class HibernateDataProvider implements IDataProvider { +// +// // TODO: Better manage Hibernate Sessions (opening and closing). +// +// private static final Logger log = Logger.getLogger(HibernateDataProvider.class); +// +// public void initialize() +// { +// // Do nothing. +// } +// +// public String getName() { +// return "syncAgent"; +// } +// +// @Autowired +// ICacheDataStore cacheDataStore; +// +// @Autowired +// Long cacheTimeout = Long.valueOf(60 * 60 * 24 * 14); // Two weeks +// +// @Autowired +// ObjectMapper safeObjectMapper; +// +// @Autowired +// SessionFactory appSessionFactory; +// public void setAppSessionFactory(SessionFactory value) { +// appSessionFactory = value; +// } +// +// +// @SuppressWarnings({ "rawtypes", "unchecked" }) +// // TODO: @Transactional(readOnly=true) +// public PerceroList getAllByName(Object aName, Integer pageNumber, Integer pageSize, Boolean returnTotal, String userId) throws Exception { +// Session s = appSessionFactory.openSession(); +// try { +// returnTotal = true; +// String aClassName = aName.toString(); +// Query countQuery = null; +// Query query = null; +// Class theClass = MappedClass.forName(aName.toString()); +// +// IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); +// MappedClass mappedClass = mcm.getMappedClassByClassName(aClassName); +// boolean isValidReadQuery = false; +// if (mappedClass != null) { +// if (mappedClass.getReadQuery() != null && StringUtils.hasText(mappedClass.getReadQuery().getQuery())) { +// isValidReadQuery = true; +// } +// } +// +// /** +// * readAllQuery optimization +// * You can now define a readAllQuery on a class to imporove it's initial download time +// * for briefcase mode. +// * +// * Only supports plain SQL for now +// */ +// if(mappedClass.getReadAllQuery() != null && mappedClass.getReadAllQuery().getQuery() != null && !mappedClass.getReadAllQuery().getQuery().isEmpty()){ +// if((mappedClass.getReadAllQuery() instanceof JpqlQuery)){ +// throw new IllegalArgumentException("Illegal query type on:"+aClassName+". readAllQueries must be plain SQL. JPQL, HQL not supported yet. "); +// } +// +// log.debug("Using readAllQuery: "+aClassName); +// String selectQueryString = mappedClass.getReadAllQuery().getQuery(); +// +// String countQueryString = "select count(*) from ("+selectQueryString+") as U"; +// +// // Add the limit clause +// if (pageSize != null && pageNumber != null && pageSize.intValue() > 0) { +// int offset = pageSize.intValue() * pageNumber.intValue(); +// selectQueryString += " limit "+pageSize+" OFFSET "+offset; +// } +// +// query = s.createSQLQuery(selectQueryString).addEntity(theClass); +// countQuery = s.createSQLQuery(countQueryString); +// +// query.setParameter("userId", userId); +// countQuery.setParameter("userId", userId); +// } +// else +// if (theClass != null) { +// String countQueryString = ""; +// if (returnTotal) +// countQueryString = "SELECT COUNT(getAllResult.ID) FROM " + aClassName + " getAllResult"; +// String queryString = "SELECT getAllResult FROM " + aClassName + " getAllResult"; +// +// // If the Read Query/Filter uses the ID, then we need to check against each ID here. +// if (isValidReadQuery) { +// String queryFilterString = mappedClass.getReadQuery().getQuery(); +// if (mappedClass.getReadQuery().getUseId()) { +// // Need to replace :id with +// queryFilterString = queryFilterString.replaceAll(":id", "getAllResult.ID"); +// queryFilterString = queryFilterString.replaceAll(":Id", "getAllResult.ID"); +// queryFilterString = queryFilterString.replaceAll(":iD", "getAllResult.ID"); +// queryFilterString = queryFilterString.replaceAll(":ID", "getAllResult.ID"); +// } +// String countQueryFilterString = ""; +// if (returnTotal) +// countQueryFilterString = queryFilterString; +// queryFilterString = queryString + " WHERE (" + queryFilterString + ") > 0 ORDER BY getAllResult.ID"; +// +// if (returnTotal) { +// countQueryFilterString = countQueryString + " WHERE (" + countQueryFilterString + ") > 0"; +// countQuery = s.createQuery(countQueryFilterString); +// } +// query = s.createQuery(queryFilterString); +// mappedClass.getReadQuery().setQueryParameters(query, null, userId); +// mappedClass.getReadQuery().setQueryParameters(countQuery, null, userId); +// } +// else { +// queryString += " ORDER BY getAllResult.ID"; +// if (returnTotal) { +// countQueryString += " ORDER BY ID"; +// countQuery = s.createQuery(countQueryString); +// } +// query = s.createQuery(queryString); +// } +// if (pageSize != null && pageNumber != null && pageSize.intValue() > 0) { +// int pageValue = pageSize.intValue() * pageNumber.intValue(); +// query.setFirstResult(pageValue); +// query.setMaxResults(pageSize.intValue()); +// } +// } +// +// if (query != null) { +// +// log.debug("Get ALL: "+aClassName); +// long t1 = new Date().getTime(); +// List list = query.list(); +// long t2 = new Date().getTime(); +// log.debug("Query Time: "+(t2-t1)); +// PerceroList result = new PerceroList( (List) SyncHibernateUtils.cleanObject(list, s, userId) ); +// long t3 = new Date().getTime(); +// log.debug("Clean Time: "+(t3-t2)); +// +// result.setPageNumber(pageNumber); +// result.setPageSize(pageSize); +// +// if (returnTotal && pageSize != null && pageNumber != null && pageSize.intValue() > 0){ +// result.setTotalLength(((Number)countQuery.uniqueResult()).intValue()); +// log.debug("Total Obs: "+result.getTotalLength()); +// } +// else +// result.setTotalLength(result.size()); +// return result; +// } +// else { +// return null; +// } +// +// } catch (Exception e) { +// log.error("Unable to getAllByName", e); +// } finally { +// s.close(); +// } +// +// return null; +// } +// +// @SuppressWarnings({ "rawtypes" }) +// // TODO: @Transactional(readOnly=true) +// public Integer countAllByName(String aClassName, String userId) throws Exception { +// Session s = appSessionFactory.openSession(); +// try { +// Query countQuery = null; +// Class theClass = MappedClass.forName(aClassName); +// +// IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); +// MappedClass mappedClass = mcm.getMappedClassByClassName(aClassName); +// boolean isValidReadQuery = false; +// if (mappedClass != null) { +// if (mappedClass.getReadQuery() != null && StringUtils.hasText(mappedClass.getReadQuery().getQuery())) { +// isValidReadQuery = true; +// } +// } +// +// if(mappedClass.getReadAllQuery() != null && mappedClass.getReadAllQuery().getQuery() != null && !mappedClass.getReadAllQuery().getQuery().isEmpty()){ +// if((mappedClass.getReadAllQuery() instanceof JpqlQuery)){ +// throw new IllegalArgumentException("Illegal query type on:"+aClassName+". readAllQueries must be plain SQL. JPQL, HQL not supported yet. "); +// } +// +// String selectQueryString = mappedClass.getReadAllQuery().getQuery(); +// +// String countQueryString = "select count(*) from ("+selectQueryString+") as U"; +// +// countQuery = s.createSQLQuery(countQueryString); +// countQuery.setParameter("userId", userId); +// } +// else if (theClass != null) { +// String countQueryString = "SELECT COUNT(getAllResult.ID) FROM " + aClassName + " getAllResult"; +// +// // If the Read Query/Filter uses the ID, then we need to check against each ID here. +// if (isValidReadQuery) { +// String queryFilterString = mappedClass.getReadQuery().getQuery(); +// if (mappedClass.getReadQuery().getUseId()) { +// // Need to replace :id with +// queryFilterString = queryFilterString.replaceAll(":id", "getAllResult.ID"); +// queryFilterString = queryFilterString.replaceAll(":Id", "getAllResult.ID"); +// queryFilterString = queryFilterString.replaceAll(":iD", "getAllResult.ID"); +// queryFilterString = queryFilterString.replaceAll(":ID", "getAllResult.ID"); +// } +// String countQueryFilterString = queryFilterString; +// countQueryFilterString = countQueryString + " WHERE (" + countQueryFilterString + ") > 0"; +// +// countQuery = s.createQuery(countQueryFilterString); +// mappedClass.getReadQuery().setQueryParameters(countQuery, null, userId); +// } +// else { +// countQueryString += " ORDER BY ID"; +// countQuery = s.createQuery(countQueryString); +// } +// } +// +// if (countQuery != null) { +//// log.debug(countQuery.toString()); +// Integer result = ((Number)countQuery.uniqueResult()).intValue(); +// return result; +// } +// else { +// return null; +// } +// +// } catch (Exception e) { +// log.error("Unable to countAllByName", e); +// } finally { +// s.close(); +// } +// +// return 0; +// } +// +// public List runQuery(MappedClass mappedClass, String queryName, Object[] queryArguments, String userId) { +// Session s = appSessionFactory.openSession(); +// try { +// if (mappedClass != null) { +// for(IMappedQuery nextQuery : mappedClass.queries) +// { +// if (queryName.equalsIgnoreCase(nextQuery.getQueryName())) +// { +// Query readFilter = s.createQuery(nextQuery.getQuery()); +// nextQuery.setQueryParameters(readFilter, queryArguments, userId, queryArguments, (SessionImpl)s); +// return processQueryResults(mappedClass.className + "_" + queryName, readFilter, readFilter.list()); +// } +// } +// } +// } catch (Exception e) { +// log.error("Unable to runQuery", e); +// } finally { +// if (s != null && s.isOpen()) +// s.close(); +// } +// +// return null; +// } +// +// @SuppressWarnings({ "rawtypes", "unchecked" }) +// protected static List processQueryResults(String resultClassName, Query updateFilter, List updateFilterResult) throws Exception { +// String[] returnAliases = updateFilter.getReturnAliases(); +// Type[] returnTypes = updateFilter.getReturnTypes(); +// +// String[] fieldNames = new String[returnAliases.length]; +// String[] getMethodNames = new String[returnAliases.length]; +// String[] setMethodNames = new String[returnAliases.length]; +// Class[] fieldClasses = new Class[returnAliases.length]; +// +// Class clazz = null; +// ClassPool pool = null; +// CtClass evalClass = null; +// +// if (returnAliases.length > 1) { +// try { +// clazz = MappedClass.forName(resultClassName); +// } catch(Exception e) { +// // Class must not yet exist, so let's create it. +// } +// +// if (clazz == null) { +// pool = ClassPool.getDefault(); +// evalClass = pool.makeClass(resultClassName); +// } +// +// // Create a new Class based on the result set. +// for(int i = 0; i < returnAliases.length; i++) { +// Type nextType = returnTypes[i]; +// String nextTypeCanonicalName = nextType.getReturnedClass().getCanonicalName(); +// String nextFieldName = returnAliases[i]; +// try { +// Integer.parseInt(nextFieldName); +// nextFieldName = "field" + i; +// } catch(NumberFormatException nfe) { +// // Do nothing. Simply means the field name is not a Number. +// } +// String nextUpperFieldName = nextFieldName.substring(0, 1).toUpperCase() + nextFieldName.substring(1); +// +// fieldNames[i] = nextFieldName; +// getMethodNames[i] = "get" + nextUpperFieldName; +// setMethodNames[i] = "set" + nextUpperFieldName; +// fieldClasses[i] = nextType.getReturnedClass(); +// +// if (evalClass != null) { +// evalClass.addField(CtField.make("private " + fieldClasses[i].getCanonicalName() + " " + nextFieldName + ";", evalClass)); +// evalClass.addMethod(CtMethod.make("public void " + setMethodNames[i] + "(" + fieldClasses[i].getCanonicalName() + " value) {this." + nextFieldName + " = value;}", evalClass)); +// evalClass.addMethod(CtMethod.make("public " + nextTypeCanonicalName +" " + getMethodNames[i] + "() {return this." + nextFieldName + ";}", evalClass)); +// } +// } +// +// if (clazz == null && evalClass != null) { +// clazz = evalClass.toClass(); +// } +// } +// +// List results = new ArrayList(); +// +// // Now populate the newly created objects. +// for(Object nextResult : (List)updateFilterResult) { +// +// if (nextResult instanceof Object[]) { +// Object nextObject = clazz.newInstance(); +// for(int i = 0; i < returnAliases.length; i++) { +// Class[] formalParams = new Class[] { fieldClasses[i] }; +// Method setMethod = clazz.getDeclaredMethod(setMethodNames[i], formalParams); +// setMethod.invoke(nextObject, ((Object[])nextResult)[i]); +// } +// +// results.add(nextObject); +// } else +// results.add(nextResult); +// } +// +// return results; +// } +// +// //@SuppressWarnings({ "rawtypes", "unchecked" }) +// public IPerceroObject findById(ClassIDPair classIdPair, String userId) { +// boolean hasReadQuery = false; +// IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); +// MappedClass mappedClass = mcm.getMappedClassByClassName(classIdPair.getClassName()); +// if (mappedClass != null) { +// if (mappedClass.getReadQuery() != null && StringUtils.hasText(mappedClass.getReadQuery().getQuery())) { +// hasReadQuery = true; +// } +// } +// +// Session s = null; +// try { +// boolean hasAccess = true; +// IPerceroObject result = systemGetById(classIdPair); +// +// if (result != null) { +// if (hasReadQuery) { +// if (s == null) +// s = appSessionFactory.openSession(); +// Query readFilter = s.createQuery(mappedClass.getReadQuery().getQuery()); +// mappedClass.getReadQuery().setQueryParameters(readFilter, result, userId); +// Number readFilterResult = (Number) readFilter.uniqueResult(); +// if (readFilterResult == null || readFilterResult.intValue() <= 0) +// hasAccess = false; +// } +// +// if (hasAccess) { +// if (s == null) +// s = appSessionFactory.openSession(); +//// ((BaseDataObject)result).setIsClean(false); +// result = (IPerceroObject) SyncHibernateUtils.cleanObject(result, s, userId); +// return result; +// } +// else { +// return null; +// } +// } +// else { +// return null; +// } +// } catch (Exception e) { +// log.error("Unable to findById", e); +// } finally { +// if (s != null && s.isOpen()) +// s.close(); +// } +// return null; +// } +// +// @SuppressWarnings({ "rawtypes", "unchecked" }) +// public IPerceroObject systemGetById(ClassIDPair classIdPair) { +// Session s = null; +// try { +// Class theClass = MappedClass.forName(classIdPair.getClassName().toString()); +// +// if (theClass != null) { +// IPerceroObject result = retrieveFromRedisCache(classIdPair); +// String key = null; +// +// if (result == null) { +// s = appSessionFactory.openSession(); +// result = (IPerceroObject) s.get(theClass, classIdPair.getID()); +// +// // Now put the object in the cache. +// if (result != null) { +// key = RedisKeyUtils.classIdPair(result.getClass().getCanonicalName(), result.getID()); +// result = (IPerceroObject) SyncHibernateUtils.cleanObject(result, s); +// if (cacheTimeout > 0) +// cacheDataStore.setValue(key, ((BaseDataObject)result).toJson()); +// } +// else { +// // Not necessarily a problem but could be helpful when debugging. +// log.debug("Unable to retrieve object from database: " + classIdPair.toJson()); +// } +// } +// else { +// key = RedisKeyUtils.classIdPair(result.getClass().getCanonicalName(), result.getID()); +// } +// +// // (Re)Set the expiration. +// if (cacheTimeout > 0 && key != null) { +// cacheDataStore.expire(key, cacheTimeout, TimeUnit.SECONDS); +// } +// +// return result; +// } else { +// return null; +// } +// } catch (Exception e) { +// log.error("Unable to systemGetById: "+classIdPair.toJson(), e); +// } finally { +// if (s != null && s.isOpen()) +// s.close(); +// } +// return null; +// } +// +// @SuppressWarnings({ "rawtypes" }) +// public IPerceroObject systemGetByIdWithClassAndSession(ClassIDPair classIdPair, Class theClass, Session s) { +// Boolean sessionAlreadyOpen = false; +// try { +// if (theClass != null) { +// IPerceroObject result = retrieveFromRedisCache(classIdPair); +// String key = null; +// +// if (result == null) { +// if (s == null || !s.isOpen()) { +// s = appSessionFactory.openSession(); +// } +// else { +// sessionAlreadyOpen = true; +// } +// result = (IPerceroObject) s.get(theClass, classIdPair.getID()); +// +// // Now put the object in the cache. +// if (result != null) { +// key = RedisKeyUtils.classIdPair(result.getClass().getCanonicalName(), result.getID()); +// result = (IPerceroObject) SyncHibernateUtils.cleanObject(result, s); +// if (cacheTimeout > 0) +// cacheDataStore.setValue(key, ((BaseDataObject)result).toJson()); +// } +// else { +// log.warn("Unable to retrieve object from database: " + classIdPair.toString()); +// } +// } +// else { +// key = RedisKeyUtils.classIdPair(result.getClass().getCanonicalName(), result.getID()); +// } +// +// // (Re)Set the expiration. +// if (cacheTimeout > 0 && key != null) { +// cacheDataStore.expire(key, cacheTimeout, TimeUnit.SECONDS); +// } +// +// return result; +// } else { +// return null; +// } +// } catch (Exception e) { +// log.error("Unable to systemGetById: "+classIdPair.toJson(), e); +// } finally { +// // Only close the session if it wasn't already open. +// if (!sessionAlreadyOpen) { +// if (s != null && s.isOpen()) { +// s.close(); +// } +// } +// } +// return null; +// } +// +// @SuppressWarnings({ "rawtypes", "unchecked" }) +// private IPerceroObject retrieveFromRedisCache(ClassIDPair classIdPair) throws Exception { +// IPerceroObject result = null; +// if (cacheTimeout > 0) { +// IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); +// Class theClass = MappedClass.forName(classIdPair.getClassName()); +// MappedClass mc = mcm.getMappedClassByClassName(classIdPair.getClassName()); +// +// String key = RedisKeyUtils.classIdPair(classIdPair.getClassName(), classIdPair.getID()); +// String jsonObjectString = (String) cacheDataStore.getValue(key); +// if (jsonObjectString != null) { +// if (IJsonObject.class.isAssignableFrom(theClass)) { +// IJsonObject jsonObject = (IJsonObject) theClass.newInstance(); +// jsonObject.fromJson(jsonObjectString); +// result = (IPerceroObject) jsonObject; +// } +// else { +// result = (IPerceroObject) safeObjectMapper.readValue(jsonObjectString, theClass); +// } +// } +// else { +// // Check MappedClass' child classes. +// Iterator itrChildMappedClasses = mc.childMappedClasses.iterator(); +// while (itrChildMappedClasses.hasNext()) { +// MappedClass nextChildMc = itrChildMappedClasses.next(); +// key = RedisKeyUtils.classIdPair(nextChildMc.className, classIdPair.getID()); +// jsonObjectString = (String) cacheDataStore.getValue(key); +// if (jsonObjectString != null) { +// result = (IPerceroObject) safeObjectMapper.readValue(jsonObjectString, theClass); +// return result; +// } +// } +// } +// } +// +// return result; +// } +// +// @SuppressWarnings({ "rawtypes", "unchecked" }) +// private Map retrieveFromRedisCache(ClassIDPairs classIdPairs, Boolean pleaseSetTimeout) throws Exception { +// Map result = new HashMap(classIdPairs.getIds().size()); +// +// if (cacheTimeout > 0) { +// IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); +// Class theClass = MappedClass.forName(classIdPairs.getClassName()); +// MappedClass mc = mcm.getMappedClassByClassName(classIdPairs.getClassName()); +// +// Set keys = new HashSet(classIdPairs.getIds().size()); +// Iterator itrIds = classIdPairs.getIds().iterator(); +// while (itrIds.hasNext()) { +// String nextId = itrIds.next(); +// String nextKey = RedisKeyUtils.classIdPair(classIdPairs.getClassName(), nextId); +// keys.add(nextKey); +// +// // Check MappedClass' child classes. +// Iterator itrChildMappedClasses = mc.childMappedClasses.iterator(); +// while (itrChildMappedClasses.hasNext()) { +// MappedClass nextChildMc = itrChildMappedClasses.next(); +// if (nextChildMc.clazz == BaseDataObject.class) { +// // Reached the top level, so break. +// break; +// } +// nextKey = RedisKeyUtils.classIdPair(nextChildMc.className, nextId); +// keys.add(nextKey); +// } +// } +// +//// String key = RedisKeyUtils.classIdPair(classIdPair.getClassName(), classIdPair.getID()); +// List jsonObjectStrings = cacheDataStore.getValues(keys); +// Iterator itrJsonObjectStrings = jsonObjectStrings.iterator(); +// while (itrJsonObjectStrings.hasNext()) { +// String jsonObjectString = (String) itrJsonObjectStrings.next(); +// if (jsonObjectString != null) { +// if (IJsonObject.class.isAssignableFrom(theClass)) { +// IJsonObject jsonObject = (IJsonObject) theClass.newInstance(); +// jsonObject.fromJson(jsonObjectString); +// result.put( (((IPerceroObject) jsonObject).getID()), (IPerceroObject) jsonObject ); +// } +// else { +// IPerceroObject nextPerceroObject = (IPerceroObject) safeObjectMapper.readValue(jsonObjectString, theClass); +// result.put( nextPerceroObject.getID(), nextPerceroObject); +// } +// +// } +//// else { +//// // Check MappedClass' child classes. +//// Iterator itrChildMappedClasses = mc.childMappedClasses.iterator(); +//// while (itrChildMappedClasses.hasNext()) { +//// MappedClass nextChildMc = itrChildMappedClasses.next(); +//// key = RedisKeyUtils.classIdPair(nextChildMc.className, classIdPair.getID()); +//// jsonObjectString = (String) redisDataStore.getValue(key); +//// if (jsonObjectString != null) { +//// result = (IPerceroObject) safeObjectMapper.readValue(jsonObjectString, theClass); +//// return result; +//// } +//// } +//// } +// } +// +// if (pleaseSetTimeout) { +// cacheDataStore.expire(keys, cacheTimeout, TimeUnit.SECONDS); +// } +// } +// +// return result; +// } +// +// @SuppressWarnings({ "rawtypes" }) +// public Boolean getReadAccess(ClassIDPair classIdPair, String userId) { +// Session s = appSessionFactory.openSession(); +// try { +// Class theClass = MappedClass.forName(classIdPair.getClassName().toString()); +// +// if (theClass != null) { +// IPerceroObject parent = (IPerceroObject) s.get(theClass, classIdPair.getID()); +// +// boolean hasAccess = (parent != null); +// +// if (hasAccess) { +// IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); +// MappedClass mappedClass = mcm.getMappedClassByClassName(classIdPair.getClassName()); +// if (mappedClass != null) { +// if (mappedClass.getReadQuery() != null && StringUtils.hasText(mappedClass.getReadQuery().getQuery())){ +// Query readFilter = s.createQuery(mappedClass.getReadQuery().getQuery()); +// mappedClass.getReadQuery().setQueryParameters(readFilter, parent, userId); +// Number readFilterResult = (Number) readFilter.uniqueResult(); +// if (readFilterResult == null || readFilterResult.intValue() <= 0) +// hasAccess = false; +// } +// } +// } +// +// return hasAccess; +// } +// } catch (Exception e) { +// log.error("Unable to getReadAccess", e); +// } finally { +// s.close(); +// } +// return false; +// } +// +// @SuppressWarnings({ "rawtypes" }) +// public Boolean getDeleteAccess(ClassIDPair classIdPair, String userId) { +// Session s = appSessionFactory.openSession(); +// try { +// Class theClass = MappedClass.forName(classIdPair.getClassName().toString()); +// +// if (theClass != null) { +// IPerceroObject parent = (IPerceroObject) s.get(theClass, classIdPair.getID()); +// +// if (parent == null) +// return true; +// +// boolean hasAccess = true; +// IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); +// MappedClass mappedClass = mcm.getMappedClassByClassName(classIdPair.getClassName()); +// if (mappedClass != null) { +// if (mappedClass.getDeleteQuery() != null && StringUtils.hasText(mappedClass.getDeleteQuery().getQuery())){ +// Query deleteFilter = s.createQuery(mappedClass.getDeleteQuery().getQuery()); +// mappedClass.getDeleteQuery().setQueryParameters(deleteFilter, parent, userId); +// Number deleteFilterResult = (Number) deleteFilter.uniqueResult(); +// if (deleteFilterResult == null || deleteFilterResult.intValue() <= 0) +// hasAccess = false; +// } +// } +// +// return hasAccess; +// } +// } catch (Exception e) { +// log.error("Unable to getDeleteAccess", e); +// } finally { +// s.close(); +// } +// return false; +// } +// +// @SuppressWarnings({ "rawtypes", "unchecked" }) +// public List findByIds(ClassIDPairs classIdPairs, String userId) { +// List result = null; +// +// boolean hasAccess = true; +// Class theClass = null; +// try { +// theClass = MappedClass.forName(classIdPairs.getClassName()); +// } catch (ClassNotFoundException e2) { +// log.error("Unable to get Class from class name " + classIdPairs.getClassName(), e2); +// return result; +// } +// +// IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); +// MappedClass mappedClass = mcm.getMappedClassByClassName(classIdPairs.getClassName()); +// boolean isValidReadQuery = false; +// if (mappedClass != null) { +// if (mappedClass.getReadQuery() != null && StringUtils.hasText(mappedClass.getReadQuery().getQuery())) { +// isValidReadQuery = true; +// } +// +// } +// +// if (hasAccess) { +// if (theClass != null) { +// Set idsSet = new HashSet(classIdPairs.getIds().size()); +// idsSet.addAll(classIdPairs.getIds()); +// +// try { +// // Attempt to get as many from the cache as possible... +// Map cachedObjects = retrieveFromRedisCache(classIdPairs, true); +// +// if (!cachedObjects.isEmpty()) { +// result = new ArrayList(cachedObjects.size()); +// List cachedResults = new ArrayList(cachedObjects.size()); +// cachedResults.addAll(cachedObjects.values()); +// idsSet.removeAll(cachedObjects.keySet()); +// +// // If there is a read query, we need to check each object through that query. +// if (isValidReadQuery) { +// Session s = null; +// try { +// // Need to clean each result for the user. +// // TODO: Need to figure out how to NOT open the Session if we don't have to. The problem lies in the SyncHibernateUtils.cleanObject function +// s = appSessionFactory.openSession(); +// +// Iterator itrCachedResults = cachedResults.iterator(); +// while (itrCachedResults.hasNext()) { +// IPerceroObject nextCachedResult = itrCachedResults.next(); +// if (isValidReadQuery) { +// Query readFilter = s.createQuery(mappedClass.getReadQuery().getQuery()); +// mappedClass.getReadQuery().setQueryParameters(readFilter, nextCachedResult, userId); +// Number readFilterResult = (Number) readFilter.uniqueResult(); +// if (readFilterResult == null || readFilterResult.intValue() <= 0) +// hasAccess = false; +// } +// +// if (hasAccess) { +// result.add( (IPerceroObject) SyncHibernateUtils.cleanObject(nextCachedResult, s, userId) ); +// } +// } +// } catch (Exception e) { +// log.error("Error cleaning object for user in findByIds", e); +// } finally { +// if (s != null && s.isOpen()) { +// s.close(); +// } +// } +// } +// else { +// // This class has relationship objects that need to be cleaned. +// if (!mappedClass.getReadAccessRightsFieldReferences().isEmpty()) { +// Session s = null; +// try { +// // Need to clean each result for the user. +// // TODO: Need to figure out how to NOT open the Session if we don't have to. The problem lies in the SyncHibernateUtils.cleanObject function +// // TODO: Maybe only clean relationships that are in mappedClass.readAccessRightsFieldReferences? +// s = appSessionFactory.openSession(); +// +// Iterator itrCachedResults = cachedResults.iterator(); +// while (itrCachedResults.hasNext()) { +// IPerceroObject nextCachedResult = itrCachedResults.next(); +// result.add( (IPerceroObject) SyncHibernateUtils.cleanObject(nextCachedResult, s, userId) ); +// } +// } catch (Exception e) { +// log.error("Error cleaning object for user in findByIds", e); +// } finally { +// if (s != null && s.isOpen()) { +// s.close(); +// } +// } +// } +// else { +// // This class has NO read access query AND has NO relationships that require cleaning, +// // since this object is completely retrieved from redis, we can send as-is. +// // Though this may seem rare, this is probably a very common path. +// result.addAll(cachedResults); +// } +// } +// } +// } catch (Exception e1) { +// // We errored out here, but we can still try and retrieve from the database. +// log.error("Error retrieving objects from redis cache, attempting to retrieve from database", e1); +// } +// +// // Now get the rest from the database. +// if (!idsSet.isEmpty()) { +// String queryString = "SELECT findByIdsResult FROM " + classIdPairs.getClassName() + " findByIdsResult WHERE findByIdsResult.ID IN (:idsSet)"; +// +// // Open the database session. +// Session s = null; +// try { +// s = appSessionFactory.openSession(); +// Query query = null; +// +// // If the Read Query/Filter uses the ID, then we need to check against each ID here. +// if (isValidReadQuery) { +// String queryFilterString = mappedClass.getReadQuery().getQuery(); +// if (mappedClass.getReadQuery().getUseId()) { +// // Need to replace :id with +// queryFilterString = queryFilterString.replaceAll(":id", "findByIdsResult.ID"); +// queryFilterString = queryFilterString.replaceAll(":Id", "findByIdsResult.ID"); +// queryFilterString = queryFilterString.replaceAll(":iD", "findByIdsResult.ID"); +// queryFilterString = queryFilterString.replaceAll(":ID", "findByIdsResult.ID"); +// } +// queryFilterString = queryString + " AND (" + queryFilterString + ") > 0"; +// +// query = s.createQuery(queryFilterString); +// mappedClass.getReadQuery().setQueryParameters(query, null, userId); +// } +// else { +// query = s.createQuery(queryString); +// } +// +// query.setParameterList("idsSet", idsSet); +// List queryResult = query.list(); +// List cleanedDatabaseObjects = (List) SyncHibernateUtils.cleanObject(queryResult, s, userId); +// +// // Need to put results into cache. +// if (cacheTimeout > 0) { +// Map mapJsonObjectStrings = new HashMap(cleanedDatabaseObjects.size()); +// Iterator itrDatabaseObjects = cleanedDatabaseObjects.iterator(); +// while (itrDatabaseObjects.hasNext()) { +// IPerceroObject nextDatabaseObject = itrDatabaseObjects.next(); +// String nextCacheKey = RedisKeyUtils.classIdPair(nextDatabaseObject.getClass().getCanonicalName(), nextDatabaseObject.getID()); +// +// mapJsonObjectStrings.put(nextCacheKey, ((BaseDataObject)nextDatabaseObject).toJson()); +// } +// +// // Store the objects in redis. +// cacheDataStore.setValues(mapJsonObjectStrings); +// // (Re)Set the expiration. +// cacheDataStore.expire(mapJsonObjectStrings.keySet(), cacheTimeout, TimeUnit.SECONDS); +// } +// +// if (result == null) { +// result = cleanedDatabaseObjects; +// } +// else { +// result.addAll(cleanedDatabaseObjects); +// } +// } catch (QueryException qe) { +// log.error("Unable to findByIds", qe); +// } catch (Exception e) { +// log.error("Unable to findByIds", e); +// } finally { +// if (s != null && s.isOpen()) { +// s.close(); +// } +// } +// } +// } +// } +// +// return result; +// } +// +// @SuppressWarnings({ "rawtypes", "unchecked" }) +// public IPerceroObject findUnique(IPerceroObject theQueryObject, String userId) { +// Session s = appSessionFactory.openSession(); +// try { +// Class objectClass = theQueryObject.getClass(); +// +// IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); +// MappedClass mappedClass = mcm.getMappedClassByClassName(objectClass.getName()); +// if (mappedClass != null) { +// Iterator itr = mappedClass.uniqueConstraints.iterator(); +// while(itr.hasNext()) { +// Criteria criteria = null; +// Object nextConstraint = itr.next(); +// if (nextConstraint instanceof MappedField) { +// MappedField nextConstraintField = (MappedField) nextConstraint; +// Object nextConstraintFieldValue = nextConstraintField.getValue(theQueryObject); +// if (nextConstraintFieldValue != null) { +// if (criteria == null) +// criteria = s.createCriteria(objectClass); +// Criterion uniqueFieldValue = Restrictions.eq(nextConstraintField.getField().getName(), nextConstraintFieldValue); +// criteria.add(uniqueFieldValue); +// } +// } +// else if (nextConstraint instanceof List) { +// List listMappedFields = (List) nextConstraint; +// Iterator itrMappedFields = listMappedFields.iterator(); +// while(itrMappedFields.hasNext()) { +// MappedField nextConstraintField = itrMappedFields.next(); +// Object nextConstraintFieldValue = nextConstraintField.getValue(theQueryObject); +// if (nextConstraintFieldValue != null) { +// if (criteria == null) +// criteria = s.createCriteria(objectClass); +// Criterion uniqueFieldValue = Restrictions.eq(nextConstraintField.getField().getName(), nextConstraintFieldValue); +// criteria.add(uniqueFieldValue); +// } +// } +// } +// +// if (criteria != null) { +// criteria.setMaxResults(1); +// Object result = criteria.uniqueResult(); +// if (result != null) { +// // Make sure user has access. +// boolean hasAccess = true; +// if (mappedClass != null) { +// if (mappedClass.getReadQuery() != null && StringUtils.hasText(mappedClass.getReadQuery().getQuery())){ +// Query readFilter = s.createQuery(mappedClass.getReadQuery().getQuery()); +// mappedClass.getReadQuery().setQueryParameters(readFilter, result, userId); +// Number readFilterResult = (Number) readFilter.uniqueResult(); +// if (readFilterResult == null || readFilterResult.intValue() <= 0) +// hasAccess = false; +// } +// } +// +// if (hasAccess) { +// result = SyncHibernateUtils.cleanObject(result, s, userId); +// return (IPerceroObject) result; +// } +// } +// } +// } +// } +// } catch (Exception e) { +// log.error("Unable to findByExample", e); +// } finally { +// s.close(); +// } +// +// return null; +// } +// +// // TODO: Add permissions check. +// @SuppressWarnings({ "unchecked" }) +// public List findByExample(IPerceroObject theQueryObject, List excludeProperties, String userId) { +// Session s = appSessionFactory.openSession(); +// try { +// Criteria criteria = s.createCriteria(theQueryObject.getClass()); +// AssociationExample example = AssociationExample.create(theQueryObject); +// BaseDataObjectPropertySelector propertySelector = new BaseDataObjectPropertySelector(excludeProperties); +// example.setPropertySelector(propertySelector); +// criteria.add(example); +// +// List result = criteria.list(); +// result = (List) SyncHibernateUtils.cleanObject(result, s, userId); +// +// return (List) result; +// } catch (Exception e) { +// log.error("Unable to findByExample", e); +// } finally { +// s.close(); +// } +// +// return null; +// } +// +// @SuppressWarnings({ "unchecked" }) +// public List systemFindByExample(IPerceroObject theQueryObject, List excludeProperties) { +// Session s = appSessionFactory.openSession(); +// try { +// Criteria criteria = s.createCriteria(theQueryObject.getClass()); +// AssociationExample example = AssociationExample.create(theQueryObject); +// BaseDataObjectPropertySelector propertySelector = new BaseDataObjectPropertySelector(excludeProperties); +// example.setPropertySelector(propertySelector); +// criteria.add(example); +// +// List result = criteria.list(); +// result = (List) SyncHibernateUtils.cleanObject(result, s); +// +// return (List) result; +// } catch (Exception e) { +// log.error("Unable to findByExample", e); +// } finally { +// s.close(); +// } +// +// return null; +// } +// +// @SuppressWarnings("unchecked") +// public List searchByExample(IPerceroObject theQueryObject, +// List excludeProperties, String userId) { +// Session s = appSessionFactory.openSession(); +// try { +// Criteria criteria = s.createCriteria(theQueryObject.getClass()); +// AssociationExample example = AssociationExample.create(theQueryObject); +// BaseDataObjectPropertySelector propertySelector = new BaseDataObjectPropertySelector(excludeProperties); +// example.setPropertySelector(propertySelector); +// example.enableLike(MatchMode.ANYWHERE); +// criteria.add(example); +// +// List result = criteria.list(); +// result = (List) SyncHibernateUtils.cleanObject(result, s, userId); +// +// return result; +// } catch (Exception e) { +// log.error("Unable to searchByExample", e); +// } finally { +// s.close(); +// } +// +// return null; +// } +// +// @SuppressWarnings({ "unchecked", "rawtypes" }) +// public IPerceroObject systemCreateObject(IPerceroObject perceroObject) +// throws SyncException { +// Session s = appSessionFactory.openSession(); +// +// try { +// // Make sure object has an ID. +// IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); +// MappedClass mappedClass = mcm.getMappedClassByClassName(perceroObject.getClass().getName()); +// if (!mappedClass.hasGeneratedId && !StringUtils.hasText(perceroObject.getID())) +// perceroObject.setID(UUID.randomUUID().toString()); +// else { +// // Check to see if item already exists. +// Object existingObject = s.get(perceroObject.getClass(), perceroObject.getID()); +// if (existingObject != null) +// { +// perceroObject = (IPerceroObject) SyncHibernateUtils.cleanObject(existingObject, s); +// deepCleanObject(perceroObject, mappedClass, s, null); +// return perceroObject; +// } +// } +// +// Transaction tx = s.beginTransaction(); +// tx.begin(); +// s.save(perceroObject); +// tx.commit(); +// +// s.refresh(perceroObject); +// +// perceroObject = (IPerceroObject) SyncHibernateUtils.cleanObject(perceroObject, s); +// deepCleanObject(perceroObject, mappedClass, s, null); +// +// // Now update the cache. +// // TODO: Field-level updates could be REALLY useful here. Would avoid A TON of UNNECESSARY work... +// if (cacheTimeout > 0) { +// String key = RedisKeyUtils.classIdPair(perceroObject.getClass().getCanonicalName(), perceroObject.getID()); +// if (cacheTimeout > 0) { +// cacheDataStore.setValue(key, ((BaseDataObject)perceroObject).toJson()); +// cacheDataStore.expire(key, cacheTimeout, TimeUnit.SECONDS); +// } +// +// Set keysToDelete = new HashSet(); +// MappedClass nextMappedClass = mcm.getMappedClassByClassName(perceroObject.getClass().getName()); +// Iterator itrToManyFields = nextMappedClass.toManyFields.iterator(); +// while(itrToManyFields.hasNext()) { +// MappedField nextMappedField = itrToManyFields.next(); +// Object fieldObject = nextMappedField.getGetter().invoke(perceroObject); +// if (fieldObject != null) { +// if (fieldObject instanceof IPerceroObject) { +// String nextKey = RedisKeyUtils.classIdPair(fieldObject.getClass().getCanonicalName(), ((IPerceroObject)fieldObject).getID()); +// keysToDelete.add(nextKey); +// } +// else if (fieldObject instanceof Collection) { +// Iterator itrFieldObject = ((Collection) fieldObject).iterator(); +// while(itrFieldObject.hasNext()) { +// Object nextListObject = itrFieldObject.next(); +// if (nextListObject instanceof IPerceroObject) { +// String nextKey = RedisKeyUtils.classIdPair(nextListObject.getClass().getCanonicalName(), ((IPerceroObject)nextListObject).getID()); +// keysToDelete.add(nextKey); +// } +// } +// } +// } +// } +// Iterator itrToOneFields = mappedClass.toOneFields.iterator(); +// while(itrToOneFields.hasNext()) { +// MappedField nextMappedField = itrToOneFields.next(); +// Object fieldObject = nextMappedField.getGetter().invoke(perceroObject); +// if (fieldObject != null) { +// if (fieldObject instanceof IPerceroObject) { +// String nextKey = RedisKeyUtils.classIdPair(fieldObject.getClass().getCanonicalName(), ((IPerceroObject)fieldObject).getID()); +// keysToDelete.add(nextKey); +// } +// else if (fieldObject instanceof Collection) { +// Iterator itrFieldObject = ((Collection) fieldObject).iterator(); +// while(itrFieldObject.hasNext()) { +// Object nextListObject = itrFieldObject.next(); +// if (nextListObject instanceof IPerceroObject) { +// String nextKey = RedisKeyUtils.classIdPair(nextListObject.getClass().getCanonicalName(), ((IPerceroObject)nextListObject).getID()); +// keysToDelete.add(nextKey); +// } +// } +// } +// } +// } +// +// if (!keysToDelete.isEmpty()) { +// cacheDataStore.deleteKeys(keysToDelete); +// // TODO: Do we simply delete the key? Or do we refetch the object here and update the key? +// //redisDataStore.setValue(nextKey, ((BaseDataObject)perceroObject).toJson()); +// } +// } +// +// return perceroObject; +// } +// catch(PropertyValueException pve) { +// log.error("Error creating object", pve); +// +// SyncDataException sde = new SyncDataException(SyncDataException.MISSING_REQUIRED_FIELD, SyncDataException.MISSING_REQUIRED_FIELD_CODE, "Missing required field " + pve.getPropertyName()); +// sde.fieldName = pve.getPropertyName(); +// throw sde; +// } +// catch(Exception e) { +// log.error("Error creating object", e); +// +// SyncDataException sde = new SyncDataException(SyncDataException.CREATE_OBJECT_ERROR, SyncDataException.CREATE_OBJECT_ERROR_CODE); +// throw sde; +// } +// finally { +// if (s != null && s.isOpen()) { +// s.close(); +// } +// } +// } +// +// private void deepCleanObject(IPerceroObject perceroObject, MappedClass mappedClass, Session s, String userId) { +// if (!(perceroObject instanceof IRootObject)) { +// for(MappedField nextMappedField : mappedClass.externalizableFields) { +// try { +// if (nextMappedField instanceof MappedFieldPerceroObject) { +// Object fieldValue = nextMappedField.getValue(perceroObject); +// if (fieldValue != null) { +// fieldValue = SyncHibernateUtils.cleanObject(fieldValue, s, userId); +// nextMappedField.getSetter().invoke(perceroObject, fieldValue); +// } +// } +// else if (nextMappedField instanceof MappedFieldList) { +// Object fieldValue = nextMappedField.getValue(perceroObject); +// if (fieldValue != null) { +// fieldValue = SyncHibernateUtils.cleanObject(fieldValue, s, userId); +// nextMappedField.getSetter().invoke(perceroObject, fieldValue); +// } +// } +// else if (nextMappedField instanceof MappedFieldMap) { +// Object fieldValue = nextMappedField.getValue(perceroObject); +// if (fieldValue != null) { +// fieldValue = SyncHibernateUtils.cleanObject(fieldValue, s, userId); +// nextMappedField.getSetter().invoke(perceroObject, fieldValue); +// } +// } +// } catch(Exception e) { +// log.error("Error in postCreateObject " + mappedClass.className + "." + nextMappedField.getField().getName(), e); +// } +// } +// } +// } +// +// public IPerceroObject createObject(IPerceroObject perceroObject, String userId) throws SyncException { +// boolean hasAccess = true; +// IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); +// MappedClass mappedClass = mcm.getMappedClassByClassName(perceroObject.getClass().getName()); +// if (mappedClass != null) { +// if (mappedClass.getCreateQuery() != null && StringUtils.hasText(mappedClass.getCreateQuery().getQuery())){ +// Session s = appSessionFactory.openSession(); +// try { +// Query createFilter = s.createQuery(mappedClass.getCreateQuery().getQuery()); +// mappedClass.getCreateQuery().setQueryParameters(createFilter, perceroObject, userId); +// Number createFilterResult = (Number) createFilter.uniqueResult(); +// if (createFilterResult == null || createFilterResult.intValue() <= 0) +// hasAccess = false; +// } catch (Exception e) { +// log.error("Error getting PUT AccessRights for object " + perceroObject.toString(), e); +// hasAccess = false; +// } finally { +// s.close(); +// } +// } +// } +// +// if (hasAccess) { +// return systemCreateObject(perceroObject); +// } else { +// return null; +// } +// } +// +// +// +// //////////////////////////////////////////////////// +// // PUT +// //////////////////////////////////////////////////// +// public IPerceroObject putObject(IPerceroObject perceroObject, Map> changedFields, String userId) throws SyncException { +// boolean hasAccess = true; +// IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); +// MappedClass mappedClass = mcm.getMappedClassByClassName(perceroObject.getClass().getName()); +// if (mappedClass != null) { +// if (mappedClass.getUpdateQuery() != null && StringUtils.hasText(mappedClass.getUpdateQuery().getQuery())){ +// Session s = appSessionFactory.openSession(); +// try { +// Query updateFilter = s.createQuery(mappedClass.getUpdateQuery().getQuery()); +// mappedClass.getUpdateQuery().setQueryParameters(updateFilter, perceroObject, userId); +// Number updateFilterResult = (Number) updateFilter.uniqueResult(); +// if (updateFilterResult == null || updateFilterResult.intValue() <= 0) +// hasAccess = false; +// } catch (Exception e) { +// log.error("Error getting PUT AccessRights for object " + perceroObject.toString(), e); +// hasAccess = false; +// } finally { +// s.close(); +// } +// } +// } +// +// if (hasAccess) { +// return systemPutObject(perceroObject, changedFields); +// } else { +// return null; +// } +// } +// +// @SuppressWarnings({ "unchecked", "rawtypes" }) +// public IPerceroObject systemPutObject(IPerceroObject perceroObject, Map> changedFields) throws SyncException { +// Session s = null; +// +// try { +// s = appSessionFactory.openSession(); +// Transaction tx = s.beginTransaction(); +// tx.begin(); +// try { +// //s.evict(perceroObject); +// //perceroObject.setID(perceroObject.getID()); +// perceroObject = (IPerceroObject) SyncHibernateUtils.cleanObject(perceroObject, s); +// //s.persist(perceroObject); +// //perceroObject = (IPerceroObject) s.merge(perceroObject); +// } catch(Exception e) { +// e.printStackTrace(); +// } +// //s.saveOrUpdate(perceroObject); +// s.update(perceroObject); +// tx.commit(); +// +// s.refresh(perceroObject); +// +// ((BaseDataObject)perceroObject).setIsClean(false); +// perceroObject = (IPerceroObject) SyncHibernateUtils.cleanObject(perceroObject, s); +// +// // Now update the cache. +// // TODO: Field-level updates could be REALLY useful here. Would avoid A TON of UNNECESSARY work... +// if (cacheTimeout > 0) { +// // TODO: Also need to update the caches of anything object that is related to this object. +// String key = RedisKeyUtils.classIdPair(perceroObject.getClass().getCanonicalName(), perceroObject.getID()); +// if (cacheDataStore.hasKey(key)) { +// cacheDataStore.setValue(key, ((BaseDataObject)perceroObject).toJson()); +// } +// +// // Iterate through each changed object and reset the cache for that object. +// if (changedFields != null) { +//// Iterator>> itrChangedFieldEntrySet = changedFields.entrySet().iterator(); +//// Set keysToDelete = new HashSet(); +//// while (itrChangedFieldEntrySet.hasNext()) { +//// Map.Entry> nextEntry = itrChangedFieldEntrySet.next(); +//// ClassIDPair thePair = nextEntry.getKey(); +//// if (!thePair.comparePerceroObject(perceroObject)) { +//// String nextKey = RedisKeyUtils.classIdPair(thePair.getClassName(), thePair.getID()); +//// keysToDelete.add(nextKey); +//// } +//// } +// Iterator itrChangedFieldKeyset = changedFields.keySet().iterator(); +// Set keysToDelete = new HashSet(); +// while (itrChangedFieldKeyset.hasNext()) { +// ClassIDPair thePair = itrChangedFieldKeyset.next(); +// if (!thePair.comparePerceroObject(perceroObject)) { +// String nextKey = RedisKeyUtils.classIdPair(thePair.getClassName(), thePair.getID()); +// keysToDelete.add(nextKey); +// } +// } +// +// if (!keysToDelete.isEmpty()) { +// cacheDataStore.deleteKeys(keysToDelete); +// // TODO: Do we simply delete the key? Or do we refetch the object here and update the key? +// //redisDataStore.setValue(nextKey, ((BaseDataObject)perceroObject).toJson()); +// } +// } +// else { +// // No changedFields? We should never get here? +// IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); +// MappedClass mappedClass = mcm.getMappedClassByClassName(perceroObject.getClass().getName()); +// Iterator itrToManyFields = mappedClass.toManyFields.iterator(); +// while(itrToManyFields.hasNext()) { +// MappedField nextMappedField = itrToManyFields.next(); +// Object fieldObject = nextMappedField.getGetter().invoke(perceroObject); +// if (fieldObject != null) { +// if (fieldObject instanceof IPerceroObject) { +// String nextKey = RedisKeyUtils.classIdPair(fieldObject.getClass().getCanonicalName(), ((IPerceroObject)fieldObject).getID()); +// if (cacheDataStore.hasKey(nextKey)) { +// cacheDataStore.deleteKey(nextKey); +// // TODO: Do we simply delete the key? Or do we refetch the object here and update the key? +// //redisDataStore.setValue(nextKey, ((BaseDataObject)perceroObject).toJson()); +// } +// } +// else if (fieldObject instanceof Collection) { +// Iterator itrFieldObject = ((Collection) fieldObject).iterator(); +// while(itrFieldObject.hasNext()) { +// Object nextListObject = itrFieldObject.next(); +// if (nextListObject instanceof IPerceroObject) { +// String nextKey = RedisKeyUtils.classIdPair(nextListObject.getClass().getCanonicalName(), ((IPerceroObject)nextListObject).getID()); +// if (cacheDataStore.hasKey(nextKey)) { +// cacheDataStore.deleteKey(nextKey); +// // TODO: Do we simply delete the key? Or do we refetch the object here and update the key? +// //redisDataStore.setValue(nextKey, ((BaseDataObject)perceroObject).toJson()); +// } +// } +// } +// } +// } +// } +// Iterator itrToOneFields = mappedClass.toOneFields.iterator(); +// while(itrToOneFields.hasNext()) { +// MappedField nextMappedField = itrToOneFields.next(); +// Object fieldObject = nextMappedField.getGetter().invoke(perceroObject); +// if (fieldObject != null) { +// if (fieldObject instanceof IPerceroObject) { +// String nextKey = RedisKeyUtils.classIdPair(fieldObject.getClass().getCanonicalName(), ((IPerceroObject)fieldObject).getID()); +// if (cacheDataStore.hasKey(nextKey)) { +// cacheDataStore.deleteKey(nextKey); +// // TODO: Do we simply delete the key? Or do we refetch the object here and update the key? +// //redisDataStore.setValue(nextKey, ((BaseDataObject)perceroObject).toJson()); +// } +// } +// else if (fieldObject instanceof Collection) { +// Iterator itrFieldObject = ((Collection) fieldObject).iterator(); +// while(itrFieldObject.hasNext()) { +// Object nextListObject = itrFieldObject.next(); +// if (nextListObject instanceof IPerceroObject) { +// String nextKey = RedisKeyUtils.classIdPair(nextListObject.getClass().getCanonicalName(), ((IPerceroObject)nextListObject).getID()); +// if (cacheDataStore.hasKey(nextKey)) { +// cacheDataStore.deleteKey(nextKey); +// // TODO: Do we simply delete the key? Or do we refetch the object here and update the key? +// //redisDataStore.setValue(nextKey, ((BaseDataObject)perceroObject).toJson()); +// } +// } +// } +// } +// } +// } +// } +// } +// +// return perceroObject; +// } catch (Exception e) { +// log.error("Error putting object", e); +// +// SyncDataException sde = new SyncDataException(SyncDataException.UPDATE_OBJECT_ERROR, SyncDataException.UPDATE_OBJECT_ERROR_CODE); +// throw sde; +// } finally { +// s.close(); +// } +// } +// +// +// +// //////////////////////////////////////////////////// +// // DELETE +// //////////////////////////////////////////////////// +// public Boolean deleteObject(ClassIDPair theClassIdPair, String userId) throws SyncException { +// Session s = appSessionFactory.openSession(); +// +// try { +// IPerceroObject perceroObject = (IPerceroObject) s.get(theClassIdPair.getClassName(), theClassIdPair.getID()); +// if (perceroObject == null) { +// return true; +// } +// +// boolean hasAccess = true; +// IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); +// MappedClass mappedClass = mcm.getMappedClassByClassName(theClassIdPair.getClassName()); +// if (mappedClass != null) { +// if (mappedClass.getDeleteQuery() != null && StringUtils.hasText(mappedClass.getDeleteQuery().getQuery())){ +// Query deleteFilter = s.createQuery(mappedClass.getDeleteQuery().getQuery()); +// mappedClass.getDeleteQuery().setQueryParameters(deleteFilter, perceroObject, userId); +// Number deleteFilterResult = (Number) deleteFilter.uniqueResult(); +// if (deleteFilterResult == null || deleteFilterResult.intValue() <= 0) +// hasAccess = false; +// } +// } +// +// if (hasAccess) { +// if (systemDeleteObject(perceroObject)) { +// return true; +// } +// else { +// return true; +// } +// } else { +// log.warn("No access to delete object " + theClassIdPair.toString()); +// return false; +// } +// } catch (Exception e) { +// log.error("Error putting object", e); +// return false; +// } finally { +// s.close(); +// } +// } +// +// @SuppressWarnings({ "unchecked", "rawtypes" }) +// public Boolean systemDeleteObject(IPerceroObject perceroObject) throws SyncException { +// Boolean result = false; +// Session s = appSessionFactory.openSession(); +// +// try { +// // Load the object. +// IPerceroObject perceroObjectToDelete = (IPerceroObject) s.load(perceroObject.getClass().getName(), perceroObject.getID()); +// +// // Make sure all associated objects are loaded. +// // Clean the object before deletion because otherwise it will fail. +// perceroObject = (IPerceroObject) SyncHibernateUtils.cleanObject(perceroObjectToDelete, s); +// +// Transaction tx = s.beginTransaction(); +// tx.begin(); +// s.delete(perceroObjectToDelete); +// tx.commit(); +// +// // Now delete from cache. +// // Now update the cache. +// // TODO: Field-level updates could be REALLY useful here. Would avoid A TON of UNNECESSARY work... +// if (cacheTimeout > 0) { +// Set keysToDelete = new HashSet(); +// +// String key = RedisKeyUtils.classIdPair(SyncHibernateUtils.getShell(perceroObject).getClass().getCanonicalName(), perceroObject.getID()); +// keysToDelete.add(key); +// +// IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); +// MappedClass mappedClass = mcm.getMappedClassByClassName(SyncHibernateUtils.getShell(perceroObject).getClass().getName()); +// Iterator itrToManyFields = mappedClass.toManyFields.iterator(); +// while(itrToManyFields.hasNext()) { +// MappedField nextMappedField = itrToManyFields.next(); +// Object fieldObject = nextMappedField.getGetter().invoke(perceroObject); +// if (fieldObject != null) { +// if (fieldObject instanceof IPerceroObject) { +// String nextKey = RedisKeyUtils.classIdPair(fieldObject.getClass().getCanonicalName(), ((IPerceroObject)fieldObject).getID()); +// keysToDelete.add(nextKey); +// } +// else if (fieldObject instanceof Collection) { +// Iterator itrFieldObject = ((Collection) fieldObject).iterator(); +// while(itrFieldObject.hasNext()) { +// Object nextListObject = itrFieldObject.next(); +// if (nextListObject instanceof IPerceroObject) { +// String nextKey = RedisKeyUtils.classIdPair(nextListObject.getClass().getCanonicalName(), ((IPerceroObject)nextListObject).getID()); +// keysToDelete.add(nextKey); +// } +// } +// } +// } +// } +// Iterator itrToOneFields = mappedClass.toOneFields.iterator(); +// while(itrToOneFields.hasNext()) { +// MappedField nextMappedField = itrToOneFields.next(); +// Object fieldObject = nextMappedField.getGetter().invoke(perceroObject); +// if (fieldObject != null) { +// if (fieldObject instanceof IPerceroObject) { +// String nextKey = RedisKeyUtils.classIdPair(fieldObject.getClass().getCanonicalName(), ((IPerceroObject)fieldObject).getID()); +// keysToDelete.add(nextKey); +// } +// else if (fieldObject instanceof Collection) { +// Iterator itrFieldObject = ((Collection) fieldObject).iterator(); +// while(itrFieldObject.hasNext()) { +// Object nextListObject = itrFieldObject.next(); +// if (nextListObject instanceof IPerceroObject) { +// String nextKey = RedisKeyUtils.classIdPair(nextListObject.getClass().getCanonicalName(), ((IPerceroObject)nextListObject).getID()); +// keysToDelete.add(nextKey); +// } +// } +// } +// } +// } +// +// if (!keysToDelete.isEmpty()) { +// cacheDataStore.deleteKeys(keysToDelete); +// // TODO: Do we simply delete the key? Or do we refetch the object here and update the key? +// //redisDataStore.setValue(nextKey, ((BaseDataObject)perceroObject).toJson()); +// } +// } +// +// result = true; +// } catch (Exception e) { +// log.error("Error deleting object", e); +// +// SyncDataException sde = new SyncDataException(SyncDataException.DELETE_OBJECT_ERROR, SyncDataException.DELETE_OBJECT_ERROR_CODE); +// throw sde; +// } finally { +// s.close(); +// } +// +// return result; +// } +// +// /* +// public Object cleanObject(Object object) { +// return SyncHibernateUtils.cleanObject(object); +// }*/ +// +// public Object cleanObject(Object object, Session s, String userId) { +// try { +// if (s == null) { +// s = appSessionFactory.openSession(); +// } +// return SyncHibernateUtils.cleanObject(object, s, userId); +// } catch(Exception e) { +// log.warn("Error cleaning object", e); +// return null; +// } finally { +// if (s != null && s.isOpen()) { +// s.close(); +// } +// } +// } +// +// +// @SuppressWarnings("rawtypes") +// public Map> getChangedMappedFields(IPerceroObject newObject) { +// Map> result = new HashMap>(); +// Collection baseObjectResult = null; +// ClassIDPair basePair = new ClassIDPair(newObject.getID(), newObject.getClass().getCanonicalName()); +// +// String className = newObject.getClass().getCanonicalName(); +// IPerceroObject oldObject = systemGetById(new ClassIDPair(newObject.getID(), className)); +// IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); +// MappedClass mc = mcm.getMappedClassByClassName(className); +// Iterator itrMappedFields = mc.externalizableFields.iterator(); +// while (itrMappedFields.hasNext()) { +// MappedField nextMappedField = itrMappedFields.next(); +// try { +// Boolean fieldIsEqual = nextMappedField.compareObjects(oldObject, newObject); +// if (!fieldIsEqual) { +// if (baseObjectResult == null) { +// baseObjectResult = new HashSet(); +// result.put(basePair, baseObjectResult); +// } +// baseObjectResult.add(nextMappedField); +// +// // If this is a relationship field, then need to grab the old and new values. +// if (nextMappedField.getReverseMappedField() != null) { +// if (nextMappedField instanceof MappedFieldPerceroObject) { +// MappedFieldPerceroObject nextMappedFieldPerceroObject = (MappedFieldPerceroObject) nextMappedField; +// +// IPerceroObject oldReversePerceroObject = (IPerceroObject) nextMappedFieldPerceroObject.getGetter().invoke(oldObject); +// if (oldReversePerceroObject != null) { +// ClassIDPair oldReversePair = new ClassIDPair(oldReversePerceroObject.getID(), oldReversePerceroObject.getClass().getCanonicalName()); +// Collection oldReverseChangedFields = result.get(oldReversePair); +// if (oldReverseChangedFields == null) { +// oldReverseChangedFields = new HashSet(); +// result.put(oldReversePair, oldReverseChangedFields); +// } +// oldReverseChangedFields.add(nextMappedField.getReverseMappedField()); +// } +// +// IPerceroObject newReversePerceroObject = (IPerceroObject) nextMappedFieldPerceroObject.getGetter().invoke(newObject); +// if (newReversePerceroObject != null) { +// ClassIDPair newReversePair = new ClassIDPair(newReversePerceroObject.getID(), newReversePerceroObject.getClass().getCanonicalName()); +// Collection changedFields = result.get(newReversePair); +// if (changedFields == null) { +// changedFields = new HashSet(); +// result.put(newReversePair, changedFields); +// } +// changedFields.add(nextMappedField.getReverseMappedField()); +// } +// } +// else if (nextMappedField instanceof MappedFieldList) { +// MappedFieldList nextMappedFieldList = (MappedFieldList) nextMappedField; +// +// List oldReverseList = (List) nextMappedFieldList.getGetter().invoke(oldObject); +// if (oldReverseList == null) +// oldReverseList = new ArrayList(); +// +// List newReverseList = (List) nextMappedFieldList.getGetter().invoke(newObject); +// if (newReverseList == null) +// newReverseList = new ArrayList(); +// +// // Compare each item in the lists. +// Collection oldChangedList = retrieveObjectsNotInCollection(oldReverseList, newReverseList); +// Iterator itrOldChangedList = oldChangedList.iterator(); +// while (itrOldChangedList.hasNext()) { +// BaseDataObject nextOldChangedObject = (BaseDataObject) itrOldChangedList.next(); +// ClassIDPair nextOldReversePair = BaseDataObject.toClassIdPair(nextOldChangedObject); +// +// // Old object is not in new list, so add to list of changed fields. +// Collection changedFields = result.get(nextOldReversePair); +// if (changedFields == null) { +// changedFields = new HashSet(); +// result.put(nextOldReversePair, changedFields); +// } +// changedFields.add(nextMappedField.getReverseMappedField()); +// } +// +// Collection newChangedList = retrieveObjectsNotInCollection(newReverseList, oldReverseList); +// Iterator itrNewChangedList = newChangedList.iterator(); +// while (itrNewChangedList.hasNext()) { +// BaseDataObject nextNewChangedObject = (BaseDataObject) itrNewChangedList.next(); +// ClassIDPair nextNewReversePair = BaseDataObject.toClassIdPair(nextNewChangedObject); +// +// // Old object is not in new list, so add to list of changed fields. +// Collection changedFields = result.get(nextNewReversePair); +// if (changedFields == null) { +// changedFields = new HashSet(); +// result.put(nextNewReversePair, changedFields); +// } +// changedFields.add(nextMappedField.getReverseMappedField()); +// } +// } +// } +// } +// } catch(Exception e) { +// log.warn("Error getting changed field: " + nextMappedField.getField().getName(), e); +// baseObjectResult.add(nextMappedField); +// } +// } +// +// return result; +// } +// +// @SuppressWarnings({ "rawtypes", "unchecked" }) +// private Collection retrieveObjectsNotInCollection(Collection baseList, Collection compareToList) { +// Collection result = new HashSet(); +// Iterator itrBaseList = baseList.iterator(); +// Iterator itrCompareToList = null; +// boolean matchFound = false; +// +// while (itrBaseList.hasNext()) { +// BaseDataObject nextBasePerceroObject = (BaseDataObject) itrBaseList.next(); +// ClassIDPair nextBasePair = BaseDataObject.toClassIdPair(nextBasePerceroObject); +// nextBasePerceroObject = (BaseDataObject) systemGetById(nextBasePair); +// +// itrCompareToList = compareToList.iterator(); +// matchFound = false; +// while (itrCompareToList.hasNext()) { +// BaseDataObject nextCompareToPerceroObject = (BaseDataObject) itrCompareToList.next(); +// nextCompareToPerceroObject = (BaseDataObject) systemGetById(BaseDataObject.toClassIdPair(nextCompareToPerceroObject)); +// +// if (nextBasePerceroObject.getClass() == nextCompareToPerceroObject.getClass() && nextBasePerceroObject.getID().equalsIgnoreCase(nextCompareToPerceroObject.getID())) { +// matchFound = true; +// break; +// } +// } +// +// if (!matchFound) { +// result.add(nextBasePerceroObject); +// } +// } +// +// return result; +// } +//} diff --git a/src/main/java/com/percero/agents/sync/services/IDataProvider.java b/src/main/java/com/percero/agents/sync/services/IDataProvider.java index ec8b82f..dc40989 100644 --- a/src/main/java/com/percero/agents/sync/services/IDataProvider.java +++ b/src/main/java/com/percero/agents/sync/services/IDataProvider.java @@ -3,8 +3,7 @@ import java.util.Collection; import java.util.List; import java.util.Map; - -import org.hibernate.Session; +import java.util.Set; import com.percero.agents.sync.exceptions.SyncException; import com.percero.agents.sync.metadata.MappedClass; @@ -21,27 +20,29 @@ public interface IDataProvider { public String getName(); public Integer countAllByName(String className, String userId) throws Exception; - public PerceroList getAllByName(Object aName, Integer pageNumber, Integer pageSize, Boolean returnTotal, String userId) throws Exception; - public List runQuery(MappedClass mappedClass, String queryName, Object[] queryArguments, String clientId); + public PerceroList getAllByName(String className, Integer pageNumber, Integer pageSize, Boolean returnTotal, String userId) throws Exception; + public Set getAllClassIdPairsByName(String className) throws Exception; + public List runQuery(MappedClass mappedClass, String queryName, Object[] queryArguments, String clientId) throws SyncException; public IPerceroObject findById(ClassIDPair classIdPair, String userId); - public T systemGetById(ClassIDPair classIdPair); + public IPerceroObject findById(ClassIDPair classIdPair, String userId, Boolean ignoreCache); public List findByIds(ClassIDPairs classIdPairs, String userId); - public IPerceroObject findUnique(IPerceroObject theQueryObject, String userId); - public List findByExample(IPerceroObject theQueryObject, List excludeProperties, String userId); - public List systemFindByExample(IPerceroObject theQueryObject, List excludeProperties); - public List searchByExample(IPerceroObject theQueryObject, List excludeProperties, String userId); + public List findByIds(ClassIDPairs classIdPairs, String userId, Boolean ignoreCache); +// public IPerceroObject findUnique(IPerceroObject theQueryObject, String userId); + public List findByExample(IPerceroObject theQueryObject, List excludeProperties, String userId, Boolean shellOnly) throws SyncException; +// public List systemFindByExample(IPerceroObject theQueryObject, List excludeProperties); +// public List searchByExample(IPerceroObject theQueryObject, List excludeProperties, String userId); public Boolean getReadAccess(ClassIDPair classIdPair, String userId); public Boolean getDeleteAccess(ClassIDPair classIdPair, String userId); - public T systemCreateObject(IPerceroObject perceroObject) throws SyncException; - public IPerceroObject createObject(IPerceroObject perceroObject, String userId) throws SyncException; - public IPerceroObject putObject(IPerceroObject perceroObject, Map> changedFields, String userId) throws SyncException; - public IPerceroObject systemPutObject(IPerceroObject perceroObject, Map> changedFields) throws SyncException; +// public T systemCreateObject(IPerceroObject perceroObject) throws SyncException; + public T createObject(T perceroObject, String userId) throws SyncException; + public T putObject(T perceroObject, Map> changedFields, String userId) throws SyncException; +// public IPerceroObject systemPutObject(IPerceroObject perceroObject, Map> changedFields) throws SyncException; public Boolean deleteObject(ClassIDPair theClassIdPair, String userId) throws SyncException; - public Boolean systemDeleteObject(IPerceroObject perceroObject) throws SyncException; +// public Boolean systemDeleteObject(IPerceroObject perceroObject) throws SyncException; //public Object cleanObject(Object object, String userId); - public Object cleanObject(Object object, Session s, String userId); +// public Object cleanObject(Object object, Session s, String userId); /** * Returns a map of changed fields for the base IPerceroObject as well as all associated objects that will @@ -50,4 +51,27 @@ public interface IDataProvider { * @return */ public Map> getChangedMappedFields(IPerceroObject newObject); + + /** + * Given the mappedField, returns ALL objects in the relationship described by the mappedField. + * If shellOnly is TRUE, then only a shell object will be returned, which is an Object with only it's ID set. + * Will enforce access rights based on the userId. + * Returns a cleaned object, meaning all its relationships are filled in with shell objects. + * + * @param perceroObject + * @param mappedField + * @param shellOnly + * @param userId + * @return List + * @throws SyncException + */ + public List findAllRelatedObjects(IPerceroObject perceroObject, MappedField mappedField, Boolean shellOnly, String userId) throws SyncException; + + public List getAllByRelationship(MappedField mappedField, ClassIDPair targetClassIdPair, Boolean shellOnly, String userId) throws SyncException; + + IPerceroObject cleanObject(IPerceroObject perceroObject, String userId) + throws SyncException; + + List cleanObject(List perceroObjects, + String userId) throws SyncException; } diff --git a/src/main/java/com/percero/agents/sync/services/ISyncAgentService.java b/src/main/java/com/percero/agents/sync/services/ISyncAgentService.java index 69d24a8..e743e77 100644 --- a/src/main/java/com/percero/agents/sync/services/ISyncAgentService.java +++ b/src/main/java/com/percero/agents/sync/services/ISyncAgentService.java @@ -24,7 +24,7 @@ public interface ISyncAgentService { public ServerResponse createObject(IPerceroObject perceroObject, String clientId) throws Exception; public ServerResponse createObject(IPerceroObject perceroObject, String clientId, Boolean pushToClient) throws Exception; - public T systemCreateObject(IPerceroObject perceroObject, String userId) throws SyncException; + public T systemCreateObject(T perceroObject, String userId) throws SyncException; public ServerResponse deleteObject(ClassIDPair theClassIdPair, String clientId) throws Exception; public ServerResponse deleteObject(ClassIDPair theClassIdPair, String clientId, Boolean pushToClient) throws Exception; public ServerResponse deleteObjectById(String theClassName, String theId, String clientId) throws Exception; @@ -39,21 +39,21 @@ public interface ISyncAgentService { public ServerResponse processTransaction(List objectsToSave, List objectsToRemove, String transactionId, Date transDate, String clientId) throws Exception; public Map countAllByName(Collection classNames, String clientId) throws Exception; - public PerceroList getAllByName(Object aName, Boolean returnTotal, String clientId) throws Exception; - public PerceroList getAllByName(Object aName, Integer pageNumber, Integer pageSize, Boolean returnTotal, String clientId) throws Exception; + public PerceroList getAllByName(String className, Boolean returnTotal, String clientId) throws Exception; + public PerceroList getAllByName(String className, Integer pageNumber, Integer pageSize, Boolean returnTotal, String clientId) throws Exception; public Object findById(ClassIDPair classIdPair, String clientId) throws Exception; public Object findById(String aClassName, String anId, String clientId) throws Exception; - public T systemGetByObject(IPerceroObject perceroObject); + public T systemGetByObject(T perceroObject); public IPerceroObject systemGetById(ClassIDPair cip); public IPerceroObject systemGetById(String aClassName, String anId); - public List systemFindByExample(Object theQueryObject, List excludeProperties); + public List systemFindByExample(Object theQueryObject, List excludeProperties) throws SyncException; public List findByIds(List classIdList, String clientId) throws Exception; public Object findByExample(Object theQueryObject, List excludeProperties, String clientId) throws Exception; public Object findUnique(Object theQueryObject, String clientId) throws Exception; public Object runQuery(String className, String queryName, Object[] queryArguments, String clientId) throws Exception; public Object runProcess(String processName, Object[] queryArguments, String clientId) throws Exception; - public Object getChangeWatcher(ClassIDPair classIdPair, String fieldName, String[] params, String clientId) throws Exception; - public List getHistory(String aClassName, String anId, String clientId) throws Exception; + public Object getChangeWatcherValue(ClassIDPair classIdPair, String fieldName, String[] params, String clientId) throws Exception; + public List getHistory(String aClassName, String anId, String clientId) throws Exception; public Object searchByExample(Object theQueryObject, String clientId) throws Exception; public Object searchByExample(Object theQueryObject, List excludeProperties, String clientId) throws Exception; diff --git a/src/main/java/com/percero/agents/sync/services/RedisDataProvider.java b/src/main/java/com/percero/agents/sync/services/RedisDataProvider.java index de3cbaa..0922ba2 100644 --- a/src/main/java/com/percero/agents/sync/services/RedisDataProvider.java +++ b/src/main/java/com/percero/agents/sync/services/RedisDataProvider.java @@ -3,10 +3,12 @@ import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.Set; import org.hibernate.Session; import org.springframework.stereotype.Component; +import com.percero.agents.sync.exceptions.SyncException; import com.percero.agents.sync.metadata.MappedClass; import com.percero.agents.sync.metadata.MappedField; import com.percero.agents.sync.vo.ClassIDPair; @@ -153,11 +155,7 @@ public IPerceroObject findById(ClassIDPair classIdPair, String userId) { return null;**/ } - public IPerceroObject systemGetById(ClassIDPair classIdPair) { - return null; - } - - public List findByIds(ClassIDPairs classIdPairs, String clientId) { + public List findByIds(ClassIDPairs classIdPairs, String clientId, Boolean ignoreCache) { /* List result = null; try { @@ -266,7 +264,7 @@ public IPerceroObject systemCreateObject(IPerceroObject perceroObject) { return null; } - public IPerceroObject createObject(IPerceroObject perceroObject, String userId) { + public T createObject(T perceroObject, String userId) throws SyncException { // TODO Auto-generated method stub return null; } @@ -276,7 +274,8 @@ public IPerceroObject createObject(IPerceroObject perceroObject, String userId) //////////////////////////////////////////////////// // PUT //////////////////////////////////////////////////// - public IPerceroObject putObject(IPerceroObject perceroObject, Map> changedFields, String userId) { + public T putObject(T perceroObject, Map> changedFields, String userId) throws SyncException { +// public IPerceroObject putObject(IPerceroObject perceroObject, Map> changedFields, String userId) { /* try { Object result = JOhm.save(perceroObject); return perceroObject; @@ -359,4 +358,72 @@ public Object cleanObject(Object object, Session s, String userId) { public Map> getChangedMappedFields(IPerceroObject newObject) { return null; } + + @Override + public PerceroList getAllByName(String className, + Integer pageNumber, Integer pageSize, Boolean returnTotal, + String userId) throws Exception { + // TODO Auto-generated method stub + return null; + } + + @Override + public IPerceroObject findById(ClassIDPair classIdPair, String userId, + Boolean ignoreCache) { + // TODO Auto-generated method stub + return null; + } + + @Override + public List findByExample(IPerceroObject theQueryObject, + List excludeProperties, String userId, Boolean shellOnly) + throws SyncException { + // TODO Auto-generated method stub + return null; + } + + @Override + public List findAllRelatedObjects( + IPerceroObject perceroObject, MappedField mappedField, + Boolean shellOnly, String userId) throws SyncException { + // TODO Auto-generated method stub + return null; + } + + @Override + public List getAllByRelationship(MappedField mappedField, + ClassIDPair targetClassIdPair, Boolean shellOnly, String userId) + throws SyncException { + // TODO Auto-generated method stub + return null; + } + + @Override + public IPerceroObject cleanObject(IPerceroObject perceroObject, + String userId) throws SyncException { + // TODO Auto-generated method stub + return null; + } + + @Override + public List cleanObject( + List perceroObjects, String userId) + throws SyncException { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set getAllClassIdPairsByName(String className) + throws Exception { + // TODO Auto-generated method stub + return null; + } + + @Override + public List findByIds(ClassIDPairs classIdPairs, + String userId) { + // TODO Auto-generated method stub + return null; + } } diff --git a/src/main/java/com/percero/agents/sync/services/SyncAgentDataProvider.java b/src/main/java/com/percero/agents/sync/services/SyncAgentDataProvider.java index d933039..316d351 100644 --- a/src/main/java/com/percero/agents/sync/services/SyncAgentDataProvider.java +++ b/src/main/java/com/percero/agents/sync/services/SyncAgentDataProvider.java @@ -37,7 +37,8 @@ import org.springframework.util.StringUtils; import com.percero.agents.sync.access.RedisKeyUtils; -import com.percero.agents.sync.datastore.RedisDataStore; +import com.percero.agents.sync.cache.CacheManager; +import com.percero.agents.sync.datastore.ICacheDataStore; import com.percero.agents.sync.exceptions.SyncDataException; import com.percero.agents.sync.exceptions.SyncException; import com.percero.agents.sync.hibernate.AssociationExample; @@ -66,22 +67,25 @@ public class SyncAgentDataProvider implements IDataProvider { // TODO: Better manage Hibernate Sessions (opening and closing). private static final Logger log = Logger.getLogger(SyncAgentDataProvider.class); - + public void initialize() { // Do nothing. } - + public String getName() { return "syncAgent"; } - + @Autowired - RedisDataStore redisDataStore; + ICacheDataStore cacheDataStore; @Autowired Long cacheTimeout = Long.valueOf(60 * 60 * 24 * 14); // Two weeks + @Autowired + CacheManager cacheManager; + @Autowired ObjectMapper safeObjectMapper; @@ -90,21 +94,20 @@ public String getName() { public void setAppSessionFactory(SessionFactory value) { appSessionFactory = value; } - + @SuppressWarnings({ "rawtypes", "unchecked" }) // TODO: @Transactional(readOnly=true) - public PerceroList getAllByName(Object aName, Integer pageNumber, Integer pageSize, Boolean returnTotal, String userId) throws Exception { + public PerceroList getAllByName(String className, Integer pageNumber, Integer pageSize, Boolean returnTotal, String userId) throws Exception { Session s = appSessionFactory.openSession(); try { returnTotal = true; - String aClassName = aName.toString(); Query countQuery = null; Query query = null; - Class theClass = MappedClass.forName(aName.toString()); + Class theClass = MappedClass.forName(className); IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); - MappedClass mappedClass = mcm.getMappedClassByClassName(aClassName); + MappedClass mappedClass = mcm.getMappedClassByClassName(className); boolean isValidReadQuery = false; if (mappedClass != null) { if (mappedClass.getReadQuery() != null && StringUtils.hasText(mappedClass.getReadQuery().getQuery())) { @@ -116,37 +119,36 @@ public PerceroList getAllByName(Object aName, Integer pageNumber * readAllQuery optimization * You can now define a readAllQuery on a class to imporove it's initial download time * for briefcase mode. - * + * * Only supports plain SQL for now */ if(mappedClass.getReadAllQuery() != null && mappedClass.getReadAllQuery().getQuery() != null && !mappedClass.getReadAllQuery().getQuery().isEmpty()){ if((mappedClass.getReadAllQuery() instanceof JpqlQuery)){ - throw new IllegalArgumentException("Illegal query type on:"+aClassName+". readAllQueries must be plain SQL. JPQL, HQL not supported yet. "); + throw new IllegalArgumentException("Illegal query type on:"+className+". readAllQueries must be plain SQL. JPQL, HQL not supported yet. "); } - - log.debug("Using readAllQuery: "+aClassName); + log.debug("Using readAllQuery: "+className); String selectQueryString = mappedClass.getReadAllQuery().getQuery(); - + String countQueryString = "select count(*) from ("+selectQueryString+") as U"; - + // Add the limit clause if (pageSize != null && pageNumber != null && pageSize.intValue() > 0) { int offset = pageSize.intValue() * pageNumber.intValue(); selectQueryString += " limit "+pageSize+" OFFSET "+offset; } - + query = s.createSQLQuery(selectQueryString).addEntity(theClass); countQuery = s.createSQLQuery(countQueryString); - + query.setParameter("userId", userId); countQuery.setParameter("userId", userId); } - else - if (theClass != null) { + else + if (theClass != null) { String countQueryString = ""; if (returnTotal) - countQueryString = "SELECT COUNT(getAllResult.ID) FROM " + aClassName + " getAllResult"; - String queryString = "SELECT getAllResult FROM " + aClassName + " getAllResult"; + countQueryString = "SELECT COUNT(getAllResult.ID) FROM " + className + " getAllResult"; + String queryString = "SELECT getAllResult FROM " + className + " getAllResult"; // If the Read Query/Filter uses the ID, then we need to check against each ID here. if (isValidReadQuery) { @@ -168,8 +170,8 @@ public PerceroList getAllByName(Object aName, Integer pageNumber countQuery = s.createQuery(countQueryFilterString); } query = s.createQuery(queryFilterString); - mappedClass.getReadQuery().setQueryParameters(query, null, userId); - mappedClass.getReadQuery().setQueryParameters(countQuery, null, userId); + mappedClass.getReadQuery().setQueryParameters(query.getQueryString(), null, userId); + mappedClass.getReadQuery().setQueryParameters(countQuery.getQueryString(), null, userId); } else { queryString += " ORDER BY getAllResult.ID"; @@ -187,8 +189,8 @@ public PerceroList getAllByName(Object aName, Integer pageNumber } if (query != null) { - - log.debug("Get ALL: "+aClassName); + log.debug("Get ALL: "+className); + long t1 = new Date().getTime(); List list = query.list(); long t2 = new Date().getTime(); @@ -196,10 +198,10 @@ public PerceroList getAllByName(Object aName, Integer pageNumber PerceroList result = new PerceroList( (List) SyncHibernateUtils.cleanObject(list, s, userId) ); long t3 = new Date().getTime(); log.debug("Clean Time: "+(t3-t2)); - + result.setPageNumber(pageNumber); result.setPageSize(pageSize); - + if (returnTotal && pageSize != null && pageNumber != null && pageSize.intValue() > 0){ result.setTotalLength(((Number)countQuery.uniqueResult()).intValue()); log.debug("Total Obs: "+result.getTotalLength()); @@ -221,6 +223,33 @@ public PerceroList getAllByName(Object aName, Integer pageNumber return null; } + @SuppressWarnings({ "rawtypes" }) + public Set getAllClassIdPairsByName(String className) throws Exception { + Set results = new HashSet(); + + Session s = appSessionFactory.openSession(); + try { + Query query = null; + + String queryString = "SELECT ID FROM " + className; + query = s.createQuery(queryString); + + List queryResults = query.list(); + Iterator itrQueryResults = queryResults.iterator(); + while (itrQueryResults.hasNext()) { + String nextId = (String) itrQueryResults.next(); + results.add(new ClassIDPair(nextId, className)); + } + + } catch (Exception e) { + log.error("Unable to getAllClassIdPairsByName", e); + } finally { + s.close(); + } + + return results; + } + @SuppressWarnings({ "rawtypes" }) // TODO: @Transactional(readOnly=true) public Integer countAllByName(String aClassName, String userId) throws Exception { @@ -228,7 +257,7 @@ public Integer countAllByName(String aClassName, String userId) throws Exception try { Query countQuery = null; Class theClass = MappedClass.forName(aClassName); - + IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); MappedClass mappedClass = mcm.getMappedClassByClassName(aClassName); boolean isValidReadQuery = false; @@ -237,22 +266,22 @@ public Integer countAllByName(String aClassName, String userId) throws Exception isValidReadQuery = true; } } - + if(mappedClass.getReadAllQuery() != null && mappedClass.getReadAllQuery().getQuery() != null && !mappedClass.getReadAllQuery().getQuery().isEmpty()){ if((mappedClass.getReadAllQuery() instanceof JpqlQuery)){ throw new IllegalArgumentException("Illegal query type on:"+aClassName+". readAllQueries must be plain SQL. JPQL, HQL not supported yet. "); } - + String selectQueryString = mappedClass.getReadAllQuery().getQuery(); - + String countQueryString = "select count(*) from ("+selectQueryString+") as U"; - + countQuery = s.createSQLQuery(countQueryString); countQuery.setParameter("userId", userId); } else if (theClass != null) { String countQueryString = "SELECT COUNT(getAllResult.ID) FROM " + aClassName + " getAllResult"; - + // If the Read Query/Filter uses the ID, then we need to check against each ID here. if (isValidReadQuery) { String queryFilterString = mappedClass.getReadQuery().getQuery(); @@ -265,16 +294,16 @@ else if (theClass != null) { } String countQueryFilterString = queryFilterString; countQueryFilterString = countQueryString + " WHERE (" + countQueryFilterString + ") > 0"; - + countQuery = s.createQuery(countQueryFilterString); - mappedClass.getReadQuery().setQueryParameters(countQuery, null, userId); + mappedClass.getReadQuery().setQueryParameters(countQuery.getQueryString(), null, userId); } else { countQueryString += " ORDER BY ID"; countQuery = s.createQuery(countQueryString); } } - + if (countQuery != null) { // log.debug(countQuery.toString()); Integer result = ((Number)countQuery.uniqueResult()).intValue(); @@ -283,66 +312,67 @@ else if (theClass != null) { else { return null; } - + } catch (Exception e) { log.error("Unable to countAllByName", e); } finally { s.close(); } - + return 0; } - public List runQuery(MappedClass mappedClass, String queryName, Object[] queryArguments, String userId) { - Session s = appSessionFactory.openSession(); - try { - if (mappedClass != null) { - for(IMappedQuery nextQuery : mappedClass.queries) - { - if (queryName.equalsIgnoreCase(nextQuery.getQueryName())) - { - Query readFilter = s.createQuery(nextQuery.getQuery()); - nextQuery.setQueryParameters(readFilter, queryArguments, userId, queryArguments, (SessionImpl)s); - return processQueryResults(mappedClass.className + "_" + queryName, readFilter, readFilter.list()); - } - } - } - } catch (Exception e) { - log.error("Unable to runQuery", e); - } finally { - if (s != null && s.isOpen()) - s.close(); - } - - return null; + public List runQuery(MappedClass mappedClass, String queryName, Object[] queryArguments, String userId) throws SyncException { + throw new SyncException(SyncException.METHOD_UNSUPPORTED, SyncException.METHOD_UNSUPPORTED_CODE); +// Session s = appSessionFactory.openSession(); +// try { +// if (mappedClass != null) { +// for(IMappedQuery nextQuery : mappedClass.queries) +// { +// if (queryName.equalsIgnoreCase(nextQuery.getQueryName())) +// { +// Query readFilter = s.createQuery(nextQuery.getQuery()); +// nextQuery.setQueryParameters(readFilter.getQueryString(), queryArguments, userId, queryArguments, (SessionImpl)s); +// return processQueryResults(mappedClass.className + "_" + queryName, readFilter, readFilter.list()); +// } +// } +// } +// } catch (Exception e) { +// log.error("Unable to runQuery", e); +// } finally { +// if (s != null && s.isOpen()) +// s.close(); +// } +// +// return null; } - + @SuppressWarnings({ "rawtypes", "unchecked" }) protected static List processQueryResults(String resultClassName, Query updateFilter, List updateFilterResult) throws Exception { String[] returnAliases = updateFilter.getReturnAliases(); Type[] returnTypes = updateFilter.getReturnTypes(); - + String[] fieldNames = new String[returnAliases.length]; String[] getMethodNames = new String[returnAliases.length]; String[] setMethodNames = new String[returnAliases.length]; Class[] fieldClasses = new Class[returnAliases.length]; - + Class clazz = null; ClassPool pool = null; CtClass evalClass = null; - + if (returnAliases.length > 1) { try { clazz = MappedClass.forName(resultClassName); } catch(Exception e) { // Class must not yet exist, so let's create it. } - + if (clazz == null) { pool = ClassPool.getDefault(); evalClass = pool.makeClass(resultClassName); } - + // Create a new Class based on the result set. for(int i = 0; i < returnAliases.length; i++) { Type nextType = returnTypes[i]; @@ -355,29 +385,29 @@ protected static List processQueryResults(String resultClassName, Query // Do nothing. Simply means the field name is not a Number. } String nextUpperFieldName = nextFieldName.substring(0, 1).toUpperCase() + nextFieldName.substring(1); - + fieldNames[i] = nextFieldName; getMethodNames[i] = "get" + nextUpperFieldName; setMethodNames[i] = "set" + nextUpperFieldName; fieldClasses[i] = nextType.getReturnedClass(); - + if (evalClass != null) { evalClass.addField(CtField.make("private " + fieldClasses[i].getCanonicalName() + " " + nextFieldName + ";", evalClass)); evalClass.addMethod(CtMethod.make("public void " + setMethodNames[i] + "(" + fieldClasses[i].getCanonicalName() + " value) {this." + nextFieldName + " = value;}", evalClass)); evalClass.addMethod(CtMethod.make("public " + nextTypeCanonicalName +" " + getMethodNames[i] + "() {return this." + nextFieldName + ";}", evalClass)); } } - + if (clazz == null && evalClass != null) { clazz = evalClass.toClass(); } } - + List results = new ArrayList(); // Now populate the newly created objects. for(Object nextResult : (List)updateFilterResult) { - + if (nextResult instanceof Object[]) { Object nextObject = clazz.newInstance(); for(int i = 0; i < returnAliases.length; i++) { @@ -385,17 +415,21 @@ protected static List processQueryResults(String resultClassName, Query Method setMethod = clazz.getDeclaredMethod(setMethodNames[i], formalParams); setMethod.invoke(nextObject, ((Object[])nextResult)[i]); } - + results.add(nextObject); } else results.add(nextResult); } - + return results; } - - //@SuppressWarnings({ "rawtypes", "unchecked" }) + + @Override public IPerceroObject findById(ClassIDPair classIdPair, String userId) { + return findById(classIdPair, userId, false); + } + @Override + public IPerceroObject findById(ClassIDPair classIdPair, String userId, Boolean ignoreCache) { boolean hasReadQuery = false; IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); MappedClass mappedClass = mcm.getMappedClassByClassName(classIdPair.getClassName()); @@ -408,14 +442,14 @@ public IPerceroObject findById(ClassIDPair classIdPair, String userId) { Session s = null; try { boolean hasAccess = true; - IPerceroObject result = systemGetById(classIdPair); - + IPerceroObject result = systemGetById(classIdPair, ignoreCache); + if (result != null) { if (hasReadQuery) { if (s == null) s = appSessionFactory.openSession(); Query readFilter = s.createQuery(mappedClass.getReadQuery().getQuery()); - mappedClass.getReadQuery().setQueryParameters(readFilter, result, userId); + mappedClass.getReadQuery().setQueryParameters(readFilter.getQueryString(), result, userId); Number readFilterResult = (Number) readFilter.uniqueResult(); if (readFilterResult == null || readFilterResult.intValue() <= 0) hasAccess = false; @@ -444,26 +478,34 @@ public IPerceroObject findById(ClassIDPair classIdPair, String userId) { return null; } - @SuppressWarnings({ "rawtypes", "unchecked" }) public IPerceroObject systemGetById(ClassIDPair classIdPair) { + return systemGetById(classIdPair, false); + } + + @SuppressWarnings({ "rawtypes" }) + public IPerceroObject systemGetById(ClassIDPair classIdPair, boolean ignoreCache) { Session s = null; try { Class theClass = MappedClass.forName(classIdPair.getClassName().toString()); - + if (theClass != null) { - IPerceroObject result = retrieveFromRedisCache(classIdPair); + IPerceroObject result = null; + + if(!ignoreCache) + result = retrieveFromRedisCache(classIdPair); + String key = null; if (result == null) { s = appSessionFactory.openSession(); result = (IPerceroObject) s.get(theClass, classIdPair.getID()); - + // Now put the object in the cache. if (result != null) { key = RedisKeyUtils.classIdPair(result.getClass().getCanonicalName(), result.getID()); result = (IPerceroObject) SyncHibernateUtils.cleanObject(result, s); if (cacheTimeout > 0) - redisDataStore.setValue(key, ((BaseDataObject)result).toJson()); + cacheDataStore.setValue(key, ((BaseDataObject)result).toJson()); } else { // Not necessarily a problem but could be helpful when debugging. @@ -476,9 +518,9 @@ public IPerceroObject systemGetById(ClassIDPair classIdPair) { // (Re)Set the expiration. if (cacheTimeout > 0 && key != null) { - redisDataStore.expire(key, cacheTimeout, TimeUnit.SECONDS); + cacheDataStore.expire(key, cacheTimeout, TimeUnit.SECONDS); } - + return result; } else { return null; @@ -499,7 +541,7 @@ public IPerceroObject systemGetByIdWithClassAndSession(ClassIDPair classIdPair, if (theClass != null) { IPerceroObject result = retrieveFromRedisCache(classIdPair); String key = null; - + if (result == null) { if (s == null || !s.isOpen()) { s = appSessionFactory.openSession(); @@ -508,13 +550,13 @@ public IPerceroObject systemGetByIdWithClassAndSession(ClassIDPair classIdPair, sessionAlreadyOpen = true; } result = (IPerceroObject) s.get(theClass, classIdPair.getID()); - + // Now put the object in the cache. if (result != null) { key = RedisKeyUtils.classIdPair(result.getClass().getCanonicalName(), result.getID()); result = (IPerceroObject) SyncHibernateUtils.cleanObject(result, s); if (cacheTimeout > 0) - redisDataStore.setValue(key, ((BaseDataObject)result).toJson()); + cacheDataStore.setValue(key, ((BaseDataObject)result).toJson()); } else { log.warn("Unable to retrieve object from database: " + classIdPair.toString()); @@ -523,12 +565,12 @@ public IPerceroObject systemGetByIdWithClassAndSession(ClassIDPair classIdPair, else { key = RedisKeyUtils.classIdPair(result.getClass().getCanonicalName(), result.getID()); } - + // (Re)Set the expiration. if (cacheTimeout > 0 && key != null) { - redisDataStore.expire(key, cacheTimeout, TimeUnit.SECONDS); + cacheDataStore.expire(key, cacheTimeout, TimeUnit.SECONDS); } - + return result; } else { return null; @@ -545,7 +587,7 @@ public IPerceroObject systemGetByIdWithClassAndSession(ClassIDPair classIdPair, } return null; } - + @SuppressWarnings({ "rawtypes", "unchecked" }) private IPerceroObject retrieveFromRedisCache(ClassIDPair classIdPair) throws Exception { IPerceroObject result = null; @@ -553,9 +595,9 @@ private IPerceroObject retrieveFromRedisCache(ClassIDPair classIdPair) throws Ex IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); Class theClass = MappedClass.forName(classIdPair.getClassName()); MappedClass mc = mcm.getMappedClassByClassName(classIdPair.getClassName()); - + String key = RedisKeyUtils.classIdPair(classIdPair.getClassName(), classIdPair.getID()); - String jsonObjectString = (String) redisDataStore.getValue(key); + String jsonObjectString = (String) cacheDataStore.getValue(key); if (jsonObjectString != null) { if (IJsonObject.class.isAssignableFrom(theClass)) { IJsonObject jsonObject = (IJsonObject) theClass.newInstance(); @@ -572,27 +614,27 @@ private IPerceroObject retrieveFromRedisCache(ClassIDPair classIdPair) throws Ex while (itrChildMappedClasses.hasNext()) { MappedClass nextChildMc = itrChildMappedClasses.next(); key = RedisKeyUtils.classIdPair(nextChildMc.className, classIdPair.getID()); - jsonObjectString = (String) redisDataStore.getValue(key); + jsonObjectString = (String) cacheDataStore.getValue(key); if (jsonObjectString != null) { result = (IPerceroObject) safeObjectMapper.readValue(jsonObjectString, theClass); return result; } } } - } - + } + return result; } @SuppressWarnings({ "rawtypes", "unchecked" }) private Map retrieveFromRedisCache(ClassIDPairs classIdPairs, Boolean pleaseSetTimeout) throws Exception { Map result = new HashMap(classIdPairs.getIds().size()); - + if (cacheTimeout > 0) { IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); Class theClass = MappedClass.forName(classIdPairs.getClassName()); MappedClass mc = mcm.getMappedClassByClassName(classIdPairs.getClassName()); - + Set keys = new HashSet(classIdPairs.getIds().size()); Iterator itrIds = classIdPairs.getIds().iterator(); while (itrIds.hasNext()) { @@ -612,9 +654,9 @@ private Map retrieveFromRedisCache(ClassIDPairs classIdP keys.add(nextKey); } } - + // String key = RedisKeyUtils.classIdPair(classIdPair.getClassName(), classIdPair.getID()); - List jsonObjectStrings = redisDataStore.getValues(keys); + List jsonObjectStrings = cacheDataStore.getValues(keys); Iterator itrJsonObjectStrings = jsonObjectStrings.iterator(); while (itrJsonObjectStrings.hasNext()) { String jsonObjectString = (String) itrJsonObjectStrings.next(); @@ -628,7 +670,7 @@ private Map retrieveFromRedisCache(ClassIDPairs classIdP IPerceroObject nextPerceroObject = (IPerceroObject) safeObjectMapper.readValue(jsonObjectString, theClass); result.put( nextPerceroObject.getID(), nextPerceroObject); } - + } // else { // // Check MappedClass' child classes. @@ -644,40 +686,40 @@ private Map retrieveFromRedisCache(ClassIDPairs classIdP // } // } } - + if (pleaseSetTimeout) { - redisDataStore.expire(keys, cacheTimeout, TimeUnit.SECONDS); + cacheDataStore.expire(keys, cacheTimeout, TimeUnit.SECONDS); } } - + return result; } - + @SuppressWarnings({ "rawtypes" }) public Boolean getReadAccess(ClassIDPair classIdPair, String userId) { Session s = appSessionFactory.openSession(); try { Class theClass = MappedClass.forName(classIdPair.getClassName().toString()); - + if (theClass != null) { IPerceroObject parent = (IPerceroObject) s.get(theClass, classIdPair.getID()); - + boolean hasAccess = (parent != null); - + if (hasAccess) { IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); MappedClass mappedClass = mcm.getMappedClassByClassName(classIdPair.getClassName()); if (mappedClass != null) { if (mappedClass.getReadQuery() != null && StringUtils.hasText(mappedClass.getReadQuery().getQuery())){ Query readFilter = s.createQuery(mappedClass.getReadQuery().getQuery()); - mappedClass.getReadQuery().setQueryParameters(readFilter, parent, userId); + mappedClass.getReadQuery().setQueryParameters(readFilter.getQueryString(), parent, userId); Number readFilterResult = (Number) readFilter.uniqueResult(); if (readFilterResult == null || readFilterResult.intValue() <= 0) hasAccess = false; } } } - + return hasAccess; } } catch (Exception e) { @@ -687,32 +729,32 @@ public Boolean getReadAccess(ClassIDPair classIdPair, String userId) { } return false; } - + @SuppressWarnings({ "rawtypes" }) public Boolean getDeleteAccess(ClassIDPair classIdPair, String userId) { Session s = appSessionFactory.openSession(); try { Class theClass = MappedClass.forName(classIdPair.getClassName().toString()); - + if (theClass != null) { IPerceroObject parent = (IPerceroObject) s.get(theClass, classIdPair.getID()); - + if (parent == null) return true; - + boolean hasAccess = true; IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); MappedClass mappedClass = mcm.getMappedClassByClassName(classIdPair.getClassName()); if (mappedClass != null) { if (mappedClass.getDeleteQuery() != null && StringUtils.hasText(mappedClass.getDeleteQuery().getQuery())){ Query deleteFilter = s.createQuery(mappedClass.getDeleteQuery().getQuery()); - mappedClass.getDeleteQuery().setQueryParameters(deleteFilter, parent, userId); + mappedClass.getDeleteQuery().setQueryParameters(deleteFilter.getQueryString(), parent, userId); Number deleteFilterResult = (Number) deleteFilter.uniqueResult(); if (deleteFilterResult == null || deleteFilterResult.intValue() <= 0) hasAccess = false; } } - + return hasAccess; } } catch (Exception e) { @@ -722,11 +764,16 @@ public Boolean getDeleteAccess(ClassIDPair classIdPair, String userId) { } return false; } - - @SuppressWarnings({ "rawtypes", "unchecked" }) + + @Override public List findByIds(ClassIDPairs classIdPairs, String userId) { + return findByIds(classIdPairs, userId, false); + } + @Override + @SuppressWarnings({ "rawtypes", "unchecked" }) + public List findByIds(ClassIDPairs classIdPairs, String userId, Boolean ignoreCache) { List result = null; - + boolean hasAccess = true; Class theClass = null; try { @@ -735,7 +782,7 @@ public List findByIds(ClassIDPairs classIdPairs, String userId) log.error("Unable to get Class from class name " + classIdPairs.getClassName(), e2); return result; } - + IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); MappedClass mappedClass = mcm.getMappedClassByClassName(classIdPairs.getClassName()); boolean isValidReadQuery = false; @@ -753,14 +800,17 @@ public List findByIds(ClassIDPairs classIdPairs, String userId) try { // Attempt to get as many from the cache as possible... - Map cachedObjects = retrieveFromRedisCache(classIdPairs, true); - - if (!cachedObjects.isEmpty()) { + Map cachedObjects = null; + if (!ignoreCache) { + cachedObjects = retrieveFromRedisCache(classIdPairs, true); + } + + if (cachedObjects != null && !cachedObjects.isEmpty()) { result = new ArrayList(cachedObjects.size()); List cachedResults = new ArrayList(cachedObjects.size()); cachedResults.addAll(cachedObjects.values()); idsSet.removeAll(cachedObjects.keySet()); - + // If there is a read query, we need to check each object through that query. if (isValidReadQuery) { Session s = null; @@ -768,18 +818,18 @@ public List findByIds(ClassIDPairs classIdPairs, String userId) // Need to clean each result for the user. // TODO: Need to figure out how to NOT open the Session if we don't have to. The problem lies in the SyncHibernateUtils.cleanObject function s = appSessionFactory.openSession(); - + Iterator itrCachedResults = cachedResults.iterator(); while (itrCachedResults.hasNext()) { IPerceroObject nextCachedResult = itrCachedResults.next(); if (isValidReadQuery) { Query readFilter = s.createQuery(mappedClass.getReadQuery().getQuery()); - mappedClass.getReadQuery().setQueryParameters(readFilter, nextCachedResult, userId); + mappedClass.getReadQuery().setQueryParameters(readFilter.getQueryString(), nextCachedResult, userId); Number readFilterResult = (Number) readFilter.uniqueResult(); if (readFilterResult == null || readFilterResult.intValue() <= 0) hasAccess = false; } - + if (hasAccess) { result.add( (IPerceroObject) SyncHibernateUtils.cleanObject(nextCachedResult, s, userId) ); } @@ -801,7 +851,7 @@ public List findByIds(ClassIDPairs classIdPairs, String userId) // TODO: Need to figure out how to NOT open the Session if we don't have to. The problem lies in the SyncHibernateUtils.cleanObject function // TODO: Maybe only clean relationships that are in mappedClass.readAccessRightsFieldReferences? s = appSessionFactory.openSession(); - + Iterator itrCachedResults = cachedResults.iterator(); while (itrCachedResults.hasNext()) { IPerceroObject nextCachedResult = itrCachedResults.next(); @@ -827,7 +877,7 @@ public List findByIds(ClassIDPairs classIdPairs, String userId) // We errored out here, but we can still try and retrieve from the database. log.error("Error retrieving objects from redis cache, attempting to retrieve from database", e1); } - + // Now get the rest from the database. if (!idsSet.isEmpty()) { String queryString = "SELECT findByIdsResult FROM " + classIdPairs.getClassName() + " findByIdsResult WHERE findByIdsResult.ID IN (:idsSet)"; @@ -849,18 +899,18 @@ public List findByIds(ClassIDPairs classIdPairs, String userId) queryFilterString = queryFilterString.replaceAll(":ID", "findByIdsResult.ID"); } queryFilterString = queryString + " AND (" + queryFilterString + ") > 0"; - + query = s.createQuery(queryFilterString); - mappedClass.getReadQuery().setQueryParameters(query, null, userId); + mappedClass.getReadQuery().setQueryParameters(query.getQueryString(), null, userId); } else { query = s.createQuery(queryString); } - + query.setParameterList("idsSet", idsSet); List queryResult = query.list(); List cleanedDatabaseObjects = (List) SyncHibernateUtils.cleanObject(queryResult, s, userId); - + // Need to put results into cache. if (cacheTimeout > 0) { Map mapJsonObjectStrings = new HashMap(cleanedDatabaseObjects.size()); @@ -868,16 +918,16 @@ public List findByIds(ClassIDPairs classIdPairs, String userId) while (itrDatabaseObjects.hasNext()) { IPerceroObject nextDatabaseObject = itrDatabaseObjects.next(); String nextCacheKey = RedisKeyUtils.classIdPair(nextDatabaseObject.getClass().getCanonicalName(), nextDatabaseObject.getID()); - + mapJsonObjectStrings.put(nextCacheKey, ((BaseDataObject)nextDatabaseObject).toJson()); } - + // Store the objects in redis. - redisDataStore.setValues(mapJsonObjectStrings); + cacheDataStore.setValues(mapJsonObjectStrings); // (Re)Set the expiration. - redisDataStore.expire(mapJsonObjectStrings.keySet(), cacheTimeout, TimeUnit.SECONDS); + cacheDataStore.expire(mapJsonObjectStrings.keySet(), cacheTimeout, TimeUnit.SECONDS); } - + if (result == null) { result = cleanedDatabaseObjects; } @@ -905,7 +955,7 @@ public IPerceroObject findUnique(IPerceroObject theQueryObject, String userId) { Session s = appSessionFactory.openSession(); try { Class objectClass = theQueryObject.getClass(); - + IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); MappedClass mappedClass = mcm.getMappedClassByClassName(objectClass.getName()); if (mappedClass != null) { @@ -947,7 +997,7 @@ else if (nextConstraint instanceof List) { if (mappedClass != null) { if (mappedClass.getReadQuery() != null && StringUtils.hasText(mappedClass.getReadQuery().getQuery())){ Query readFilter = s.createQuery(mappedClass.getReadQuery().getQuery()); - mappedClass.getReadQuery().setQueryParameters(readFilter, result, userId); + mappedClass.getReadQuery().setQueryParameters(readFilter.getQueryString(), result, userId); Number readFilterResult = (Number) readFilter.uniqueResult(); if (readFilterResult == null || readFilterResult.intValue() <= 0) hasAccess = false; @@ -973,7 +1023,7 @@ else if (nextConstraint instanceof List) { // TODO: Add permissions check. @SuppressWarnings({ "unchecked" }) - public List findByExample(IPerceroObject theQueryObject, List excludeProperties, String userId) { + public List findByExample(IPerceroObject theQueryObject, List excludeProperties, String userId, Boolean shellOnly) { Session s = appSessionFactory.openSession(); try { Criteria criteria = s.createCriteria(theQueryObject.getClass()); @@ -994,7 +1044,7 @@ public List findByExample(IPerceroObject theQueryObject, List systemFindByExample(IPerceroObject theQueryObject, List excludeProperties) { Session s = appSessionFactory.openSession(); @@ -1004,23 +1054,23 @@ public List systemFindByExample(IPerceroObject theQueryObject, L BaseDataObjectPropertySelector propertySelector = new BaseDataObjectPropertySelector(excludeProperties); example.setPropertySelector(propertySelector); criteria.add(example); - + List result = criteria.list(); result = (List) SyncHibernateUtils.cleanObject(result, s); - + return (List) result; } catch (Exception e) { log.error("Unable to findByExample", e); } finally { s.close(); } - + return null; } @SuppressWarnings("unchecked") public List searchByExample(IPerceroObject theQueryObject, - List excludeProperties, String userId) { + List excludeProperties, String userId) { Session s = appSessionFactory.openSession(); try { Criteria criteria = s.createCriteria(theQueryObject.getClass()); @@ -1032,7 +1082,7 @@ public List searchByExample(IPerceroObject theQueryObject, List result = criteria.list(); result = (List) SyncHibernateUtils.cleanObject(result, s, userId); - + return result; } catch (Exception e) { log.error("Unable to searchByExample", e); @@ -1047,7 +1097,7 @@ public List searchByExample(IPerceroObject theQueryObject, public IPerceroObject systemCreateObject(IPerceroObject perceroObject) throws SyncException { Session s = appSessionFactory.openSession(); - + try { // Make sure object has an ID. IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); @@ -1064,14 +1114,14 @@ public IPerceroObject systemCreateObject(IPerceroObject perceroObject) return perceroObject; } } - + Transaction tx = s.beginTransaction(); tx.begin(); s.save(perceroObject); tx.commit(); - + s.refresh(perceroObject); - + perceroObject = (IPerceroObject) SyncHibernateUtils.cleanObject(perceroObject, s); deepCleanObject(perceroObject, mappedClass, s, null); @@ -1080,8 +1130,8 @@ public IPerceroObject systemCreateObject(IPerceroObject perceroObject) if (cacheTimeout > 0) { String key = RedisKeyUtils.classIdPair(perceroObject.getClass().getCanonicalName(), perceroObject.getID()); if (cacheTimeout > 0) { - redisDataStore.setValue(key, ((BaseDataObject)perceroObject).toJson()); - redisDataStore.expire(key, cacheTimeout, TimeUnit.SECONDS); + cacheDataStore.setValue(key, ((BaseDataObject)perceroObject).toJson()); + cacheDataStore.expire(key, cacheTimeout, TimeUnit.SECONDS); } Set keysToDelete = new HashSet(); @@ -1107,9 +1157,9 @@ else if (fieldObject instanceof Collection) { } } } - Iterator itrToOneFields = mappedClass.toOneFields.iterator(); + Iterator itrToOneFields = mappedClass.toOneFields.iterator(); while(itrToOneFields.hasNext()) { - MappedField nextMappedField = itrToOneFields.next(); + MappedFieldPerceroObject nextMappedField = itrToOneFields.next(); Object fieldObject = nextMappedField.getGetter().invoke(perceroObject); if (fieldObject != null) { if (fieldObject instanceof IPerceroObject) { @@ -1128,26 +1178,26 @@ else if (fieldObject instanceof Collection) { } } } - + if (!keysToDelete.isEmpty()) { - redisDataStore.deleteKeys(keysToDelete); + cacheDataStore.deleteKeys(keysToDelete); // TODO: Do we simply delete the key? Or do we refetch the object here and update the key? //redisDataStore.setValue(nextKey, ((BaseDataObject)perceroObject).toJson()); } } - + return perceroObject; } catch(PropertyValueException pve) { log.error("Error creating object", pve); - + SyncDataException sde = new SyncDataException(SyncDataException.MISSING_REQUIRED_FIELD, SyncDataException.MISSING_REQUIRED_FIELD_CODE, "Missing required field " + pve.getPropertyName()); sde.fieldName = pve.getPropertyName(); throw sde; } catch(Exception e) { log.error("Error creating object", e); - + SyncDataException sde = new SyncDataException(SyncDataException.CREATE_OBJECT_ERROR, SyncDataException.CREATE_OBJECT_ERROR_CODE); throw sde; } @@ -1157,7 +1207,7 @@ else if (fieldObject instanceof Collection) { } } } - + private void deepCleanObject(IPerceroObject perceroObject, MappedClass mappedClass, Session s, String userId) { if (!(perceroObject instanceof IRootObject)) { for(MappedField nextMappedField : mappedClass.externalizableFields) { @@ -1190,6 +1240,7 @@ else if (nextMappedField instanceof MappedFieldMap) { } } + @SuppressWarnings("unchecked") public IPerceroObject createObject(IPerceroObject perceroObject, String userId) throws SyncException { boolean hasAccess = true; IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); @@ -1198,8 +1249,16 @@ public IPerceroObject createObject(IPerceroObject perceroObject, String userId) if (mappedClass.getCreateQuery() != null && StringUtils.hasText(mappedClass.getCreateQuery().getQuery())){ Session s = appSessionFactory.openSession(); try { + IMappedQuery createQuery = mappedClass.getCreateQuery(); Query createFilter = s.createQuery(mappedClass.getCreateQuery().getQuery()); - mappedClass.getCreateQuery().setQueryParameters(createFilter, perceroObject, userId); + if (createQuery instanceof JpqlQuery) { + createFilter = ((JpqlQuery)createFilter).createQuery(perceroObject, userId, null, (SessionImpl) s); + } + else { + String createFilterString = mappedClass.getCreateQuery().setQueryParameters(createFilter.getQueryString(), perceroObject, userId); + createFilter = s.createQuery(createFilterString); + } + Number createFilterResult = (Number) createFilter.uniqueResult(); if (createFilterResult == null || createFilterResult.intValue() <= 0) hasAccess = false; @@ -1219,12 +1278,13 @@ public IPerceroObject createObject(IPerceroObject perceroObject, String userId) } } - + //////////////////////////////////////////////////// // PUT //////////////////////////////////////////////////// - public IPerceroObject putObject(IPerceroObject perceroObject, Map> changedFields, String userId) throws SyncException { + @SuppressWarnings("unchecked") + public T putObject(T perceroObject, Map> changedFields, String userId) throws SyncException { boolean hasAccess = true; IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); MappedClass mappedClass = mcm.getMappedClassByClassName(perceroObject.getClass().getName()); @@ -1233,7 +1293,7 @@ public IPerceroObject putObject(IPerceroObject perceroObject, Map> changedFields) throws SyncException { Session s = null; @@ -1278,113 +1337,14 @@ public IPerceroObject systemPutObject(IPerceroObject perceroObject, Map 0) { - // TODO: Also need to update the caches of anything object that is related to this object. - String key = RedisKeyUtils.classIdPair(perceroObject.getClass().getCanonicalName(), perceroObject.getID()); - if (redisDataStore.hasKey(key)) { - redisDataStore.setValue(key, ((BaseDataObject)perceroObject).toJson()); - } - - // Iterate through each changed object and reset the cache for that object. - if (changedFields != null) { -// Iterator>> itrChangedFieldEntrySet = changedFields.entrySet().iterator(); -// Set keysToDelete = new HashSet(); -// while (itrChangedFieldEntrySet.hasNext()) { -// Map.Entry> nextEntry = itrChangedFieldEntrySet.next(); -// ClassIDPair thePair = nextEntry.getKey(); -// if (!thePair.comparePerceroObject(perceroObject)) { -// String nextKey = RedisKeyUtils.classIdPair(thePair.getClassName(), thePair.getID()); -// keysToDelete.add(nextKey); -// } -// } - Iterator itrChangedFieldKeyset = changedFields.keySet().iterator(); - Set keysToDelete = new HashSet(); - while (itrChangedFieldKeyset.hasNext()) { - ClassIDPair thePair = itrChangedFieldKeyset.next(); - if (!thePair.comparePerceroObject(perceroObject)) { - String nextKey = RedisKeyUtils.classIdPair(thePair.getClassName(), thePair.getID()); - keysToDelete.add(nextKey); - } - } - - if (!keysToDelete.isEmpty()) { - redisDataStore.deleteKeys(keysToDelete); - // TODO: Do we simply delete the key? Or do we refetch the object here and update the key? - //redisDataStore.setValue(nextKey, ((BaseDataObject)perceroObject).toJson()); - } - } - else { - // No changedFields? We should never get here? - IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); - MappedClass mappedClass = mcm.getMappedClassByClassName(perceroObject.getClass().getName()); - Iterator itrToManyFields = mappedClass.toManyFields.iterator(); - while(itrToManyFields.hasNext()) { - MappedField nextMappedField = itrToManyFields.next(); - Object fieldObject = nextMappedField.getGetter().invoke(perceroObject); - if (fieldObject != null) { - if (fieldObject instanceof IPerceroObject) { - String nextKey = RedisKeyUtils.classIdPair(fieldObject.getClass().getCanonicalName(), ((IPerceroObject)fieldObject).getID()); - if (redisDataStore.hasKey(nextKey)) { - redisDataStore.deleteKey(nextKey); - // TODO: Do we simply delete the key? Or do we refetch the object here and update the key? - //redisDataStore.setValue(nextKey, ((BaseDataObject)perceroObject).toJson()); - } - } - else if (fieldObject instanceof Collection) { - Iterator itrFieldObject = ((Collection) fieldObject).iterator(); - while(itrFieldObject.hasNext()) { - Object nextListObject = itrFieldObject.next(); - if (nextListObject instanceof IPerceroObject) { - String nextKey = RedisKeyUtils.classIdPair(nextListObject.getClass().getCanonicalName(), ((IPerceroObject)nextListObject).getID()); - if (redisDataStore.hasKey(nextKey)) { - redisDataStore.deleteKey(nextKey); - // TODO: Do we simply delete the key? Or do we refetch the object here and update the key? - //redisDataStore.setValue(nextKey, ((BaseDataObject)perceroObject).toJson()); - } - } - } - } - } - } - Iterator itrToOneFields = mappedClass.toOneFields.iterator(); - while(itrToOneFields.hasNext()) { - MappedField nextMappedField = itrToOneFields.next(); - Object fieldObject = nextMappedField.getGetter().invoke(perceroObject); - if (fieldObject != null) { - if (fieldObject instanceof IPerceroObject) { - String nextKey = RedisKeyUtils.classIdPair(fieldObject.getClass().getCanonicalName(), ((IPerceroObject)fieldObject).getID()); - if (redisDataStore.hasKey(nextKey)) { - redisDataStore.deleteKey(nextKey); - // TODO: Do we simply delete the key? Or do we refetch the object here and update the key? - //redisDataStore.setValue(nextKey, ((BaseDataObject)perceroObject).toJson()); - } - } - else if (fieldObject instanceof Collection) { - Iterator itrFieldObject = ((Collection) fieldObject).iterator(); - while(itrFieldObject.hasNext()) { - Object nextListObject = itrFieldObject.next(); - if (nextListObject instanceof IPerceroObject) { - String nextKey = RedisKeyUtils.classIdPair(nextListObject.getClass().getCanonicalName(), ((IPerceroObject)nextListObject).getID()); - if (redisDataStore.hasKey(nextKey)) { - redisDataStore.deleteKey(nextKey); - // TODO: Do we simply delete the key? Or do we refetch the object here and update the key? - //redisDataStore.setValue(nextKey, ((BaseDataObject)perceroObject).toJson()); - } - } - } - } - } - } - } - } + cacheManager.updateCachedObject(perceroObject, changedFields); return perceroObject; } catch (Exception e) { log.error("Error putting object", e); - + SyncDataException sde = new SyncDataException(SyncDataException.UPDATE_OBJECT_ERROR, SyncDataException.UPDATE_OBJECT_ERROR_CODE); throw sde; } finally { @@ -1392,7 +1352,7 @@ else if (fieldObject instanceof Collection) { } } - + //////////////////////////////////////////////////// // DELETE @@ -1405,14 +1365,14 @@ public Boolean deleteObject(ClassIDPair theClassIdPair, String userId) throws Sy if (perceroObject == null) { return true; } - + boolean hasAccess = true; IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); MappedClass mappedClass = mcm.getMappedClassByClassName(theClassIdPair.getClassName()); if (mappedClass != null) { if (mappedClass.getDeleteQuery() != null && StringUtils.hasText(mappedClass.getDeleteQuery().getQuery())){ Query deleteFilter = s.createQuery(mappedClass.getDeleteQuery().getQuery()); - mappedClass.getDeleteQuery().setQueryParameters(deleteFilter, perceroObject, userId); + mappedClass.getDeleteQuery().setQueryParameters(deleteFilter.getQueryString(), perceroObject, userId); Number deleteFilterResult = (Number) deleteFilter.uniqueResult(); if (deleteFilterResult == null || deleteFilterResult.intValue() <= 0) hasAccess = false; @@ -1422,7 +1382,7 @@ public Boolean deleteObject(ClassIDPair theClassIdPair, String userId) throws Sy if (hasAccess) { if (systemDeleteObject(perceroObject)) { return true; - } + } else { return true; } @@ -1438,7 +1398,6 @@ public Boolean deleteObject(ClassIDPair theClassIdPair, String userId) throws Sy } } - @SuppressWarnings({ "unchecked", "rawtypes" }) public Boolean systemDeleteObject(IPerceroObject perceroObject) throws SyncException { Boolean result = false; Session s = appSessionFactory.openSession(); @@ -1450,77 +1409,19 @@ public Boolean systemDeleteObject(IPerceroObject perceroObject) throws SyncExcep // Make sure all associated objects are loaded. // Clean the object before deletion because otherwise it will fail. perceroObject = (IPerceroObject) SyncHibernateUtils.cleanObject(perceroObjectToDelete, s); - + Transaction tx = s.beginTransaction(); tx.begin(); s.delete(perceroObjectToDelete); tx.commit(); - // Now delete from cache. - // Now update the cache. - // TODO: Field-level updates could be REALLY useful here. Would avoid A TON of UNNECESSARY work... - if (cacheTimeout > 0) { - Set keysToDelete = new HashSet(); - - String key = RedisKeyUtils.classIdPair(SyncHibernateUtils.getShell(perceroObject).getClass().getCanonicalName(), perceroObject.getID()); - keysToDelete.add(key); - - IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); - MappedClass mappedClass = mcm.getMappedClassByClassName(SyncHibernateUtils.getShell(perceroObject).getClass().getName()); - Iterator itrToManyFields = mappedClass.toManyFields.iterator(); - while(itrToManyFields.hasNext()) { - MappedField nextMappedField = itrToManyFields.next(); - Object fieldObject = nextMappedField.getGetter().invoke(perceroObject); - if (fieldObject != null) { - if (fieldObject instanceof IPerceroObject) { - String nextKey = RedisKeyUtils.classIdPair(fieldObject.getClass().getCanonicalName(), ((IPerceroObject)fieldObject).getID()); - keysToDelete.add(nextKey); - } - else if (fieldObject instanceof Collection) { - Iterator itrFieldObject = ((Collection) fieldObject).iterator(); - while(itrFieldObject.hasNext()) { - Object nextListObject = itrFieldObject.next(); - if (nextListObject instanceof IPerceroObject) { - String nextKey = RedisKeyUtils.classIdPair(nextListObject.getClass().getCanonicalName(), ((IPerceroObject)nextListObject).getID()); - keysToDelete.add(nextKey); - } - } - } - } - } - Iterator itrToOneFields = mappedClass.toOneFields.iterator(); - while(itrToOneFields.hasNext()) { - MappedField nextMappedField = itrToOneFields.next(); - Object fieldObject = nextMappedField.getGetter().invoke(perceroObject); - if (fieldObject != null) { - if (fieldObject instanceof IPerceroObject) { - String nextKey = RedisKeyUtils.classIdPair(fieldObject.getClass().getCanonicalName(), ((IPerceroObject)fieldObject).getID()); - keysToDelete.add(nextKey); - } - else if (fieldObject instanceof Collection) { - Iterator itrFieldObject = ((Collection) fieldObject).iterator(); - while(itrFieldObject.hasNext()) { - Object nextListObject = itrFieldObject.next(); - if (nextListObject instanceof IPerceroObject) { - String nextKey = RedisKeyUtils.classIdPair(nextListObject.getClass().getCanonicalName(), ((IPerceroObject)nextListObject).getID()); - keysToDelete.add(nextKey); - } - } - } - } - } - - if (!keysToDelete.isEmpty()) { - redisDataStore.deleteKeys(keysToDelete); - // TODO: Do we simply delete the key? Or do we refetch the object here and update the key? - //redisDataStore.setValue(nextKey, ((BaseDataObject)perceroObject).toJson()); - } - } + // Now delete from cache and update related objects. + cacheManager.handleDeletedObject(perceroObject, SyncHibernateUtils.getShell(perceroObject).getClass().getName(), false); result = true; } catch (Exception e) { log.error("Error deleting object", e); - + SyncDataException sde = new SyncDataException(SyncDataException.DELETE_OBJECT_ERROR, SyncDataException.DELETE_OBJECT_ERROR_CODE); throw sde; } finally { @@ -1550,14 +1451,14 @@ public Object cleanObject(Object object, Session s, String userId) { } } } - - + + @SuppressWarnings("rawtypes") public Map> getChangedMappedFields(IPerceroObject newObject) { Map> result = new HashMap>(); Collection baseObjectResult = null; ClassIDPair basePair = new ClassIDPair(newObject.getID(), newObject.getClass().getCanonicalName()); - + String className = newObject.getClass().getCanonicalName(); IPerceroObject oldObject = systemGetById(new ClassIDPair(newObject.getID(), className)); IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); @@ -1573,12 +1474,12 @@ public Map> getChangedMappedFields(IPercero result.put(basePair, baseObjectResult); } baseObjectResult.add(nextMappedField); - + // If this is a relationship field, then need to grab the old and new values. if (nextMappedField.getReverseMappedField() != null) { if (nextMappedField instanceof MappedFieldPerceroObject) { MappedFieldPerceroObject nextMappedFieldPerceroObject = (MappedFieldPerceroObject) nextMappedField; - + IPerceroObject oldReversePerceroObject = (IPerceroObject) nextMappedFieldPerceroObject.getGetter().invoke(oldObject); if (oldReversePerceroObject != null) { ClassIDPair oldReversePair = new ClassIDPair(oldReversePerceroObject.getID(), oldReversePerceroObject.getClass().getCanonicalName()); @@ -1589,7 +1490,7 @@ public Map> getChangedMappedFields(IPercero } oldReverseChangedFields.add(nextMappedField.getReverseMappedField()); } - + IPerceroObject newReversePerceroObject = (IPerceroObject) nextMappedFieldPerceroObject.getGetter().invoke(newObject); if (newReversePerceroObject != null) { ClassIDPair newReversePair = new ClassIDPair(newReversePerceroObject.getID(), newReversePerceroObject.getClass().getCanonicalName()); @@ -1603,22 +1504,22 @@ public Map> getChangedMappedFields(IPercero } else if (nextMappedField instanceof MappedFieldList) { MappedFieldList nextMappedFieldList = (MappedFieldList) nextMappedField; - + List oldReverseList = (List) nextMappedFieldList.getGetter().invoke(oldObject); if (oldReverseList == null) oldReverseList = new ArrayList(); - + List newReverseList = (List) nextMappedFieldList.getGetter().invoke(newObject); if (newReverseList == null) newReverseList = new ArrayList(); - + // Compare each item in the lists. Collection oldChangedList = retrieveObjectsNotInCollection(oldReverseList, newReverseList); Iterator itrOldChangedList = oldChangedList.iterator(); while (itrOldChangedList.hasNext()) { BaseDataObject nextOldChangedObject = (BaseDataObject) itrOldChangedList.next(); ClassIDPair nextOldReversePair = BaseDataObject.toClassIdPair(nextOldChangedObject); - + // Old object is not in new list, so add to list of changed fields. Collection changedFields = result.get(nextOldReversePair); if (changedFields == null) { @@ -1633,7 +1534,7 @@ else if (nextMappedField instanceof MappedFieldList) { while (itrNewChangedList.hasNext()) { BaseDataObject nextNewChangedObject = (BaseDataObject) itrNewChangedList.next(); ClassIDPair nextNewReversePair = BaseDataObject.toClassIdPair(nextNewChangedObject); - + // Old object is not in new list, so add to list of changed fields. Collection changedFields = result.get(nextNewReversePair); if (changedFields == null) { @@ -1650,10 +1551,10 @@ else if (nextMappedField instanceof MappedFieldList) { baseObjectResult.add(nextMappedField); } } - + return result; } - + @SuppressWarnings({ "rawtypes", "unchecked" }) private Collection retrieveObjectsNotInCollection(Collection baseList, Collection compareToList) { Collection result = new HashSet(); @@ -1665,24 +1566,109 @@ private Collection retrieveObjectsNotInCollection(Collection baseList, Collectio BaseDataObject nextBasePerceroObject = (BaseDataObject) itrBaseList.next(); ClassIDPair nextBasePair = BaseDataObject.toClassIdPair(nextBasePerceroObject); nextBasePerceroObject = (BaseDataObject) systemGetById(nextBasePair); - + itrCompareToList = compareToList.iterator(); matchFound = false; while (itrCompareToList.hasNext()) { BaseDataObject nextCompareToPerceroObject = (BaseDataObject) itrCompareToList.next(); nextCompareToPerceroObject = (BaseDataObject) systemGetById(BaseDataObject.toClassIdPair(nextCompareToPerceroObject)); - + if (nextBasePerceroObject.getClass() == nextCompareToPerceroObject.getClass() && nextBasePerceroObject.getID().equalsIgnoreCase(nextCompareToPerceroObject.getID())) { matchFound = true; break; } } - + if (!matchFound) { result.add(nextBasePerceroObject); } } + + return result; + } + + + @Override + public List findAllRelatedObjects( + IPerceroObject perceroObject, MappedField mappedField, + Boolean shellOnly, String userId) throws SyncException { + List result = new ArrayList(); + + if (!StringUtils.hasText(perceroObject.getID())) { + // No valid ID on the object, so can't search for it. + return result; + } + + if (mappedField.getMappedClass().getSourceMappedFields().contains(mappedField)) { + // This object is the source. + IPerceroObject thisObject = this.findById(BaseDataObject.toClassIdPair(perceroObject), userId); + IPerceroObject relatedObject; + try { + relatedObject = (IPerceroObject) mappedField.getGetter().invoke(thisObject); + } catch (Exception e) { + throw new SyncException(e); + } + if (relatedObject != null) { + if (!shellOnly) { + MappedClass relatedMappedClass = MappedClassManagerFactory.getMappedClassManager().getMappedClassByClassName(relatedObject.getClass().getCanonicalName()); + relatedObject = relatedMappedClass.getDataProvider().findById(BaseDataObject.toClassIdPair(relatedObject), userId); + } + result.add(relatedObject); + } + } + else { + // This object is the target. + // The reverse mapped field should be the MappedField on the target object, the one that this MUST be the data provider for. + MappedField reverseMappedField = mappedField.getReverseMappedField(); + if (reverseMappedField == null) { + // No reverse mapped field, meaning there is nothing to do. + return result; + } + + IDataProvider dataProvider = reverseMappedField.getMappedClass().getDataProvider(); + result = dataProvider.getAllByRelationship(reverseMappedField, BaseDataObject.toClassIdPair(perceroObject), shellOnly, userId); + } return result; } + + public List getAllByRelationship(MappedField mappedField, ClassIDPair targetClassIdPair, Boolean shellOnly, String userId) throws SyncException { +// throw new SyncException(SyncException.METHOD_UNSUPPORTED, SyncException.METHOD_UNSUPPORTED_CODE); + try { + if (mappedField.getMappedClass().getSourceMappedFields().contains(mappedField)) { + // This object is the source. + IPerceroObject exampleObject = (IPerceroObject) mappedField.getMappedClass().clazz.newInstance(); + IPerceroObject targetObject = (IPerceroObject) Class.forName(targetClassIdPair.getClassName()).newInstance(); + targetObject.setID(targetClassIdPair.getID()); + mappedField.getSetter().invoke(exampleObject, targetObject); + return findByExample(exampleObject, null, userId, shellOnly); + } + else { + // This object is the target. + if (mappedField.getReverseMappedField() != null) { + IDataProvider dataProvider = mappedField.getReverseMappedField().getMappedClass().getDataProvider(); + IPerceroObject targetObject = (IPerceroObject) Class.forName(targetClassIdPair.getClassName()).newInstance(); + return dataProvider.findAllRelatedObjects(targetObject, mappedField.getReverseMappedField(), shellOnly, userId); + } + else { + return new ArrayList(0); + } + } + } catch (Exception e) { + throw new SyncException(e); + } + } + + @Override + public IPerceroObject cleanObject(IPerceroObject perceroObject, + String userId) throws SyncException { + throw new SyncException(SyncException.METHOD_UNSUPPORTED, SyncException.METHOD_UNSUPPORTED_CODE); + } + + @Override + public List cleanObject( + List perceroObjects, String userId) + throws SyncException { + throw new SyncException(SyncException.METHOD_UNSUPPORTED, SyncException.METHOD_UNSUPPORTED_CODE); + } } diff --git a/src/main/java/com/percero/agents/sync/services/SyncAgentService.java b/src/main/java/com/percero/agents/sync/services/SyncAgentService.java index 120ca8e..b7f20a9 100644 --- a/src/main/java/com/percero/agents/sync/services/SyncAgentService.java +++ b/src/main/java/com/percero/agents/sync/services/SyncAgentService.java @@ -12,6 +12,8 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; import java.util.UUID; import javax.annotation.PostConstruct; @@ -27,9 +29,9 @@ import com.percero.agents.sync.access.IAccessManager; import com.percero.agents.sync.access.RedisKeyUtils; -import com.percero.agents.sync.cw.IChangeWatcherHelper; import com.percero.agents.sync.cw.IChangeWatcherHelperFactory; -import com.percero.agents.sync.datastore.RedisDataStore; +import com.percero.agents.sync.cw.IChangeWatcherValueHelper; +import com.percero.agents.sync.datastore.ICacheDataStore; import com.percero.agents.sync.events.SyncEvent; import com.percero.agents.sync.exceptions.ClientException; import com.percero.agents.sync.exceptions.SyncDataException; @@ -87,9 +89,9 @@ public void setSafeObjectMapper(SafeObjectMapper value) { } @Autowired - RedisDataStore redisDataStore; - public void setRedisDataStore(RedisDataStore value) { - redisDataStore = value; + ICacheDataStore cacheDataStore; + public void setCacheDataStore(ICacheDataStore value) { + cacheDataStore = value; } @Autowired @@ -252,37 +254,36 @@ public String getDataId(String clientId) throws Exception { if (!isValidClient) throw new ClientException(ClientException.INVALID_CLIENT, ClientException.INVALID_CLIENT_CODE); - String dataId = (String) redisDataStore.listIndex(RedisKeyUtils.dataRecord()); + String dataId = (String) cacheDataStore.listIndex(RedisKeyUtils.dataRecord()); if (dataId == null) { dataId = UUID.randomUUID().toString(); - redisDataStore.lpushListValue(RedisKeyUtils.dataRecord(), dataId); + cacheDataStore.lpushListValue(RedisKeyUtils.dataRecord(), dataId); } return dataId; } - public PerceroList getAllByName(Object aName, Boolean returnTotal, String clientId) throws Exception { - return getAllByName(aName, null, null, returnTotal, clientId); + public PerceroList getAllByName(String className, Boolean returnTotal, String clientId) throws Exception { + return getAllByName(className, null, null, returnTotal, clientId); } @SuppressWarnings("rawtypes") - public PerceroList getAllByName(Object aName, Integer pageNumber, Integer pageSize, Boolean returnTotal, String clientId) throws Exception { + public PerceroList getAllByName(String className, Integer pageNumber, Integer pageSize, Boolean returnTotal, String clientId) throws Exception { Boolean isValidClient = accessManager.validateClientByClientId(clientId); if (!isValidClient) throw new ClientException(ClientException.INVALID_CLIENT, ClientException.INVALID_CLIENT_CODE); - String aClassName = aName.toString(); - Class theClass = MappedClass.forName(aClassName); + Class theClass = MappedClass.forName(className); // Get the MappedClass and determine which DataProvider provides data for this object. IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); - MappedClass mappedClass = mcm.getMappedClassByClassName(aName.toString()); + MappedClass mappedClass = mcm.getMappedClassByClassName(className); IDataProvider dataProvider = dataProviderManager.getDataProviderByName(mappedClass.dataProviderName); String userId = accessManager.getClientUserId(clientId); - PerceroList result = dataProvider.getAllByName(aName, pageNumber, pageSize, returnTotal, userId); + PerceroList result = dataProvider.getAllByName(className, pageNumber, pageSize, returnTotal, userId); if (result != null) { // Register an Zero ID object to indicate that this user wants updates to ALL objects of this type. @@ -379,18 +380,16 @@ public Object runProcess(String processName, Object[] queryArguments, String cli return result; } - public Object getChangeWatcher(ClassIDPair classIdPair, String fieldName, String[] params, String clientId) throws Exception { + public Object getChangeWatcherValue(ClassIDPair classIdPair, String fieldName, String[] params, String clientId) throws Exception { Object result = null; Boolean isValidClient = accessManager.validateClientByClientId(clientId); if (!isValidClient) throw new ClientException(ClientException.INVALID_CLIENT, ClientException.INVALID_CLIENT_CODE); - if (changeWatcherHelperFactory != null) - { - IChangeWatcherHelper cwh = changeWatcherHelperFactory.getHelper(classIdPair.getClassName()); - result = cwh.get(fieldName, classIdPair, params, clientId); - } + // Only ChangeWatcherValueHelper's have the "get" function. + IChangeWatcherValueHelper cwh = (IChangeWatcherValueHelper) changeWatcherHelperFactory.getHelper(classIdPair.getClassName()); + result = cwh.get(fieldName, classIdPair, params, clientId); return result; } @@ -413,20 +412,20 @@ public Object findByExample(Object theQueryObject, if (mappedClass != null) { String userId = accessManager.getClientUserId(clientId); IDataProvider dataProvider = dataProviderManager.getDataProviderByName(mappedClass.dataProviderName); - return dataProvider.findByExample((IPerceroObject) theQueryObject, excludeProperties, userId); + return dataProvider.findByExample((IPerceroObject) theQueryObject, excludeProperties, userId, false); } } return null; } - public List systemFindByExample(Object theQueryObject, List excludeProperties) { + public List systemFindByExample(Object theQueryObject, List excludeProperties) throws SyncException { if (theQueryObject instanceof IPerceroObject) { IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); MappedClass mappedClass = mcm.getMappedClassByClassName(theQueryObject.getClass().getCanonicalName()); if (mappedClass != null) { IDataProvider dataProvider = dataProviderManager.getDataProviderByName(mappedClass.dataProviderName); - return dataProvider.systemFindByExample((IPerceroObject) theQueryObject, excludeProperties); + return dataProvider.findByExample((IPerceroObject) theQueryObject, excludeProperties, null, false); } } @@ -446,7 +445,11 @@ public Object findUnique(Object theQueryObject, String clientId) throws Exceptio String userId = accessManager.getClientUserId(clientId); IDataProvider dataProvider = dataProviderManager.getDataProviderByName(mappedClass.dataProviderName); - IPerceroObject result = dataProvider.findUnique((IPerceroObject) theQueryObject, userId); + List results = dataProvider.findByExample((IPerceroObject) theQueryObject, null, userId, false); + IPerceroObject result = null; + if (results != null && !results.isEmpty()) { + result = results.get(0); + } if (result != null) { postGetHelper.postGetObject(result, userId, clientId); @@ -475,7 +478,7 @@ public Object searchByExample(Object theQueryObject, String userId = accessManager.getClientUserId(clientId); IDataProvider dataProvider = dataProviderManager.getDataProviderByName(mappedClass.dataProviderName); - List result = dataProvider.searchByExample((IPerceroObject) theQueryObject, excludeProperties, userId); + List result = dataProvider.findByExample((IPerceroObject) theQueryObject, excludeProperties, userId, false); if (result != null && result.size() > 0) postGetHelper.postGetObject(result, userId, clientId); @@ -517,7 +520,7 @@ public IPerceroObject systemGetById(String aClassName, String anId) {//throws Ex MappedClass mappedClass = mcm.getMappedClassByClassName(aClassName); IDataProvider dataProvider = dataProviderManager.getDataProviderByName(mappedClass.dataProviderName); - IPerceroObject result = dataProvider.systemGetById(new ClassIDPair(anId, aClassName)); + IPerceroObject result = dataProvider.findById(new ClassIDPair(anId, aClassName), null); return result; } @@ -528,12 +531,13 @@ public IPerceroObject systemGetById(ClassIDPair cip) { //throws Exception { MappedClass mappedClass = mcm.getMappedClassByClassName(cip.getClassName()); IDataProvider dataProvider = dataProviderManager.getDataProviderByName(mappedClass.dataProviderName); - IPerceroObject result = dataProvider.systemGetById(cip); + IPerceroObject result = dataProvider.findById(cip, null); return result; } - public T systemGetByObject(IPerceroObject perceroObject) {//throws Exception { + @SuppressWarnings("unchecked") + public T systemGetByObject(T perceroObject) {//throws Exception { // Get the MappedClass and determine which DataProvider provides data for this object. if (perceroObject == null || !StringUtils.hasText(perceroObject.getID())) { return null; @@ -543,7 +547,7 @@ public T systemGetByObject(IPerceroObject perceroObje MappedClass mappedClass = mcm.getMappedClassByClassName(className); IDataProvider dataProvider = dataProviderManager.getDataProviderByName(mappedClass.dataProviderName); - T result = dataProvider.systemGetById(new ClassIDPair(perceroObject.getID(), className)); + T result = (T) dataProvider.findById(new ClassIDPair(perceroObject.getID(), className), null); return result; } @@ -559,7 +563,7 @@ public T systemGetByObject(IPerceroObject perceroObje * @return List * @throws Exception */ - public List getHistory(ClassIDPair classIdPair, String clientId) throws Exception { + public List getHistory(ClassIDPair classIdPair, String clientId) throws Exception { return getHistory(classIdPair.getClassName(), classIdPair.getID(), clientId); } @@ -575,7 +579,7 @@ public List getHistory(ClassIDPair classIdPair, String clientId) throws * @return List * @throws Exception */ - public List getHistory(String aClassName, String anId, String clientId) throws Exception { + public List getHistory(String aClassName, String anId, String clientId) throws Exception { Boolean isValidClient = accessManager.validateClientByClientId(clientId); if (!isValidClient) throw new ClientException(ClientException.INVALID_CLIENT, ClientException.INVALID_CLIENT_CODE); @@ -590,7 +594,7 @@ public List getHistory(String aClassName, String anId, String clientId) if (hasReadAccess) { // Get the Historical Objects. - return redisDataStore.listAll(RedisKeyUtils.historicalObject(aClassName, anId)); + return cacheDataStore.listAll(RedisKeyUtils.historicalObject(aClassName, anId)); } return null; @@ -647,7 +651,7 @@ public ServerResponse putObject(IPerceroObject perceroObject, String transaction // Get the most recent ObjectModJournal, only if the UpdateDate is set. if (updateDate != null) { try { - objectModJournal = (ObjectModJournal) redisDataStore.listIndex(RedisKeyUtils.objectModJournal(perceroObject.getClass().getName(), perceroObject.getID())); + objectModJournal = (ObjectModJournal) cacheDataStore.listIndex(RedisKeyUtils.objectModJournal(perceroObject.getClass().getName(), perceroObject.getID())); } catch(Exception e) { log.warn("Unable to retrieve most recent objectModJournal", e); } @@ -694,7 +698,7 @@ public ServerResponse putObject(IPerceroObject perceroObject, String transaction newModJournal.setClassID(result.getID()); newModJournal.setClassName(result.getClass().getName()); - redisDataStore.lpushListValue(RedisKeyUtils.objectModJournal(perceroObject.getClass().getCanonicalName(), perceroObject.getID()), newModJournal); + cacheDataStore.lpushListValue(RedisKeyUtils.objectModJournal(perceroObject.getClass().getCanonicalName(), perceroObject.getID()), newModJournal); // Also store historical record, if necessary. // Get the Current object if this is a BaseHistoryObject. @@ -709,7 +713,7 @@ public ServerResponse putObject(IPerceroObject perceroObject, String transaction historyObject.setObjectChangerId(userId); historyObject.setObjectData(safeObjectMapper.writeValueAsString(result)); - redisDataStore.lpushListValue(RedisKeyUtils.historicalObject(result.getClass().getCanonicalName(), result.getID()), historyObject); + cacheDataStore.lpushListValue(RedisKeyUtils.historicalObject(result.getClass().getCanonicalName(), result.getID()), historyObject); } if (taskExecutor != null && false) { @@ -757,7 +761,7 @@ public boolean systemPutObject(IPerceroObject perceroObject, String transactionI try { // Get the most recent ObjectModJournal. try { - objectModJournal = (ObjectModJournal) redisDataStore.listIndex(RedisKeyUtils.objectModJournal(perceroObject.getClass().getName(), perceroObject.getID())); + objectModJournal = (ObjectModJournal) cacheDataStore.listIndex(RedisKeyUtils.objectModJournal(perceroObject.getClass().getName(), perceroObject.getID())); } catch(Exception e) { log.warn("Unable to retrieve most recent objectModJournal", e); } @@ -784,7 +788,7 @@ public boolean systemPutObject(IPerceroObject perceroObject, String transactionI IDataProvider dataProvider = dataProviderManager.getDataProviderByName(mappedClass.dataProviderName); changedFields = dataProvider.getChangedMappedFields(perceroObject); if (changedFields == null || changedFields.size() > 0) { - result = dataProvider.systemPutObject(perceroObject, changedFields); + result = dataProvider.putObject(perceroObject, changedFields, null); if (result != null) { // Now record the updated object. @@ -797,7 +801,7 @@ public boolean systemPutObject(IPerceroObject perceroObject, String transactionI newModJournal.setClassName(result.getClass().getName()); newModJournal.setDateModified(updateDate); - redisDataStore.lpushListValue(RedisKeyUtils.objectModJournal(result.getClass().getCanonicalName(), result.getID()), newModJournal); + cacheDataStore.lpushListValue(RedisKeyUtils.objectModJournal(result.getClass().getCanonicalName(), result.getID()), newModJournal); // Also store historical record, if necessary. // Get the Current object if this is a BaseHistoryObject. @@ -812,7 +816,7 @@ public boolean systemPutObject(IPerceroObject perceroObject, String transactionI historyObject.setObjectChangerId(userId); historyObject.setObjectData(safeObjectMapper.writeValueAsString(result)); - redisDataStore.lpushListValue(RedisKeyUtils.historicalObject(result.getClass().getCanonicalName(), result.getID()), historyObject); + cacheDataStore.lpushListValue(RedisKeyUtils.historicalObject(result.getClass().getCanonicalName(), result.getID()), historyObject); //syncSession.save(historyObject); } @@ -875,7 +879,7 @@ public ServerResponse createObject(IPerceroObject perceroObject, String clientId newModJournal.setClassName(result.getClass().getName()); try { - redisDataStore.lpushListValue(RedisKeyUtils.objectModJournal(result.getClass().getCanonicalName(), result.getID()), newModJournal); + cacheDataStore.lpushListValue(RedisKeyUtils.objectModJournal(result.getClass().getCanonicalName(), result.getID()), newModJournal); } catch(Exception e) { log.error("Unable to save mod journal to redis cache", e); } @@ -894,7 +898,7 @@ public ServerResponse createObject(IPerceroObject perceroObject, String clientId historyObject.setObjectData(safeObjectMapper.writeValueAsString(result)); try { - redisDataStore.lpushListValue(RedisKeyUtils.historicalObject(result.getClass().getCanonicalName(), result.getID()), historyObject); + cacheDataStore.lpushListValue(RedisKeyUtils.historicalObject(result.getClass().getCanonicalName(), result.getID()), historyObject); } catch(Exception e) { log.error("Unable to save history object to redis cache", e); } @@ -929,7 +933,7 @@ public ServerResponse createObject(IPerceroObject perceroObject, String clientId return response; } - public T systemCreateObject(IPerceroObject perceroObject, String userId) throws SyncException { + public T systemCreateObject(T perceroObject, String userId) throws SyncException { log.debug("Create: " + perceroObject.getClass().getName() + ": " + perceroObject.getID()); @@ -938,7 +942,7 @@ public T systemCreateObject(IPerceroObject perceroObj IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); MappedClass mappedClass = mcm.getMappedClassByClassName(perceroObject.getClass().getName()); IDataProvider dataProvider = dataProviderManager.getDataProviderByName(mappedClass.dataProviderName); - T result = dataProvider.systemCreateObject(perceroObject); + T result = dataProvider.createObject(perceroObject, null); if (result != null) { Date updateDate = new Date(); @@ -956,7 +960,7 @@ public T systemCreateObject(IPerceroObject perceroObj newModJournal.setClassName(result.getClass().getName()); try { - redisDataStore.lpushListValue(RedisKeyUtils.objectModJournal(result.getClass().getCanonicalName(), result.getID()), newModJournal); + cacheDataStore.lpushListValue(RedisKeyUtils.objectModJournal(result.getClass().getCanonicalName(), result.getID()), newModJournal); } catch(Exception e) { log.error("Unable to save mod journal to redis cache", e); } @@ -975,7 +979,7 @@ public T systemCreateObject(IPerceroObject perceroObj historyObject.setObjectData(safeObjectMapper.writeValueAsString(result)); try { - redisDataStore.lpushListValue(RedisKeyUtils.historicalObject(result.getClass().getCanonicalName(), result.getID()), historyObject); + cacheDataStore.lpushListValue(RedisKeyUtils.historicalObject(result.getClass().getCanonicalName(), result.getID()), historyObject); } catch(Exception e) { log.error("Unable to save history object to redis cache", e); } @@ -1037,7 +1041,7 @@ public ServerResponse deleteObject(ClassIDPair theClassIdPair, String clientId, hasAccess = dataProvider.getDeleteAccess(theClassIdPair, userId); if (hasAccess) { - IPerceroObject perceroObject = dataProvider.systemGetById(theClassIdPair); + IPerceroObject perceroObject = dataProvider.findById(theClassIdPair, null); if (perceroObject == null) { response.setIsSuccessful(true); return response; @@ -1080,7 +1084,7 @@ public boolean systemDeleteObject(IPerceroObject perceroObject, String clientId, if (mappedClass != null) { IDataProvider dataProvider = dataProviderManager.getDataProviderByName(mappedClass.dataProviderName); - perceroObject = (IPerceroObject) dataProvider.systemGetById(new ClassIDPair(perceroObject.getID(), perceroObject.getClass().getCanonicalName())); + perceroObject = (IPerceroObject) dataProvider.findById(new ClassIDPair(perceroObject.getID(), perceroObject.getClass().getCanonicalName()), null); if (perceroObject == null) { return true; @@ -1092,17 +1096,13 @@ public boolean systemDeleteObject(IPerceroObject perceroObject, String clientId, MappedField nextRemoveMappedFieldRef = nextEntry.getKey(); try { MappedField nextMappedField = nextEntry.getValue(); -// for(MappedField nextRemoveMappedFieldRef : mappedClass.cascadeRemoveFieldReferences.keySet()) { -// try { -// // nextRemoveMappedFieldRef points to mappedClass, so need to find all objects of that type that point to this particular -// // instance perceroObejct of mappedClass. -// MappedField nextMappedField = mappedClass.cascadeRemoveFieldReferences.get(nextRemoveMappedFieldRef); if (nextMappedField == null) { // There is no direct link from mappedClass, so need to get all by example. IPerceroObject tempObject = (IPerceroObject) nextRemoveMappedFieldRef.getMappedClass().clazz.newInstance(); nextRemoveMappedFieldRef.getSetter().invoke(tempObject, perceroObject); IDataProvider dataProviderRef = dataProviderManager.getDataProviderByName(nextRemoveMappedFieldRef.getMappedClass().dataProviderName); - List referencingObjects = dataProviderRef.systemFindByExample(tempObject, null); + List referencingObjectsNew = dataProviderRef.findAllRelatedObjects(perceroObject, nextMappedField, false, null); + List referencingObjects = dataProviderRef.findByExample(tempObject, null, null, false); Iterator itrReferencingObjects = referencingObjects.iterator(); while (itrReferencingObjects.hasNext()) { IPerceroObject nextReferencingObject = itrReferencingObjects.next(); @@ -1114,7 +1114,8 @@ public boolean systemDeleteObject(IPerceroObject perceroObject, String clientId, IPerceroObject tempObject = (IPerceroObject) nextRemoveMappedFieldRef.getMappedClass().clazz.newInstance(); nextRemoveMappedFieldRef.getSetter().invoke(tempObject, perceroObject); IDataProvider dataProviderRef = dataProviderManager.getDataProviderByName(nextRemoveMappedFieldRef.getMappedClass().dataProviderName); - List referencingObjects = dataProviderRef.systemFindByExample(tempObject, null); + List referencingObjectsNew = dataProviderRef.findAllRelatedObjects(perceroObject, nextMappedField, false, null); + List referencingObjects = dataProviderRef.findByExample(tempObject, null, null, false); Iterator itrReferencingObjects = referencingObjects.iterator(); while (itrReferencingObjects.hasNext()) { IPerceroObject nextReferencingObject = itrReferencingObjects.next(); @@ -1135,17 +1136,13 @@ public boolean systemDeleteObject(IPerceroObject perceroObject, String clientId, MappedField nextToNullMappedFieldRef = nextEntry.getKey(); try { MappedField nextMappedField = nextEntry.getValue(); -// for(MappedField nextToNullMappedFieldRef : mappedClass.nulledOnRemoveFieldReferences.keySet()) { -// try { -// // nextRemoveMappedFieldRef points to mappedClass, so need to find all objects of that type that point to this particular -// // instance perceroObejct of mappedClass. -// MappedField nextMappedField = mappedClass.nulledOnRemoveFieldReferences.get(nextToNullMappedFieldRef); if (nextMappedField == null) { // There is no direct link from mappedClass, so need to get all by example. IPerceroObject tempObject = (IPerceroObject) nextToNullMappedFieldRef.getMappedClass().clazz.newInstance(); nextToNullMappedFieldRef.getSetter().invoke(tempObject, perceroObject); IDataProvider dataProviderRef = dataProviderManager.getDataProviderByName(nextToNullMappedFieldRef.getMappedClass().dataProviderName); - List referencingObjects = dataProviderRef.systemFindByExample(tempObject, null); + List referencingObjectsNew = dataProviderRef.findAllRelatedObjects(perceroObject, nextMappedField, false, null); + List referencingObjects = dataProviderRef.findByExample(tempObject, null, null, false); Iterator itrReferencingObjects = referencingObjects.iterator(); while (itrReferencingObjects.hasNext()) { IPerceroObject nextReferencingObject = itrReferencingObjects.next(); @@ -1158,7 +1155,8 @@ public boolean systemDeleteObject(IPerceroObject perceroObject, String clientId, IPerceroObject tempObject = (IPerceroObject) nextToNullMappedFieldRef.getMappedClass().clazz.newInstance(); nextToNullMappedFieldRef.getSetter().invoke(tempObject, perceroObject); IDataProvider dataProviderRef = dataProviderManager.getDataProviderByName(nextToNullMappedFieldRef.getMappedClass().dataProviderName); - List referencingObjects = dataProviderRef.systemFindByExample(tempObject, null); + List referencingObjectsNew = dataProviderRef.findAllRelatedObjects(perceroObject, nextMappedField, false, null); + List referencingObjects = dataProviderRef.findByExample(tempObject, null, null, false); Iterator itrReferencingObjects = referencingObjects.iterator(); while (itrReferencingObjects.hasNext()) { IPerceroObject nextReferencingObject = itrReferencingObjects.next(); @@ -1172,34 +1170,10 @@ public boolean systemDeleteObject(IPerceroObject perceroObject, String clientId, } } - Iterator itrToOneFieldsToUpdate = mappedClass.toOneFields.iterator(); - while (itrToOneFieldsToUpdate.hasNext()) { - MappedField nextToOneField = itrToOneFieldsToUpdate.next(); - if (nextToOneField instanceof MappedFieldPerceroObject) { - MappedFieldPerceroObject nextPerceroObjectField = (MappedFieldPerceroObject) nextToOneField; - IPerceroObject toOneObject = (IPerceroObject) nextPerceroObjectField.getGetter().invoke(perceroObject); - if (toOneObject != null) { - // Remove this object from the cache. - // TODO: ? - } - } - } - - Iterator itrToManyFieldsToUpdate = mappedClass.toManyFields.iterator(); - while (itrToManyFieldsToUpdate.hasNext()) { - MappedField nextToManyField = itrToManyFieldsToUpdate.next(); - if (nextToManyField instanceof MappedFieldPerceroObject) { - MappedFieldPerceroObject nextPerceroObjectField = (MappedFieldPerceroObject) nextToManyField; - // TODO: ? - } - else if (nextToManyField instanceof MappedFieldList) { - MappedFieldList nextListField = (MappedFieldList) nextToManyField; - // TODO: ? - } - } + Map objectsToUpdate = mappedClass.getRelatedClassIdPairMappedFieldMap(perceroObject, false); // If the result has been set to false, it means that deletion/update of one of the related objects failed. - if (result && dataProvider.systemDeleteObject(perceroObject)) { + if (result && dataProvider.deleteObject(BaseDataObject.toClassIdPair(perceroObject), null)) { // Also store historical record, if necessary. if (storeHistory && (perceroObject instanceof IHistoryObject)) { @@ -1213,7 +1187,7 @@ else if (nextToManyField instanceof MappedFieldList) { historyObject.setObjectChangerId(userId); historyObject.setObjectData(safeObjectMapper.writeValueAsString(perceroObject)); - redisDataStore.lpushListValue(RedisKeyUtils.historicalObject(perceroObject.getClass().getCanonicalName(), perceroObject.getID()), historyObject); + cacheDataStore.lpushListValue(RedisKeyUtils.historicalObject(perceroObject.getClass().getCanonicalName(), perceroObject.getID()), historyObject); } catch(Exception e) { log.warn("Unable to save HistoricalObject in deleteObject", e); } @@ -1225,6 +1199,16 @@ else if (nextToManyField instanceof MappedFieldList) { postDeleteHelper.postDeleteObject(perceroObject, userId, clientId, pushToUser); } + Iterator> itrObjectsToUpdate = objectsToUpdate.entrySet().iterator(); + while (itrObjectsToUpdate.hasNext()) { + Entry nextObjectToUpdate = itrObjectsToUpdate.next(); + Map> changedFields = new HashMap>(); + Collection changedMappedFields = new ArrayList(1); + changedMappedFields.add(nextObjectToUpdate.getValue()); + changedFields.put(nextObjectToUpdate.getKey(), changedMappedFields); + postPutHelper.postPutObject(nextObjectToUpdate.getKey(), userId, clientId, true, changedFields); + } + result = true; } else { @@ -1234,6 +1218,7 @@ else if (nextToManyField instanceof MappedFieldList) { return result; } + public void updatesReceived(ClassIDPair[] theObjects, String clientId) throws Exception { Boolean isValidClient = accessManager.validateClientByClientId(clientId); diff --git a/src/main/java/com/percero/agents/sync/vo/ClassIDPairs.java b/src/main/java/com/percero/agents/sync/vo/ClassIDPairs.java index 2a5b1b3..993c858 100644 --- a/src/main/java/com/percero/agents/sync/vo/ClassIDPairs.java +++ b/src/main/java/com/percero/agents/sync/vo/ClassIDPairs.java @@ -36,6 +36,13 @@ public void setClassName(String value) { className = value; } + public void addId(String id) { + if (ids == null) { + ids = new ArrayList(1); + } + ids.add(id); + } + public void readExternal(ObjectInput input) { try { setClassName(input.readUTF()); diff --git a/src/main/java/com/percero/amqp/PerceroAgentListener.java b/src/main/java/com/percero/amqp/PerceroAgentListener.java index d2974b5..ef8876a 100644 --- a/src/main/java/com/percero/amqp/PerceroAgentListener.java +++ b/src/main/java/com/percero/amqp/PerceroAgentListener.java @@ -36,6 +36,11 @@ */ @Component("perceroAgentListener") public class PerceroAgentListener implements MessageListener { + + private static PerceroAgentListener perceroAgentListener = null; + public static PerceroAgentListener getInstance() { + return perceroAgentListener; + } @Autowired AmqpTemplate template; @@ -114,6 +119,10 @@ public class PerceroAgentListener implements MessageListener { public static final String PROCESS_TRANSACTION = "processTransaction"; private static Logger logger = Logger.getLogger(PerceroAgentListener.class); + + public PerceroAgentListener() { + PerceroAgentListener.perceroAgentListener = this; + } /** * Message handling function diff --git a/src/main/java/com/percero/amqp/handlers/GetChangeWatcherHandler.java b/src/main/java/com/percero/amqp/handlers/GetChangeWatcherHandler.java index 87651f9..e2020fe 100644 --- a/src/main/java/com/percero/amqp/handlers/GetChangeWatcherHandler.java +++ b/src/main/java/com/percero/amqp/handlers/GetChangeWatcherHandler.java @@ -20,7 +20,7 @@ public GetChangeWatcherHandler() { public SyncResponse handleMessage(SyncRequest request, String replyTo) throws Exception { PushCWUpdateResponse response = new PushCWUpdateResponse(); PushCWUpdateRequest pushCwUpdateRequest = (PushCWUpdateRequest) request; - Object result = syncAgentService.getChangeWatcher(pushCwUpdateRequest.getClassIdPair(), pushCwUpdateRequest.getFieldName(), pushCwUpdateRequest.getParams(), pushCwUpdateRequest.getClientId()); + Object result = syncAgentService.getChangeWatcherValue(pushCwUpdateRequest.getClassIdPair(), pushCwUpdateRequest.getFieldName(), pushCwUpdateRequest.getParams(), pushCwUpdateRequest.getClientId()); response.setFieldName(pushCwUpdateRequest.getFieldName()); response.setParams(pushCwUpdateRequest.getParams()); response.setClassIdPair(pushCwUpdateRequest.getClassIdPair()); diff --git a/src/main/java/com/percero/framework/metadata/IMappedQuery.java b/src/main/java/com/percero/framework/metadata/IMappedQuery.java index 9b27f07..d306209 100644 --- a/src/main/java/com/percero/framework/metadata/IMappedQuery.java +++ b/src/main/java/com/percero/framework/metadata/IMappedQuery.java @@ -1,7 +1,5 @@ package com.percero.framework.metadata; -import org.hibernate.Query; -import org.hibernate.impl.SessionImpl; public interface IMappedQuery { @@ -13,6 +11,6 @@ public interface IMappedQuery { public boolean getUseId(); - public void setQueryParameters(Query theQuery, Object theObject, String userId) throws Exception; - public void setQueryParameters(Query theQuery, Object theObject, String userId, Object[] params, SessionImpl s) throws Exception; + public String setQueryParameters(String theQuery, Object theObject, String userId) throws Exception; + public String setQueryParameters(String theQuery, Object theObject, String userId, Object[] params) throws Exception; } diff --git a/src/main/java/com/percero/util/DateUtils.java b/src/main/java/com/percero/util/DateUtils.java new file mode 100644 index 0000000..46b5bf3 --- /dev/null +++ b/src/main/java/com/percero/util/DateUtils.java @@ -0,0 +1,14 @@ +package com.percero.util; + +public class DateUtils { + + public static java.sql.Date utilDateToSqlDate(java.util.Date utilDate) { + if (utilDate != null) { + return new java.sql.Date(utilDate.getTime()); + } + else { + return null; + } + } + +} diff --git a/src/main/resources/log4j.properties b/src/main/resources/log4j.properties index e55f929..6a6b6bf 100644 --- a/src/main/resources/log4j.properties +++ b/src/main/resources/log4j.properties @@ -1,5 +1,7 @@ log4j.rootLogger=info, stdout +log4j.category.com.percero.agents.sync.jobs=DEBUG + log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout diff --git a/src/main/resources/spring/percero-spring-config.xml b/src/main/resources/spring/percero-spring-config.xml index a7f0cff..58a00b1 100644 --- a/src/main/resources/spring/percero-spring-config.xml +++ b/src/main/resources/spring/percero-spring-config.xml @@ -44,6 +44,8 @@ + + @@ -95,6 +97,11 @@ + diff --git a/src/test/java/com/percero/agents/sync/datastore/RedisClusterCacheDataStoreTest.java b/src/test/java/com/percero/agents/sync/datastore/RedisClusterCacheDataStoreTest.java new file mode 100644 index 0000000..8e9834a --- /dev/null +++ b/src/test/java/com/percero/agents/sync/datastore/RedisClusterCacheDataStoreTest.java @@ -0,0 +1,142 @@ +package com.percero.agents.sync.datastore; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.concurrent.TimeUnit; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.data.redis.core.HashOperations; +import org.springframework.data.redis.core.ListOperations; +import org.springframework.data.redis.core.ScanOptions; +import org.springframework.data.redis.core.SetOperations; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.data.redis.core.ZSetOperations; + +import com.percero.agents.sync.services.PerceroRedisTemplate; + +@RunWith(MockitoJUnitRunner.class) +public class RedisClusterCacheDataStoreTest { + + @Mock + private PerceroRedisTemplate template; + + @InjectMocks + private RedisClusterCacheDataStore redisCacheDataStore; + + @Mock + private SetOperations setOperations; + + @Mock + private ListOperations listOperations; + + @Mock + private HashOperations hashOperations; + + @Mock + private ValueOperations valueOperations; + + @Mock + private ZSetOperations zsetOperations; + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + } + + @AfterClass + public static void tearDownAfterClass() throws Exception { + } + + @Before + public void setUp() throws Exception { +// redisCacheDataStore = new RedisCacheDataStore(); + + Mockito.when(template.opsForSet()).thenReturn(setOperations); + Mockito.when(template.opsForList()).thenReturn(listOperations); + Mockito.when(template.opsForHash()).thenReturn(hashOperations); + Mockito.when(template.opsForValue()).thenReturn(valueOperations); + Mockito.when(template.opsForZSet()).thenReturn(zsetOperations); + } + + @SuppressWarnings("unchecked") + @After + public void tearDown() throws Exception { + // Confirm that no non-cluster safe operations have been performed. + + // Sets + Mockito.verify(setOperations, Mockito.never()).union(Mockito.anyString(), Mockito.anyCollection()); + Mockito.verify(setOperations, Mockito.never()).union(Mockito.anyString(), Mockito.anyString()); + Mockito.verify(setOperations, Mockito.never()).unionAndStore(Mockito.anyString(), Mockito.anyCollection(), Mockito.anyString()); + Mockito.verify(setOperations, Mockito.never()).unionAndStore(Mockito.anyString(), Mockito.anyString(), Mockito.anyString()); + Mockito.verify(setOperations, Mockito.never()).difference(Mockito.anyString(), Mockito.anyCollection()); + Mockito.verify(setOperations, Mockito.never()).difference(Mockito.anyString(), Mockito.anyString()); + Mockito.verify(setOperations, Mockito.never()).differenceAndStore(Mockito.anyString(), Mockito.anyCollection(), Mockito.anyString()); + Mockito.verify(setOperations, Mockito.never()).differenceAndStore(Mockito.anyString(), Mockito.anyString(), Mockito.anyString()); + Mockito.verify(setOperations, Mockito.never()).intersect(Mockito.anyString(), Mockito.anyCollection()); + Mockito.verify(setOperations, Mockito.never()).intersect(Mockito.anyString(), Mockito.anyString()); + Mockito.verify(setOperations, Mockito.never()).intersectAndStore(Mockito.anyString(), Mockito.anyCollection(), Mockito.anyString()); + Mockito.verify(setOperations, Mockito.never()).intersectAndStore(Mockito.anyString(), Mockito.anyString(), Mockito.anyString()); + Mockito.verify(setOperations, Mockito.never()).move(Mockito.anyString(), Mockito.anyObject(), Mockito.anyString()); + Mockito.verify(setOperations, Mockito.never()).scan(Mockito.anyString(), Mockito.any(ScanOptions.class)); + + // ZSets + Mockito.verify(zsetOperations, Mockito.never()).unionAndStore(Mockito.anyString(), Mockito.anyCollection(), Mockito.anyString()); + Mockito.verify(zsetOperations, Mockito.never()).unionAndStore(Mockito.anyString(), Mockito.anyString(), Mockito.anyString()); + Mockito.verify(zsetOperations, Mockito.never()).intersectAndStore(Mockito.anyString(), Mockito.anyCollection(), Mockito.anyString()); + Mockito.verify(zsetOperations, Mockito.never()).intersectAndStore(Mockito.anyString(), Mockito.anyString(), Mockito.anyString()); + Mockito.verify(zsetOperations, Mockito.never()).scan(Mockito.anyString(), Mockito.any(ScanOptions.class)); + + // Hashes + Mockito.verify(hashOperations, Mockito.never()).scan(Mockito.anyString(), Mockito.any(ScanOptions.class)); + + // Value + Mockito.verify(valueOperations, Mockito.never()).multiGet(Mockito.anyCollection()); + + } + + @Test + public void testExpire() { + String key = "KEY"; + long timeout = 1000; + TimeUnit timeUnit = TimeUnit.MILLISECONDS; + + redisCacheDataStore.expire(key, timeout, timeUnit); + Mockito.verify(template, Mockito.never()).expire(Mockito.anyString(), Mockito.anyLong(), Mockito.any(TimeUnit.class)); + + redisCacheDataStore.expire(key, timeout, timeUnit, false); + Mockito.verify(template, Mockito.never()).expire(Mockito.anyString(), Mockito.anyLong(), Mockito.any(TimeUnit.class)); + + Collection keys = new ArrayList(); + keys.add("KEY_1"); + keys.add("KEY_2"); + + redisCacheDataStore.expire(keys, timeout, timeUnit); + Mockito.verify(template, Mockito.never()).expire(Mockito.anyString(), Mockito.anyLong(), Mockito.any(TimeUnit.class)); + + redisCacheDataStore.expire(keys, timeout, timeUnit, false); + Mockito.verify(template, Mockito.never()).expire(Mockito.anyString(), Mockito.anyLong(), Mockito.any(TimeUnit.class)); + + + redisCacheDataStore.expire(key, timeout, timeUnit, true); + Mockito.verify(template, Mockito.times(1)).expire(Mockito.anyString(), Mockito.anyLong(), Mockito.any(TimeUnit.class)); + + // Test exit from expire loop. + Mockito.when(template.expire(Mockito.anyString(), Mockito.anyLong(), Mockito.any(TimeUnit.class))).thenReturn(false); + redisCacheDataStore.expire(keys, timeout, timeUnit, true); + Mockito.verify(template, Mockito.times(2)).expire(Mockito.anyString(), Mockito.anyLong(), Mockito.any(TimeUnit.class)); + + // Test complete expire loop + Mockito.when(template.expire(Mockito.anyString(), Mockito.anyLong(), Mockito.any(TimeUnit.class))).thenReturn(true); + redisCacheDataStore.expire(keys, timeout, timeUnit, true); + Mockito.verify(template, Mockito.times(4)).expire(Mockito.anyString(), Mockito.anyLong(), Mockito.any(TimeUnit.class)); + } + +} diff --git a/src/test/java/com/percero/agents/sync/jobs/UpdateTableProcessorTest.java b/src/test/java/com/percero/agents/sync/jobs/UpdateTableProcessorTest.java new file mode 100644 index 0000000..c1d0757 --- /dev/null +++ b/src/test/java/com/percero/agents/sync/jobs/UpdateTableProcessorTest.java @@ -0,0 +1,140 @@ +package com.percero.agents.sync.jobs; + +import com.percero.example.Email; +import com.percero.example.Person; +import com.percero.test.utils.AuthUtil; +import com.percero.test.utils.CleanerUtil; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +/** + * Created by Jonathan Samples on 9/4/15. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = { "classpath:spring/update_table_processor.xml" }) +public class UpdateTableProcessorTest { + + @Autowired + UpdateTableProcessorFactory processorFactory; + @Autowired + UpdateTableConnectionFactory connectionFactory; + @Autowired + UpdateTablePoller poller; + @Autowired + CleanerUtil cleanerUtil; + @Autowired + AuthUtil authUtil; + + String tableName = "update_table"; + + @Before + public void before() throws Exception{ + // Disable the poller so it doesn't step on our toes + poller.enabled = false; + cleanerUtil.cleanAll(); + try(Connection connection = connectionFactory.getConnection(); + Statement statement = connection.createStatement()) + { + // Truncate the update table + String sql = "delete from " + tableName; + statement.executeUpdate(sql); + } + } + + @Test + public void getClassForTableName_NoTableAnnotation() throws Exception{ + UpdateTableProcessor processor = processorFactory.getProcessor(tableName); + Class clazz = processor.getClassForTableName("Email"); + Assert.assertEquals(Email.class, clazz); + } + + @Test + public void getClassForTableName_TableAnnotation() throws Exception{ + UpdateTableProcessor processor = processorFactory.getProcessor(tableName); + Class clazz = processor.getClassForTableName("Person"); + Assert.assertEquals(Person.class, clazz); + } + + @Test + public void getClassForTableName_NotFound() throws Exception{ + UpdateTableProcessor processor = processorFactory.getProcessor(tableName); + Class clazz = processor.getClassForTableName("NotAnEntity"); + Assert.assertNull(clazz); + } + + // Shared setup method + public void setupThreeRowsInUpdateTable() throws SQLException{ + try(Connection connection = connectionFactory.getConnection(); + Statement statement = connection.createStatement()) + { + // Add some fixture data + statement.executeUpdate("insert into " + tableName + "(tableName, rowId, type) values " + + "('Email','1','UPDATE')," + + "('Person','1','INSERT')," + + "('Block','1','DELETE')"); + } + } + + @Test + public void getRow() throws Exception { + setupThreeRowsInUpdateTable(); + UpdateTableProcessor processor = processorFactory.getProcessor(tableName); + UpdateTableRow row = processor.getRow(); + + Assert.assertNotNull(row); + Assert.assertNotNull(row.getLockId()); + Assert.assertNotNull(row.getLockDate()); + + String sql = "select * from "+tableName+" where ID="+row.getID(); + + try(Connection connection = connectionFactory.getConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery(sql)) + { + Assert.assertTrue(resultSet.next()); + Assert.assertNotNull(resultSet.getInt("lockID")); + Assert.assertNotNull(resultSet.getDate("lockDate")); + } + } + + + @Test + public void processMultipleRows() throws Exception { + setupThreeRowsInUpdateTable(); + + UpdateTableProcessor processor = processorFactory.getProcessor(tableName); + ProcessorResult result = processor.process(); + Assert.assertEquals(3, result.getTotal()); + Assert.assertEquals(0, result.getNumFailed()); + Assert.assertTrue(result.isSuccess()); + try(Connection connection = connectionFactory.getConnection(); + Statement statement = connection.createStatement()) + { + String sql = "select count(*) as 'count' from " + tableName; + ResultSet resultSet = statement.executeQuery(sql); + Assert.assertTrue(resultSet.next()); + Assert.assertEquals(0, resultSet.getInt("count")); + } + } + + @Test + public void singleRowInsertAllList(){ + + } + + @Test + public void singleRowInsertRefList(){ + + } + +} diff --git a/src/test/java/com/percero/client/AStackClient.java b/src/test/java/com/percero/client/AStackClient.java new file mode 100644 index 0000000..f9c39e1 --- /dev/null +++ b/src/test/java/com/percero/client/AStackClient.java @@ -0,0 +1,74 @@ +package com.percero.client; + +import com.percero.agents.auth.vo.AuthenticationRequest; +import com.percero.agents.auth.vo.AuthenticationResponse; +import com.percero.agents.auth.vo.UserToken; +import org.springframework.amqp.core.Message; +import org.springframework.amqp.core.MessageListener; +import org.springframework.amqp.rabbit.connection.ConnectionFactory; +import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; + +import java.util.UUID; + +/** + * Intended to maintain session with the server and listen for + * push messages. + */ +public class AStackClient { + + /** + * Some service deps + */ + private AStackRPCService rpcService; + private String clientId = UUID.randomUUID().toString(); + private ConnectionFactory amqpConnectionFactory; + public AStackClient(AStackRPCService rpcService, ConnectionFactory amqpConnectionFactory){ + this.rpcService = rpcService; + this.amqpConnectionFactory = amqpConnectionFactory; + } + + /** + * Some internal state + */ + private UserToken userToken; + + public boolean authenticateAnonymously(){ + AuthenticationRequest request = new AuthenticationRequest(); + request.setAuthProvider("anonymous"); + request.setClientId(clientId); + + AuthenticationResponse response = rpcService.authenticate(request); + userToken = response.getResult(); + boolean result = (userToken != null); + if(result){ + setupClientQueueAndStartListening(); + } + + return result; + } + + /** + * Sets up the queue to listen for messages to the client (including responses to RPC) + */ + private SimpleMessageListenerContainer listenerContainer; + private void setupClientQueueAndStartListening(){ + listenerContainer = new SimpleMessageListenerContainer(amqpConnectionFactory); + listenerContainer.setQueueNames(clientId); + listenerContainer.setMessageListener(getMessageListener()); + listenerContainer.start(); + } + + private MessageListener messageListener; + private MessageListener getMessageListener(){ + if(messageListener == null) + messageListener = new MessageListener() { + @Override + public void onMessage(Message message) { + String key = message.getMessageProperties().getReceivedRoutingKey(); + System.out.println("Got Message from server: "+key); + } + }; + + return messageListener; + } +} diff --git a/src/test/java/com/percero/client/AStackClientFactory.java b/src/test/java/com/percero/client/AStackClientFactory.java new file mode 100644 index 0000000..18f4006 --- /dev/null +++ b/src/test/java/com/percero/client/AStackClientFactory.java @@ -0,0 +1,22 @@ +package com.percero.client; + +import org.springframework.amqp.rabbit.connection.ConnectionFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Created by jonnysamps on 9/14/15. + */ +@Component +public class AStackClientFactory { + + @Autowired + AStackRPCService rpcService; + + @Autowired + ConnectionFactory connectionFactory; + + public AStackClient getClient(){ + return new AStackClient(rpcService, connectionFactory); + } +} diff --git a/src/test/java/com/percero/client/AStackClientTest.java b/src/test/java/com/percero/client/AStackClientTest.java new file mode 100644 index 0000000..517a89c --- /dev/null +++ b/src/test/java/com/percero/client/AStackClientTest.java @@ -0,0 +1,41 @@ +package com.percero.client; + +import com.percero.agents.sync.jobs.UpdateTablePoller; +import com.percero.test.utils.CleanerUtil; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import static org.junit.Assert.*; + +/** + * Created by jonnysamps on 9/14/15. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = { "classpath:spring/update_table_processor.xml" }) +public class AStackClientTest { + + @Autowired + AStackClientFactory clientFactory; + + @Autowired + CleanerUtil cleanerUtil; + + @Autowired + UpdateTablePoller poller; + + @Before + public void before(){ + poller.enabled = false; + cleanerUtil.cleanAll(); + } + + @Test + public void authenticateAnonymously(){ + AStackClient client = clientFactory.getClient(); + boolean result = client.authenticateAnonymously(); + assertTrue(result); + } +} diff --git a/src/test/java/com/percero/client/AStackRPCService.java b/src/test/java/com/percero/client/AStackRPCService.java new file mode 100644 index 0000000..0818695 --- /dev/null +++ b/src/test/java/com/percero/client/AStackRPCService.java @@ -0,0 +1,23 @@ +package com.percero.client; + +import com.percero.agents.auth.vo.AuthenticationRequest; +import com.percero.agents.auth.vo.AuthenticationResponse; +import org.springframework.amqp.core.AmqpTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Main interface for communicating to the backend through AMQP + * Created by jonnysamps on 9/13/15. + */ +@Component +public class AStackRPCService { + + @Autowired + AmqpTemplate amqpTemplate; + + public AuthenticationResponse authenticate(AuthenticationRequest request){ + return (AuthenticationResponse) amqpTemplate.convertSendAndReceive("authenticate", request); + } + +} diff --git a/src/test/java/com/percero/client/Callback.java b/src/test/java/com/percero/client/Callback.java new file mode 100644 index 0000000..ae6ac4e --- /dev/null +++ b/src/test/java/com/percero/client/Callback.java @@ -0,0 +1,9 @@ +package com.percero.client; + +/** + * Interface to define the way that the client does async processing with callbacks + * Created by jonnysamps on 9/15/15. + */ +public interface Callback { + void execute(T result); +} diff --git a/src/test/java/com/percero/example/Block.java b/src/test/java/com/percero/example/Block.java new file mode 100644 index 0000000..925bcbe --- /dev/null +++ b/src/test/java/com/percero/example/Block.java @@ -0,0 +1,86 @@ +package com.percero.example; + +import com.google.gson.JsonObject; +import com.percero.agents.sync.metadata.annotations.Externalize; +import com.percero.agents.sync.vo.BaseDataObject; +import com.percero.serial.JsonUtils; +import org.codehaus.jackson.JsonGenerationException; +import org.codehaus.jackson.annotate.JsonProperty; +import org.codehaus.jackson.map.JsonMappingException; +import org.codehaus.jackson.map.ObjectMapper; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import java.io.IOException; +import java.io.Serializable; + +@Entity +public class Block extends BaseDataObject implements Serializable{ + + ////////////////////////////////////////////////////// + // Properties + ////////////////////////////////////////////////////// + @Id + @Externalize + @Column(unique=true,name="ID") + private String ID; + @JsonProperty(value="ID") + public String getID() { + return this.ID; + } + @JsonProperty(value="ID") + public void setID(String value) { + this.ID = value; + } + + @Column + @Externalize + private String color; + public String getColor() { + return this.color; + } + public void setColor(String value) + { + this.color = value; + } + + ////////////////////////////////////////////////////// + // JSON + ////////////////////////////////////////////////////// + @Override + public String retrieveJson(ObjectMapper objectMapper) { + String objectJson = super.retrieveJson(objectMapper); + + // Properties + objectJson += ",\"color\":"; + if (getColor() == null) + objectJson += "null"; + else { + if (objectMapper == null) + objectMapper = new ObjectMapper(); + try { + objectJson += objectMapper.writeValueAsString(getColor()); + } catch (JsonGenerationException e) { + objectJson += "null"; + e.printStackTrace(); + } catch (JsonMappingException e) { + objectJson += "null"; + e.printStackTrace(); + } catch (IOException e) { + objectJson += "null"; + e.printStackTrace(); + } + } + + return objectJson; + } + + @Override + protected void fromJson(JsonObject jsonObject) { + super.fromJson(jsonObject); + + // Properties + setColor(JsonUtils.getJsonString(jsonObject, "color")); + } +} diff --git a/src/test/java/com/percero/example/Circle.java b/src/test/java/com/percero/example/Circle.java new file mode 100644 index 0000000..d398177 --- /dev/null +++ b/src/test/java/com/percero/example/Circle.java @@ -0,0 +1,114 @@ +package com.percero.example; + +import com.google.gson.JsonObject; +import com.percero.agents.sync.metadata.annotations.Externalize; +import com.percero.agents.sync.vo.BaseDataObject; +import com.percero.serial.JsonUtils; +import org.codehaus.jackson.JsonGenerationException; +import org.codehaus.jackson.annotate.JsonProperty; +import org.codehaus.jackson.map.JsonMappingException; +import org.codehaus.jackson.map.ObjectMapper; + +import javax.persistence.*; +import java.io.IOException; +import java.io.Serializable; + +@Entity +public class Circle extends BaseDataObject implements Serializable{ + + ////////////////////////////////////////////////////// + // Properties + ////////////////////////////////////////////////////// + @Id + @Externalize + @Column(unique=true,name="ID") + private String ID; + @JsonProperty(value="ID") + public String getID() { + return this.ID; + } + @JsonProperty(value="ID") + public void setID(String value) { + this.ID = value; + } + + @Column + @Externalize + private String color; + public String getColor() { + return this.color; + } + public void setColor(String value) + { + this.color = value; + } + + ////////////////////////////////////////////////////// + // Source Relationships + ////////////////////////////////////////////////////// + @Externalize + @JoinColumn(name="person_ID") + @org.hibernate.annotations.ForeignKey(name="FK_Person_person_TO_Circle") + @ManyToOne(fetch=FetchType.LAZY, optional=false) + private Person person; + public Person getPerson() { + return this.person; + } + public void setPerson(Person value) { + this.person = value; + } + + ////////////////////////////////////////////////////// + // JSON + ////////////////////////////////////////////////////// + @Override + public String retrieveJson(ObjectMapper objectMapper) { + String objectJson = super.retrieveJson(objectMapper); + + // Properties + objectJson += ",\"color\":"; + if (getColor() == null) + objectJson += "null"; + else { + if (objectMapper == null) + objectMapper = new ObjectMapper(); + try { + objectJson += objectMapper.writeValueAsString(getColor()); + } catch (JsonGenerationException e) { + objectJson += "null"; + e.printStackTrace(); + } catch (JsonMappingException e) { + objectJson += "null"; + e.printStackTrace(); + } catch (IOException e) { + objectJson += "null"; + e.printStackTrace(); + } + } + + objectJson += ",\"person\":"; + if (getPerson() == null) + objectJson += "null"; + else { + try { + objectJson += getPerson().toEmbeddedJson(); + } catch(Exception e) { + objectJson += "null"; + } + } + objectJson += ""; + + return objectJson; + } + + @Override + protected void fromJson(JsonObject jsonObject) { + super.fromJson(jsonObject); + + // Properties + setColor(JsonUtils.getJsonString(jsonObject, "color")); + + // Source Relationships + this.person = JsonUtils.getJsonPerceroObject(jsonObject, "person"); + } +} diff --git a/src/test/java/com/percero/example/Email.java b/src/test/java/com/percero/example/Email.java new file mode 100644 index 0000000..9264dac --- /dev/null +++ b/src/test/java/com/percero/example/Email.java @@ -0,0 +1,190 @@ +package com.percero.example; + +import com.google.gson.JsonObject; +import com.percero.agents.auth.vo.IUserIdentifier; +import com.percero.agents.sync.metadata.annotations.*; +import com.percero.agents.sync.vo.BaseDataObject; +import com.percero.serial.BDODeserializer; +import com.percero.serial.BDOSerializer; +import com.percero.serial.JsonUtils; +import org.codehaus.jackson.JsonGenerationException; +import org.codehaus.jackson.annotate.JsonProperty; +import org.codehaus.jackson.map.JsonMappingException; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.map.annotate.JsonDeserialize; +import org.codehaus.jackson.map.annotate.JsonSerialize; + +import javax.persistence.*; +import java.io.IOException; +import java.io.Serializable; +import java.util.Date; + + +@EntityInterface(interfaceClass=IUserIdentifier.class) +@Entity +public class Email extends BaseDataObject implements Serializable, IUserIdentifier +{ + ////////////////////////////////////////////////////// + // VERSION + ////////////////////////////////////////////////////// + @Override + public String classVersion() { + return "0.0.0"; + } + + + ////////////////////////////////////////////////////// + // ID + ////////////////////////////////////////////////////// + @Id + @Externalize + @Column(unique=true,name="ID") + private String ID; + @JsonProperty(value="ID") + public String getID() { + return this.ID; + } + @JsonProperty(value="ID") + public void setID(String value) { + this.ID = value; + } + + ////////////////////////////////////////////////////// + // Properties + ////////////////////////////////////////////////////// + @Column + @Externalize + private Date dateCreated; + public Date getDateCreated() { + return this.dateCreated; + } + public void setDateCreated(Date value) + { + this.dateCreated = value; + } + + @Column + @Externalize + private Date dateModified; + public Date getDateModified() { + return this.dateModified; + } + public void setDateModified(Date value) + { + this.dateModified = value; + } + + @Column + @PropertyInterface(entityInterfaceClass=IUserIdentifier.class, propertyName="userIdentifier", + params={@PropertyInterfaceParam(name="paradigm", value="email")} + ) + @Externalize + private String value; + public String getValue() { + return this.value; + } + public void setValue(String value) + { + this.value = value; + } + + + ////////////////////////////////////////////////////// + // Source Relationships + ////////////////////////////////////////////////////// + @RelationshipInterface(entityInterfaceClass=IUserIdentifier.class, sourceVarName="userAnchor") + @Externalize + @JsonSerialize(using=BDOSerializer.class) + @JsonDeserialize(using=BDODeserializer.class) + @JoinColumn(name="user_ID") + @org.hibernate.annotations.ForeignKey(name="FK_Person_person_TO_Email") + @ManyToOne(fetch=FetchType.LAZY, optional=false) + private Person person; + public Person getPerson() { + return this.person; + } + public void setPerson(Person value) { + this.person = value; + } + + ////////////////////////////////////////////////////// + // Target Relationships + ////////////////////////////////////////////////////// + + + + ////////////////////////////////////////////////////// + // JSON + ////////////////////////////////////////////////////// + @Override + public String retrieveJson(ObjectMapper objectMapper) { + String objectJson = super.retrieveJson(objectMapper); + + // Properties + objectJson += ",\"dateCreated\":"; + if (getDateCreated() == null) + objectJson += "null"; + else { + objectJson += getDateCreated().getTime(); + } + + objectJson += ",\"dateModified\":"; + if (getDateModified() == null) + objectJson += "null"; + else { + objectJson += getDateModified().getTime(); + } + + objectJson += ",\"value\":"; + if (getValue() == null) + objectJson += "null"; + else { + if (objectMapper == null) + objectMapper = new ObjectMapper(); + try { + objectJson += objectMapper.writeValueAsString(getValue()); + } catch (JsonGenerationException e) { + objectJson += "null"; + e.printStackTrace(); + } catch (JsonMappingException e) { + objectJson += "null"; + e.printStackTrace(); + } catch (IOException e) { + objectJson += "null"; + e.printStackTrace(); + } + } + + // Source Relationships + objectJson += ",\"person\":"; + if (getPerson() == null) + objectJson += "null"; + else { + try { + objectJson += getPerson().toEmbeddedJson(); + } catch(Exception e) { + objectJson += "null"; + } + } + objectJson += ""; + + // Target Relationships + + return objectJson; + } + + @Override + protected void fromJson(JsonObject jsonObject) { + super.fromJson(jsonObject); + + // Properties + setDateCreated(JsonUtils.getJsonDate(jsonObject, "dateCreated")); + setDateModified(JsonUtils.getJsonDate(jsonObject, "dateModified")); + setValue(JsonUtils.getJsonString(jsonObject, "value")); + + // Source Relationships + this.person = JsonUtils.getJsonPerceroObject(jsonObject, "person"); + + // Target Relationships + } +} diff --git a/src/test/java/com/percero/example/ExampleAccountHelper.java b/src/test/java/com/percero/example/ExampleAccountHelper.java new file mode 100644 index 0000000..c8eb060 --- /dev/null +++ b/src/test/java/com/percero/example/ExampleAccountHelper.java @@ -0,0 +1,21 @@ +package com.percero.example; + +import com.percero.agents.auth.helpers.AccountHelper; +import com.percero.framework.bl.ManifestHelper; +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +@Component +public class ExampleAccountHelper extends AccountHelper { + + private static final Logger log = Logger.getLogger(ExampleAccountHelper.class); + + public static final String ROLE_ADMIN = "ADMIN"; + public static final String ROLE_BASIC = "BASIC"; + + public ExampleAccountHelper() { + super(); + manifest = new ExampleManifest(); + ManifestHelper.setManifest(manifest); + } +} diff --git a/src/test/java/com/percero/example/ExampleChangeWatcherFactory.java b/src/test/java/com/percero/example/ExampleChangeWatcherFactory.java new file mode 100644 index 0000000..929d2d9 --- /dev/null +++ b/src/test/java/com/percero/example/ExampleChangeWatcherFactory.java @@ -0,0 +1,10 @@ +package com.percero.example; + +import com.percero.agents.sync.cw.ChangeWatcherHelperFactory; +import org.springframework.stereotype.Component; + + +@Component +public class ExampleChangeWatcherFactory extends ChangeWatcherHelperFactory { + +} \ No newline at end of file diff --git a/src/test/java/com/percero/example/ExampleManifest.java b/src/test/java/com/percero/example/ExampleManifest.java new file mode 100644 index 0000000..a9cf5b9 --- /dev/null +++ b/src/test/java/com/percero/example/ExampleManifest.java @@ -0,0 +1,54 @@ +package com.percero.example; + +import com.percero.framework.bl.IManifest; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Created by jonnysamps on 9/4/15. + */ +@Component +public class ExampleManifest implements IManifest { + private List classList = null; + public List getClassList() { + if (classList == null) { + classList = new ArrayList(); + // Register Classes here + classList.add(Person.class); + classList.add(PersonRole.class); + classList.add(Email.class); + classList.add(Block.class); + } + return classList; + } + + private List objectList = null; + public List getObjectList() { + if (objectList == null) { + objectList = new ArrayList(); + // Instantiate one of each model object + objectList.add(new Person()); + objectList.add(new PersonRole()); + objectList.add(new Email()); + objectList.add(new Block()); + } + return objectList; + } + + private Map uuidMap = null; + public Map getUuidMap() { + if (uuidMap == null) { + uuidMap = new HashMap(); + // Create UUID map for each model class + uuidMap.put("1", Person.class); + uuidMap.put("2", PersonRole.class); + uuidMap.put("3", Email.class); + uuidMap.put("4", Block.class); + } + return uuidMap; + } +} diff --git a/src/test/java/com/percero/example/ExampleProcessHelper.java b/src/test/java/com/percero/example/ExampleProcessHelper.java new file mode 100644 index 0000000..7318763 --- /dev/null +++ b/src/test/java/com/percero/example/ExampleProcessHelper.java @@ -0,0 +1,8 @@ +package com.percero.example; + +import com.percero.agents.sync.helpers.ProcessHelper; +import org.springframework.stereotype.Component; + +@Component +public class ExampleProcessHelper extends ProcessHelper { +} diff --git a/src/test/java/com/percero/example/Person.java b/src/test/java/com/percero/example/Person.java new file mode 100644 index 0000000..1dccf05 --- /dev/null +++ b/src/test/java/com/percero/example/Person.java @@ -0,0 +1,289 @@ +package com.percero.example; + +import com.google.gson.JsonObject; +import com.percero.agents.auth.vo.IUserAnchor; +import com.percero.agents.sync.metadata.annotations.EntityInterface; +import com.percero.agents.sync.metadata.annotations.Externalize; +import com.percero.agents.sync.metadata.annotations.PropertyInterface; +import com.percero.agents.sync.vo.BaseDataObject; +import com.percero.serial.JsonUtils; +import org.codehaus.jackson.JsonGenerationException; +import org.codehaus.jackson.annotate.JsonProperty; +import org.codehaus.jackson.map.JsonMappingException; +import org.codehaus.jackson.map.ObjectMapper; + +import javax.persistence.*; +import java.io.IOException; +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +@Entity +@Table(name = "Person") +@EntityInterface(interfaceClass=IUserAnchor.class) +public class Person extends BaseDataObject implements Serializable, IUserAnchor +{ + ////////////////////////////////////////////////////// + // VERSION + ////////////////////////////////////////////////////// + @Override + public String classVersion() { + return "0.0.0"; + } + + ////////////////////////////////////////////////////// + // Properties + ////////////////////////////////////////////////////// + @Id + @Externalize + @Column(unique=true,name="ID") + private String ID; + @JsonProperty(value="ID") + public String getID() { + return this.ID; + } + @JsonProperty(value="ID") + public void setID(String value) { + this.ID = value; + } + + @Column + @Externalize + private Date dateCreated; + public Date getDateCreated() { + return this.dateCreated; + } + public void setDateCreated(Date value) + { + this.dateCreated = value; + } + + @Column + @Externalize + private Date dateModified; + public Date getDateModified() { + return this.dateModified; + } + public void setDateModified(Date value) + { + this.dateModified = value; + } + + @Column + @PropertyInterface(entityInterfaceClass=IUserAnchor.class, propertyName="userId", params={}) + @Externalize + private String userId; + public String getUserId() { + return this.userId; + } + public void setUserId(String value) + { + this.userId = value; + } + + @Column + @PropertyInterface(entityInterfaceClass=IUserAnchor.class, propertyName="firstName", params={}) + @Externalize + private String firstName; + public String getFirstName() { + return this.firstName; + } + public void setFirstName(String value) + { + this.firstName = value; + } + + @Column + @PropertyInterface(entityInterfaceClass=IUserAnchor.class, propertyName="lastName",params={}) + @Externalize + private String lastName; + public String getLastName() { + return this.lastName; + } + public void setLastName(String value) + { + this.lastName = value; + } + + @Externalize + @OneToMany(fetch= FetchType.LAZY, targetEntity=PersonRole.class, mappedBy="person", cascade=javax.persistence.CascadeType.REMOVE) + private List personRoles; + public List getPersonRoles() { + return this.personRoles; + } + public void setPersonRoles(List value) { + this.personRoles = value; + } + + @Externalize + @OneToMany(fetch=FetchType.LAZY, targetEntity=Email.class, mappedBy="person", cascade=javax.persistence.CascadeType.REMOVE) + private List emails; + public List getEmails() { + return this.emails; + } + public void setEmails(List value) { + this.emails = value; + } + + @Externalize + @OneToMany(fetch=FetchType.LAZY, targetEntity=Circle.class, mappedBy="person", cascade=javax.persistence.CascadeType.REMOVE) + private List circles; + public List getCircles() { + return this.circles; + } + public void setCircles(List value) { + this.circles = value; + } + + ////////////////////////////////////////////////////// + // JSON + ////////////////////////////////////////////////////// + @Override + public String retrieveJson(ObjectMapper objectMapper) { + String objectJson = super.retrieveJson(objectMapper); + + // Properties + objectJson += ",\"dateCreated\":"; + if (getDateCreated() == null) + objectJson += "null"; + else { + objectJson += getDateCreated().getTime(); + } + + objectJson += ",\"dateModified\":"; + if (getDateModified() == null) + objectJson += "null"; + else { + objectJson += getDateModified().getTime(); + } + + objectJson += ",\"userId\":"; + if (getUserId() == null) + objectJson += "null"; + else { + if (objectMapper == null) + objectMapper = new ObjectMapper(); + try { + objectJson += objectMapper.writeValueAsString(getUserId()); + } catch (JsonGenerationException e) { + objectJson += "null"; + e.printStackTrace(); + } catch (JsonMappingException e) { + objectJson += "null"; + e.printStackTrace(); + } catch (IOException e) { + objectJson += "null"; + e.printStackTrace(); + } + } + + objectJson += ",\"firstName\":"; + if (getFirstName() == null) + objectJson += "null"; + else { + if (objectMapper == null) + objectMapper = new ObjectMapper(); + try { + objectJson += objectMapper.writeValueAsString(getFirstName()); + } catch (JsonGenerationException e) { + objectJson += "null"; + e.printStackTrace(); + } catch (JsonMappingException e) { + objectJson += "null"; + e.printStackTrace(); + } catch (IOException e) { + objectJson += "null"; + e.printStackTrace(); + } + } + + objectJson += ",\"lastName\":"; + if (getLastName() == null) + objectJson += "null"; + else { + if (objectMapper == null) + objectMapper = new ObjectMapper(); + try { + objectJson += objectMapper.writeValueAsString(getLastName()); + } catch (JsonGenerationException e) { + objectJson += "null"; + e.printStackTrace(); + } catch (JsonMappingException e) { + objectJson += "null"; + e.printStackTrace(); + } catch (IOException e) { + objectJson += "null"; + e.printStackTrace(); + } + } + + objectJson += ",\"personRoles\":["; + if (getPersonRoles() != null) { + int personRolesCounter = 0; + for(PersonRole nextPersonRoles : getPersonRoles()) { + if (personRolesCounter > 0) + objectJson += ","; + try { + objectJson += nextPersonRoles.toEmbeddedJson(); + personRolesCounter++; + } catch(Exception e) { + // Do nothing. + } + } + } + objectJson += "]"; + + objectJson += ",\"emails\":["; + if (getEmails() != null) { + int emailsCounter = 0; + for(Email email : getEmails()) { + if (emailsCounter > 0) + objectJson += ","; + try { + objectJson += email.toEmbeddedJson(); + emailsCounter++; + } catch(Exception e) { + // Do nothing. + } + } + } + objectJson += "]"; + + objectJson += ",\"circles\":["; + if (getCircles() != null) { + int circlesCounter = 0; + for(Circle circle : getCircles()) { + if (circlesCounter > 0) + objectJson += ","; + try { + objectJson += circle.toEmbeddedJson(); + circlesCounter++; + } catch(Exception e) { + // Do nothing. + } + } + } + objectJson += "]"; + + + + return objectJson; + } + + @Override + protected void fromJson(JsonObject jsonObject) { + super.fromJson(jsonObject); + + // Properties + setDateCreated(JsonUtils.getJsonDate(jsonObject, "dateCreated")); + setDateModified(JsonUtils.getJsonDate(jsonObject, "dateModified")); + setUserId(JsonUtils.getJsonString(jsonObject, "userId")); + setFirstName(JsonUtils.getJsonString(jsonObject, "firstName")); + setLastName(com.percero.serial.JsonUtils.getJsonString(jsonObject, "lastName")); + + this.personRoles = (List) JsonUtils.getJsonListPerceroObject(jsonObject, "personRoles"); + this.emails = (List) JsonUtils.getJsonListPerceroObject(jsonObject, "emails"); + this.circles = (List) JsonUtils.getJsonListPerceroObject(jsonObject, "circles"); + } + +} diff --git a/src/test/java/com/percero/example/PersonRole.java b/src/test/java/com/percero/example/PersonRole.java new file mode 100644 index 0000000..739b8e2 --- /dev/null +++ b/src/test/java/com/percero/example/PersonRole.java @@ -0,0 +1,180 @@ +package com.percero.example; + +import com.google.gson.JsonObject; +import com.percero.agents.auth.vo.IUserRole; +import com.percero.agents.sync.metadata.annotations.EntityInterface; +import com.percero.agents.sync.metadata.annotations.Externalize; +import com.percero.agents.sync.metadata.annotations.PropertyInterface; +import com.percero.agents.sync.metadata.annotations.RelationshipInterface; +import com.percero.agents.sync.vo.BaseDataObject; +import com.percero.serial.JsonUtils; +import org.codehaus.jackson.JsonGenerationException; +import org.codehaus.jackson.annotate.JsonProperty; +import org.codehaus.jackson.map.JsonMappingException; +import org.codehaus.jackson.map.ObjectMapper; + +import javax.persistence.*; +import java.io.IOException; +import java.io.Serializable; +import java.util.Date; + +@EntityInterface(interfaceClass=IUserRole.class) +@Entity +public class PersonRole extends BaseDataObject implements Serializable, IUserRole +{ + ////////////////////////////////////////////////////// + // VERSION + ////////////////////////////////////////////////////// + @Override + public String classVersion() { + return "0.0.0"; + } + + + ////////////////////////////////////////////////////// + // ID + ////////////////////////////////////////////////////// + @Id + @Externalize + @Column(unique=true,name="ID") + private String ID; + @JsonProperty(value="ID") + public String getID() { + return this.ID; + } + @JsonProperty(value="ID") + public void setID(String value) { + this.ID = value; + } + + ////////////////////////////////////////////////////// + // Properties + ////////////////////////////////////////////////////// + @Column + @Externalize + private Date dateCreated; + public Date getDateCreated() { + return this.dateCreated; + } + public void setDateCreated(Date value) + { + this.dateCreated = value; + } + + @Column + @Externalize + private Date dateModified; + public Date getDateModified() { + return this.dateModified; + } + public void setDateModified(Date value) + { + this.dateModified = value; + } + + @Column + @PropertyInterface(entityInterfaceClass=IUserRole.class, propertyName="roleName",params={}) + @Externalize + private String roleName; + public String getRoleName() { + return this.roleName; + } + public void setRoleName(String value) + { + this.roleName = value; + } + + + ////////////////////////////////////////////////////// + // Source Relationships + ////////////////////////////////////////////////////// + @RelationshipInterface(entityInterfaceClass=IUserRole.class, sourceVarName="userAnchor") + @Externalize + @JoinColumn(name="person_ID") + @org.hibernate.annotations.ForeignKey(name="FK_User_user_TO_UserRole") + @ManyToOne(fetch=FetchType.LAZY, optional=false) + private Person person; + public Person getPerson() { + return this.person; + } + public void setPerson(Person value) { + this.person = value; + } + + + ////////////////////////////////////////////////////// + // JSON + ////////////////////////////////////////////////////// + @Override + public String retrieveJson(ObjectMapper objectMapper) { + String objectJson = super.retrieveJson(objectMapper); + + // Properties + objectJson += ",\"dateCreated\":"; + if (getDateCreated() == null) + objectJson += "null"; + else { + objectJson += getDateCreated().getTime(); + } + + objectJson += ",\"dateModified\":"; + if (getDateModified() == null) + objectJson += "null"; + else { + objectJson += getDateModified().getTime(); + } + + objectJson += ",\"roleName\":"; + if (getRoleName() == null) + objectJson += "null"; + else { + if (objectMapper == null) + objectMapper = new ObjectMapper(); + try { + objectJson += objectMapper.writeValueAsString(getRoleName()); + } catch (JsonGenerationException e) { + objectJson += "null"; + e.printStackTrace(); + } catch (JsonMappingException e) { + objectJson += "null"; + e.printStackTrace(); + } catch (IOException e) { + objectJson += "null"; + e.printStackTrace(); + } + } + + // Source Relationships + objectJson += ",\"person\":"; + if (getPerson() == null) + objectJson += "null"; + else { + try { + objectJson += getPerson().toEmbeddedJson(); + } catch(Exception e) { + objectJson += "null"; + } + } + objectJson += ""; + + // Target Relationships + + return objectJson; + } + + @Override + protected void fromJson(JsonObject jsonObject) { + super.fromJson(jsonObject); + + // Properties + setDateCreated(JsonUtils.getJsonDate(jsonObject, "dateCreated")); + setDateModified(JsonUtils.getJsonDate(jsonObject, "dateModified")); + setRoleName(JsonUtils.getJsonString(jsonObject, "roleName")); + + // Source Relationships + this.person = JsonUtils.getJsonPerceroObject(jsonObject, "person"); + + // Target Relationships + } +} + diff --git a/src/test/java/com/percero/test/utils/AuthUtil.java b/src/test/java/com/percero/test/utils/AuthUtil.java new file mode 100644 index 0000000..43d6ecb --- /dev/null +++ b/src/test/java/com/percero/test/utils/AuthUtil.java @@ -0,0 +1,24 @@ +package com.percero.test.utils; + +import com.percero.agents.auth.vo.AuthenticationRequest; +import com.percero.agents.auth.vo.AuthenticationResponse; +import com.percero.client.AStackRPCService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Created by jonnysamps on 9/13/15. + */ +@Component +public class AuthUtil { + + @Autowired + AStackRPCService rpcService; + + public AuthenticationResponse authenticateAnonymously(){ + AuthenticationRequest request = new AuthenticationRequest(); + request.setAuthProvider("anonymous"); + AuthenticationResponse response = (AuthenticationResponse) rpcService.authenticate(request); + return response; + } +} diff --git a/src/test/java/com/percero/test/utils/CleanerUtil.java b/src/test/java/com/percero/test/utils/CleanerUtil.java new file mode 100644 index 0000000..dd2f7a7 --- /dev/null +++ b/src/test/java/com/percero/test/utils/CleanerUtil.java @@ -0,0 +1,58 @@ +package com.percero.test.utils; + +import com.percero.agents.sync.metadata.IMappedClassManager; +import com.percero.agents.sync.metadata.MappedClass; +import com.percero.agents.sync.metadata.MappedClassManagerFactory; +import com.percero.agents.sync.services.PerceroRedisTemplate; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.connection.RedisConnection; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +/** + * Created by jonnysamps on 9/14/15. + */ +@Component +public class CleanerUtil { + + @Autowired + PerceroRedisTemplate redisTemplate; + + @Autowired + SessionFactory appSessionFactory; + + /** + * Wipe the database and redis + */ + public void cleanAll(){ + cleanDB(); + cleanCache(); + } + + @Transactional + public void cleanDB(){ + Session s = null; + try { + s = appSessionFactory.getCurrentSession(); + IMappedClassManager mcm = MappedClassManagerFactory.getMappedClassManager(); + for (MappedClass mappedClass : mcm.getAllMappedClasses()) { + s.createQuery("delete from "+mappedClass.tableName).executeUpdate(); + } + } catch(Exception e){ + System.out.println(e.getMessage()); + } + } + + public void cleanCache(){ + RedisConnection connection = null; + try{ + connection = redisTemplate.getConnectionFactory().getConnection(); + connection.flushAll(); + } finally{ + if(connection != null) + connection.close(); + } + } +} diff --git a/src/test/resources/google/client_secrets.json b/src/test/resources/google/client_secrets.json new file mode 100644 index 0000000..36a97f9 --- /dev/null +++ b/src/test/resources/google/client_secrets.json @@ -0,0 +1,13 @@ +{ + "web": { + "client_id":"426306336879-m3ffk6mqt63pot5pg1kq52b7rmf2lnfo.apps.googleusercontent.com", + "auth_uri":"https://accounts.google.com/o/oauth2/auth", + "token_uri":"https://accounts.google.com/o/oauth2/token", + "auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs", + "client_email":"426306336879-m3ffk6mqt63pot5pg1kq52b7rmf2lnfo@developer.gserviceaccount.com", + "client_x509_cert_url":"https://www.googleapis.com/robot/v1/metadata/x509/426306336879-m3ffk6mqt63pot5pg1kq52b7rmf2lnfo%40developer.gserviceaccount.com", + "client_secret":"HPH9s_dgj1pLNn4VcB5ZFxre", + "redirect_uris":["https://localhost:8081/oauth2callback.html"], + "javascript_origins":["https://localhost:8081"] + } +} \ No newline at end of file diff --git a/src/test/resources/google/privatekey.p12 b/src/test/resources/google/privatekey.p12 new file mode 100644 index 0000000..bbae007 Binary files /dev/null and b/src/test/resources/google/privatekey.p12 differ diff --git a/src/test/resources/properties/test/env.properties b/src/test/resources/properties/test/env.properties new file mode 100644 index 0000000..fcc8219 --- /dev/null +++ b/src/test/resources/properties/test/env.properties @@ -0,0 +1,72 @@ +# User Device Timeout - Two Weeks +userDeviceTimeout=1209600 + +# One Minute +cacheTimeout=60 + +# Whether Active Stack should store the history of each object +storeHistory=false + +# Application Database Details +databaseProject.driverClassName=com.mysql.jdbc.Driver +databaseProject.host=localhost +databaseProject.port=3306 +databaseProject.dbname=as_example +databaseProject.username=root +databaseProject.password=root + +# AuthAgent Database Details +databaseAuth.driverClassName=com.mysql.jdbc.Driver +databaseAuth.host=localhost +databaseAuth.port=3306 +databaseAuth.dbname=as_example +databaseAuth.username=root +databaseAuth.password=root +anonAuth.enabled=false +anonAuth.roleNames= +anonAuth.code= + +# Redis +redis.host=127.0.0.1 +redis.port=6379 +redis.password= + +# RabbitMQ +gateway.rabbitmq.login=guest +gateway.rabbitmq.password=guest +gateway.rabbitmq.host=localhost +gateway.rabbitmq.port=5672 + +domain.packageToScan=com.percero.example + +# Flyway +flywayProject.CleanClass=disabled +flywayProject.InitClass=disabled +flywayProject.MigrateClass=disabled + +flywayAuth.CleanClass=disabled +flywayAuth.InitClass=disabled +flywayAuth.MigrateClass=disabled + +# OAuth +oauth.google.clientId=426306336879-m3ffk6mqt63pot5pg1kq52b7rmf2lnfo.apps.googleusercontent.com +oauth.google.clientSecret=HPH9s_dgj1pLNn4VcB5ZFxre +oauth.google.domain= +oauth.google.admin=jonnysamps@gmail.com +oauth.google.webCallbackUrl=http://localhost:8081/oauth2callback.html +oauth.google.application_name="Hello World ActiveStack" + +# FileAuth +fileAuth.fileLocation=src/main/resources/auth/users.json +fileAuth.providerID=jsonfile + +# PulseHttpAuth +pulseHttpAuth.hostPortAndContext=https://localhost:8900/auth +pulseHttpAuth.trustAllCerts=true + +# Update Table +updateTable.tableNames=update_table +updateTable.jdbcUrl=jdbc:mysql://localhost/as_example +updateTable.username=root +updateTable.password=root +updateTable.driverClassName=com.mysql.jdbc.Driver \ No newline at end of file diff --git a/src/test/resources/spring/update_table_processor.xml b/src/test/resources/spring/update_table_processor.xml new file mode 100644 index 0000000..9d0ba64 --- /dev/null +++ b/src/test/resources/spring/update_table_processor.xml @@ -0,0 +1,325 @@ + + + + + + + + + + + + classpath:properties/test/env.properties + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + hibernate.dialect=org.hibernate.dialect.MySQLDialect + hibernate.hbm2ddl.auto=update + + hibernate.show_sql=false + hibernate.format_sql=false + + hibernate.connection.aggressive_release=true + hibernate.jdbc.batch_size=20 + hibernate.connection.autocommit=false + hibernate.enable_lazy_load_no_trans=true + + + + + com.percero.amqp + com.percero.agents.auth.vo + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + hibernate.dialect=org.hibernate.dialect.MySQLDialect + hibernate.hbm2ddl.auto=update + + hibernate.show_sql=false + hibernate.format_sql=false + + hibernate.connection.aggressive_release=true + hibernate.jdbc.batch_size=20 + hibernate.connection.autocommit=false + hibernate.connection.autoReconnect=true + hibernate.enable_lazy_load_no_trans=true + + + + + $pf{domain.packageToScan} + + + + + + + + + diff --git a/src/test/resources/update_table_test_sql.md b/src/test/resources/update_table_test_sql.md new file mode 100644 index 0000000..5c486a7 --- /dev/null +++ b/src/test/resources/update_table_test_sql.md @@ -0,0 +1,109 @@ +# Single Row Insert +## ALL list - PASSING +### First try +```sql +insert into Block set ID='1block', color='#aabbcc'; +insert into update_table set rowID='1block', tableName='Block', type='INSERT'; +``` + +### Second try +```sql +insert into Block set ID='2block', color='#8899aa'; +insert into update_table set rowID='2block', tableName='Block', type='INSERT'; +``` + +## Referenced list - PASSING +### First try +```sql +insert into Circle set ID='1circle', color='#11aa22', person_ID='e3d04ee4-e996-4f4a-9c5e-7e87e5685849'; +insert into update_table set rowID='1circle', tableName='Circle', type='INSERT'; +``` + +### Second try +```sql +insert into Circle set ID='2circle', color='#22bb33', person_ID='e3d04ee4-e996-4f4a-9c5e-7e87e5685849'; +insert into update_table set rowID='2circle', tableName='Circle', type='INSERT'; +``` + +# Whole Table Insert +## ALL list - PASSING +### First try +```sql +insert into Block set ID='3block', color='#a3b3c3'; +insert into Block set ID='4block', color='#a4b4c4'; +insert into update_table set rowID=null, tableName='Block', type='INSERT'; +``` + +### Second try +```sql +insert into Block set ID='5block', color='#b5b5c5'; +insert into Block set ID='6block', color='#b6b6c6'; +insert into update_table set rowID=null, tableName='Block', type='INSERT'; +``` + +## Reference list - PASSING + +```sql +-- First Try +insert into Circle set ID='3circle', color='#ccc333', person_ID='e3d04ee4-e996-4f4a-9c5e-7e87e5685849'; +insert into Circle set ID='4circle', color='#ccc444', person_ID='e3d04ee4-e996-4f4a-9c5e-7e87e5685849'; +insert into update_table set rowID=null, tableName='Circle', type='INSERT'; + +-- Second try +insert into Circle set ID='5circle', color='#ccc555', person_ID='e3d04ee4-e996-4f4a-9c5e-7e87e5685849'; +insert into Circle set ID='6circle', color='#ccc666', person_ID='e3d04ee4-e996-4f4a-9c5e-7e87e5685849'; +insert into update_table set rowID=null, tableName='Circle', type='INSERT'; +``` + +# Single Row Update +## Value update - PASSING +```sql +-- First Try -- +update Block set color='#333333' where ID='1' ; +insert into update_table set rowID='1', tableName='Block', type='UPDATE'; + +-- Second Try -- +update Block set color='#aaaaaa' where ID='1' ; +insert into update_table set rowID='1', tableName='Block', type='UPDATE'; +``` + +## Reference List - PASSING + +Make sure that when updating references that objects are added/removed the appropriate lists + +```sql +-- First Try -- +update Circle set color='green', person_ID='a11df72c-9aa5-4f75-ba41-ce4c4539fdbe' where ID='6circle'; +insert into update_table set rowID='6circle', tableName='Circle', type='UPDATE'; + +-- Second Try -- +update Circle set color='red', person_ID='e3d04ee4-e996-4f4a-9c5e-7e87e5685849' where ID='6circle'; +insert into update_table set rowID='6circle', tableName='Circle', type='UPDATE'; +``` + +# Whole Table update +## Value update - PASSING +```sql +-- First Try -- +update Block set color='coral'; +insert into update_table set rowID=null, tableName='Block', type='UPDATE'; + +-- Second Try -- +update Block set color='lightcoral'; +insert into update_table set rowID=null, tableName='Block', type='UPDATE'; +``` + +## Reference List - +```sql +-- First Try -- +update Circle set person_ID='' where person_ID='a11df72c-9aa5-4f75-ba41-ce4c4539fdbe'; +update Circle set person_ID='a11df72c-9aa5-4f75-ba41-ce4c4539fdbe' where person_ID='e3d04ee4-e996-4f4a-9c5e-7e87e5685849'; +update Circle set person_ID='e3d04ee4-e996-4f4a-9c5e-7e87e5685849' where person_ID=''; +insert into update_table set rowID=null, tableName='Circle', type='UPDATE'; + +-- Second try -- +update Circle set person_ID=null where person_ID='a11df72c-9aa5-4f75-ba41-ce4c4539fdbe'; +update Circle set person_ID='a11df72c-9aa5-4f75-ba41-ce4c4539fdbe' where person_ID='e3d04ee4-e996-4f4a-9c5e-7e87e5685849'; +update Circle set person_ID='e3d04ee4-e996-4f4a-9c5e-7e87e5685849' where person_ID=null; +insert into update_table set rowID=null, tableName='Circle', type='UPDATE'; +``` \ No newline at end of file diff --git a/src/test/sql/example_schema.sql b/src/test/sql/example_schema.sql new file mode 100644 index 0000000..c367916 --- /dev/null +++ b/src/test/sql/example_schema.sql @@ -0,0 +1,15 @@ +CREATE DATABASE IF NOT EXISTS as_example; + +DROP TABLE IF EXISTS `update_table`; +CREATE TABLE `update_table` ( + `ID` int(11) NOT NULL AUTO_INCREMENT, + `tableName` varchar(255) DEFAULT NULL, + `rowID` varchar(255) DEFAULT NULL, + `type` enum('INSERT','UPDATE','DELETE') NOT NULL DEFAULT 'UPDATE', + `lockID` int(11) DEFAULT NULL, + `lockDate` datetime DEFAULT NULL, + `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`ID`), + KEY `tableName` (`tableName`), + KEY `lockID` (`lockID`) +); \ No newline at end of file