[ 
https://issues.apache.org/jira/browse/JCS-68?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=12749487#action_12749487
 ] 

Aaron Smuts commented on JCS-68:
--------------------------------

Thanks, this is a good idea.  Why are you calling the remove method via 
reflection?  That's too fragile.  Especially without any unit tests.  I need 
unit tests for all changes.  

> 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

Reply via email to