0

I know there are quite a few CCE questions on SO. I have read the majority of them either in detail or briefly and I cannot find anything that applies to my situation. My exact error is:

Exception in thread "pool-1-thread-1" java.lang.ClassCastException: datastructures.instances.JClass cannot be cast to java.util.ArrayList at if ((results = mCallsDownstreamCache.get(origin)) == null) {

As you will see in the code, what I'm doing is asking for an ArrayList from a cache (HashMap) and then making a decision on that. The odd behavior here is that datastructures.instances.JClass is in no way referenced in the piece of code that generates the error.

To give you some context, I have a database "model" which fulfills requests from a "controller". Those results are stored in a cache local to the model and if they exist the model will return the cache thus not having to hit the db. My caching elements are effectively decorators for the Commons' JCS.

The offending line is wrapped in a block comment and inline comment

public class AnalyzeModel extends Model {

    public final String TAG = getClass().getSimpleName();
    public CacheDecorator<Integer, JClass> mClassCache = new CacheDecorator<Integer, JClass>();
    public CacheDecorator<Integer, JMethod> mMethodCache = new CacheDecorator<Integer, JMethod>();
    public CacheDecorator<Integer, ArrayList<Integer>> mCallsUpstreamCache =
            new CacheDecorator<Integer, ArrayList<Integer>>();
    public CacheDecorator<Integer, ArrayList<Integer>> mCallsDownstreamCache =
            new CacheDecorator<Integer, ArrayList<Integer>>();

    public void close() {
        super.close();
    }

    public Pair<Integer, ArrayList<Integer>> selectCallGraphDownstream(int origin) {
        ArrayList<Integer> results = new ArrayList<Integer>();

        /**
         * This is the offending line
         */
        if ((results = mCallsDownstreamCache.get(origin)) == null) {
        // End error line
            results = new ArrayList<Integer>();
            for (Record r : mQuery.select(
                    mQuery.mQuerier.select(Calls.CALLS.TID)
                            .from(Calls.CALLS)
                            .where(Calls.CALLS.SID.eq(origin)))) {
                results.add(r.getValue(Calls.CALLS.TID));
            }
            mCallsDownstreamCache.put(origin, results);
            Statistics.CACHE_MISS++;
        } else {
            Statistics.CACHE_HITS++;
        }
        return new Pair<Integer, ArrayList<Integer>>(origin, results);
    }

}

public class CacheDecorator<K, V> {

    public final String TAG = getClass().getSimpleName();

    private CacheAccess<K, V> mCache;

    public CacheDecorator() {
        try {
            mCache = JCS.getInstance("default");
        } catch (CacheException e) {
            BillBoard.e(TAG, "Error getting cache configuration: " + e.toString());
            e.printStackTrace();
        }
    }

    /**
     * Get an object from cache
     * @param obj object to retrieve from cache
     * @return generic object of retrieved value, null if not found
     */
    public V get(K obj) {
        return mCache.get(obj);
    }

    /**
     * Place an object in cache
     * @param key generic key for reference
     * @param obj generic object to be cached
     */
    public synchronized void put(K key, V obj) {
        try {
            if(obj != null) {
                mCache.putSafe(key, obj);
            }
        } catch( CacheException e) {
            //BillBoard.d(TAG, obj.toString());
            //BillBoard.e(TAG, "Error placing item in cache: " + e.toString());
            //e.printStackTrace();
        }
    }

    /**
     * Get the stats from our cache manager
     * @return String of our cache object
     */
    public String getStats() {
        shutDownCache();
        return mCache.getStats();
    }

    public static void shutDownCache() {
        CompositeCacheManager.getInstance().shutDown();
    }
}

Some additional details that may, or may not, be helpful:

  • The Pair<V, K> datastructure is just an immutable 2-pair tuple class
  • CacheDecorator.get(V obj) returns null if the object doesn't exist in cache
  • I've tried quite a bit in regards to casting and such
  • JClass does have references elsewhere in the code, but no reference in the offending method
  • JClass is a representation of a java class, it's a custom structure
lilott8
  • 1,116
  • 2
  • 17
  • 41

1 Answers1

1

By altering your config to include region-specific configs as presented in the documentation, and passing a region to your wrapper, should resolve the problem.

Looks like you are using the same cache region for all your wrappers and hence referencing the same 'under laying cache' across your wrappers. Could you change mCache = JCS.getInstance("default"); to something like mCache = JCS.getInstance("uniqueNameForWrapper"); for all your wrappers?

lilott8
  • 1,116
  • 2
  • 17
  • 41
Brimzi
  • 191
  • 2
  • 5
  • The string passed to the instance is strictly for parsing the properties config that is required for the JCS instance. So all `CacheAccess` instances will use the caching strategies defined in `default` sections of the config. – lilott8 Jun 09 '15 at 00:36
  • I was looking at the source for JCS.getInstance at [link](https://commons.apache.org/proper/commons-jcs/commons-jcs-core/apidocs/index.html) **line 162**, following the trail you end up at [link](https://commons.apache.org/proper/commons-jcs/commons-jcs-core/apidocs/index.html) **line 591**, I am not familiar with JCS and I could be wrong but it looks like that name is used as a key for the underlaying CacheAccess object – Brimzi Jun 09 '15 at 00:53
  • hmm, that could be a lead to the answer. I think the answer might actually in the use of `defineRegion` [defined](https://commons.apache.org/proper/commons-jcs/commons-jcs-core/apidocs/index.html). My decorator might be too simple. I'll do some testing. From what I've read [here](http://commons.apache.org/proper/commons-jcs/getting_started/intro.html) the `JCS.getInstance([name])` refers to the properties file that is required to define a cache. You can see I trivially decorated their simple example as a generalized interface. – lilott8 Jun 09 '15 at 03:50
  • Yes i saw that one,and I suspect each region definition maps to exactly one 'CacheAccess' object. By the way another way you can use the configuration file to define the custom regions like in [here](http://www.devx.com/Java/introduction-to-java-caching-system.html) – Brimzi Jun 09 '15 at 06:32
  • By the way another way you can define the custom regions is by using the configuration file like [here](http://www.devx.com/Java/introduction-to-java-caching-system.html). So the way I see it;Each wrapper should have its own corresponding region.One question though,did you try what I suggested?i wouldn't be surprised if it still worked – Brimzi Jun 09 '15 at 06:39
  • I'm working on implementing it now. The only argument for why this approach won't work is because I do this very same thing in other models (multiple caches in the same class using the same instance) and it all works perfectly there. – lilott8 Jun 09 '15 at 16:33
  • any additional information on this? I have a similar scenario in an older version of JCS. We are only using a single region across various instances, but using unique keys (and have verified they are truly unique). There are odd times where we should get object A back but are getting object B back instead so trying to see if this solution would apply. Unable to reproduce it in a test case as of yet which is making it hard to determine how to fix. – Michael Oct 12 '15 at 15:58