[ https://issues.apache.org/jira/browse/JCS-68?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=12750574#action_12750574 ]
Aaron Smuts commented on JCS-68: -------------------------------- Yes. We can restrict the constructor (if needed) and not the class. > Admin.jsp on remote cache server does not broadcast removes - patch > ------------------------------------------------------------------- > > Key: JCS-68 > URL: https://issues.apache.org/jira/browse/JCS-68 > Project: JCS > Issue Type: Bug > Components: RMI Remote Cache > Affects Versions: jcs-1.3, jcs-1.4-dev > Environment: All > Reporter: Niall Gallagher > Assignee: Aaron Smuts > Priority: Minor > Original Estimate: 0.5h > Remaining Estimate: 0.5h > > We'd like to contribute some patches to fix an issue with the JCSAdmin.jsp > when it's used on a JCS remote cache server. > We use this JSP on our remote cache server. This allows us to browse the > objects stored in our distributed cache (i.e. uploaded by the client > servers), and allows us to remove arbitrary objects from the distributed > cache by clicking 'remove' next to the key of the relevant object displayed > on the JSP. > The issue is: when we use the unmodified version of the code to remove an > object, the object is successfully removed from the JCS remote server, but > the 'remove' event for that object is not broadcast to all client machines. > Client machines which start up after we remove the object get 'null' when > they try to retrieve the object (the desired behaviour). However client > machines which were already running and using this object continue to see the > object in their view of the cache. > This problem occurs because the JCSAdminBean (used by this JSP) calls the > wrong API in JCS to remove objects from the cache when it's running on the > remote cache server. It calls the CompositeCache API, which is intended for > use client-side only. > Our patches update JCSAdminBean to call this same API when its running on a > client machine, BUT if it's running on a machine on which the JCS remote > server is enabled, it calls the RemoteCacheServer API instead. > The fix involves replacing 3 methods in org.apache.jcs.admin.JCSAdminBean as > follows: > /** > * Clears all regions in the cache. > * <p/> > * If this class is running within a remote cache server, clears all > regions via the <code>RemoteCacheServer</code> > * API, so that removes will be broadcast to client machines. Otherwise > clears all regions in the cache directly via > * the usual cache API. > */ > public void clearAllRegions() throws IOException { > if (RemoteCacheServerFactory.getRemoteCacheServer() == null) { > // Not running in a remote cache server. > // Remove objects from the cache directly, as no need to > broadcast removes to client machines... > String[] names = cacheHub.getCacheNames(); > for (int i = 0; i < names.length; i++) { > cacheHub.getCache(names[i]).removeAll(); > } > } > else { > // Running in a remote cache server. > // Remove objects via the RemoteCacheServer API, so that removes > will be broadcast to client machines... > try { > String[] cacheNames = > CompositeCacheManager.getInstance().getCacheNames(); > // Call remoteCacheServer.removeAll(String) for each > cacheName... > // Note: We must do this using reflection to bypass its > package-private access... > Object remoteCacheServerObject = > RemoteCacheServerFactory.getRemoteCacheServer(); > Method removeAllMethod = > remoteCacheServerObject.getClass().getMethod("removeAll", new > Class[]{String.class}); > boolean previouslyAccessibility = > removeAllMethod.isAccessible(); > removeAllMethod.setAccessible(true); > for (int i = 0; i < cacheNames.length; i++) { > String cacheName = cacheNames[i]; > removeAllMethod.invoke(remoteCacheServerObject, new > Object[]{cacheName}); > } > removeAllMethod.setAccessible(previouslyAccessibility); > } > catch (Exception e) { > throw new IllegalStateException("Failed to remove all > elements from all cache regions: " + e, e); > } > } > } > /** > * Clears a particular cache region. > * <p/> > * If this class is running within a remote cache server, clears the > region via the <code>RemoteCacheServer</code> > * API, so that removes will be broadcast to client machines. Otherwise > clears the region directly via the usual > * cache API. > */ > public void clearRegion(String cacheName) throws IOException { > if (cacheName == null) { > throw new IllegalArgumentException("The cache name specified was > null."); > } > if (RemoteCacheServerFactory.getRemoteCacheServer() == null) { > // Not running in a remote cache server. > // Remove objects from the cache directly, as no need to > broadcast removes to client machines... > cacheHub.getCache(cacheName).removeAll(); > } > else { > // Running in a remote cache server. > // Remove objects via the RemoteCacheServer API, so that removes > will be broadcast to client machines... > try { > // Call remoteCacheServer.removeAll(String)... > // Note: We must do this using reflection to bypass its > package-private access... > Object remoteCacheServerObject = > RemoteCacheServerFactory.getRemoteCacheServer(); > Method removeAllMethod = > remoteCacheServerObject.getClass().getMethod("removeAll", new > Class[]{String.class}); > boolean previouslyAccessibility = > removeAllMethod.isAccessible(); > removeAllMethod.setAccessible(true); > removeAllMethod.invoke(remoteCacheServerObject, new > Object[]{cacheName}); > removeAllMethod.setAccessible(previouslyAccessibility); > } > catch (Exception e) { > throw new IllegalStateException("Failed to remove all > elements from cache region [" + cacheName + "]: " + e, e); > } > } > } > /** > * Removes a particular item from a particular region. > * <p/> > * If this class is running within a remote cache server, removes the > item via the <code>RemoteCacheServer</code> > * API, so that removes will be broadcast to client machines. Otherwise > clears the region directly via the usual > * cache API. > * > * @param cacheName > * @param key > * > * @throws IOException > */ > public void removeItem(String cacheName, String key) throws IOException { > if (cacheName == null) { > throw new IllegalArgumentException("The cache name specified was > null."); > } > if (key == null) { > throw new IllegalArgumentException("The key specified was null."); > } > if (RemoteCacheServerFactory.getRemoteCacheServer() == null) { > // Not running in a remote cache server. > // Remove objects from the cache directly, as no need to > broadcast removes to client machines... > cacheHub.getCache(cacheName).remove(key); > } > else { > // Running in a remote cache server. > // Remove objects via the RemoteCacheServer API, so that removes > will be broadcast to client machines... > try { > Object keyToRemove = null; > CompositeCache cache = > CompositeCacheManager.getInstance().getCache(cacheName); > // A String key was supplied, but to remove elements via the > RemoteCacheServer API, we need the > // actual key object as stored in the cache (i.e. a > Serializable object). To find the key in this form, > // we iterate through all keys stored in the memory cache > until we find one whose toString matches > // the string supplied... > Object[] allKeysInCache = > cache.getMemoryCache().getKeyArray(); > for (int i = 0; i < allKeysInCache.length; i++) { > Object keyInCache = allKeysInCache[i]; > if (keyInCache.toString().equals(key)) { > if (keyToRemove == null) { > keyToRemove = keyInCache; > } > else { > // A key matching the one specified was already > found... > throw new IllegalStateException("Unexpectedly > found duplicate keys in the cache region matching the key specified."); > } > } > } > if (keyToRemove == null) { > throw new IllegalStateException("No match for this key > could be found in the set of keys retrieved from the memory cache."); > } > if (!(keyToRemove instanceof Serializable)) { > throw new IllegalStateException("Found key [" + > keyToRemove + ", " + keyToRemove.getClass() + "] in cache matching key > specified, however key found in cache is unexpectedly not serializable."); > } > // At this point, we have retrieved the matching Serializable > key. > // Call remoteCacheServer.remove(String, Serializable)... > // Note: We must fo this using reflection to bypass its > package-private access... > Object remoteCacheServerObject = > RemoteCacheServerFactory.getRemoteCacheServer(); > Method removeMethod = > remoteCacheServerObject.getClass().getMethod("remove", new > Class[]{String.class, Serializable.class}); > boolean previouslyAccessibility = removeMethod.isAccessible(); > removeMethod.setAccessible(true); > removeMethod.invoke(remoteCacheServerObject, new > Object[]{cacheName, keyToRemove}); > removeMethod.setAccessible(previouslyAccessibility); > } > catch (Exception e) { > throw new IllegalStateException("Failed to remove element > with key [" + key + ", " + key.getClass() + "] from cache region [" + > cacheName + "]: " + e, e); > } > } > } -- This message is automatically generated by JIRA. - You can reply to this email to add a comment to the issue online. --------------------------------------------------------------------- To unsubscribe, e-mail: jcs-dev-unsubscr...@jakarta.apache.org For additional commands, e-mail: jcs-dev-h...@jakarta.apache.org