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


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 a 3 methods in org.apache.jcs.admin.JCSAdminBean 
with 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