[
https://issues.apache.org/jira/browse/JCS-68?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Thomas Vandahl resolved JCS-68.
-------------------------------
Resolution: Fixed
Fix Version/s: jcs-1.4-dev
Applied patches (slightly modified)
> 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
> Fix For: jcs-1.4-dev
>
> 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.
If you think it was sent incorrectly, please contact your JIRA administrators:
https://issues.apache.org/jira/secure/ContactAdministrators!default.jspa
For more information on JIRA, see: http://www.atlassian.com/software/jira