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

Niall Gallagher commented on JCS-68:
------------------------------------

FYI we updated the getByteCount method in JCSAdminBean too. This is not 
strictly necessary to fix the issue above, it's fixes a display issue only. The 
unmodified code wasn't accounting for objects which were already serialized 
(which is always the case on the remote cache server). This version accounts 
for objects which are already serialized...

    /**
     * Tries to estimate how much data is in a region. This is expensive. If 
there are any non serializable objects in
     * the region or an error occurs, suppresses exceptions and returns 0.
     * <p/>
     *
     * @return int The size of the region in bytes.
     */
    public int getByteCount(CompositeCache cache) throws Exception {
        if (cache == null) {
            throw new IllegalArgumentException("The cache object specified was 
null.");
        }
        long size = 0;
        try {
            MemoryCache memCache = cache.getMemoryCache();

            Iterator iter = memCache.getIterator();
            while (iter.hasNext()) {

                Map.Entry nextMapEntry = (Map.Entry) iter.next();
                ICacheElement ice = (ICacheElement) nextMapEntry.getValue();

                if (ice instanceof CacheElementSerialized) {
                    size = size + ((CacheElementSerialized) 
ice).getSerializedValue().length;
                }
                else {
                    Serializable element = ice.getVal();

                    //CountingOnlyOutputStream: Keeps track of the number of 
bytes written to it, but doesn't write them anywhere.
                    CountingOnlyOutputStream counter = new 
CountingOnlyOutputStream();
                    try {
                        ObjectOutputStream out = new 
ObjectOutputStream(counter);
                        out.writeObject(element);
                    }
                    catch (IOException e) {
                        throw new RuntimeException("IOException while trying to 
measure the size of the cached element", e);
                    }
                    // 4 bytes lost for the serialization header
                    size = size + counter.getCount() - 4;
                }
            }
            if (size > Integer.MAX_VALUE) {
                throw new IllegalStateException("The size of cache " + 
cache.getCacheName() + " (" + size + " bytes) is too large to be represented as 
an integer.");
            }
        }
        catch (Exception e) {
//            throw new RuntimeException("Failed to calculate the size of cache 
region [" + cache.getCacheName() + "]:" + e, e);
            return 0;
        }
        return (int) size;
    }


> 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 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