Question

I run out of ideas, Google also did not help. Use case seems to be trivial but it fails with ClassCastException. I don't know what I am doing wrong. There's a simple method to return first element matching a given category, take a look.

private Category selectElement(List<? extends Category> results, Code code) {
    return selectFirst(results, having(on(Category.class).getCode(), is(code)));
}

Execution gives this top of the stack:

java.lang.ClassCastException: name.wilu.logic.report.utils.SheetLoader$Category$$EnhancerByCGLIB$$3a35aefc cannot be cast to net.sf.cglib.proxy.Factory
at ch.lambdaj.proxy.ClassImposterizer.createProxy(ClassImposterizer.java:134)
at ch.lambdaj.proxy.ClassImposterizer.imposterise(ClassImposterizer.java:101)
at ch.lambdaj.proxy.ProxyUtil.createProxy(ProxyUtil.java:52)
at ch.lambdaj.function.argument.ArgumentsFactory.createPlaceholder(ArgumentsFactory.java:68)
at ch.lambdaj.function.argument.ArgumentsFactory.registerNewArgument(ArgumentsFactory.java:58)
at ch.lambdaj.function.argument.ArgumentsFactory.createArgument(ArgumentsFactory.java:50)
at ch.lambdaj.function.argument.ArgumentsFactory.createArgument(ArgumentsFactory.java:39)
at ch.lambdaj.Lambda.on(Lambda.java:63)

I had the same problem while using lambdaJ for manipulation over hibernate's persistent collections holding entities. I gave up assuming there might be some problem with proxying objects (entities in collection) that already are proxies. It seems I were wrong because Category and all inherited classes are pojos passsed to the hibernate as a result transformer.

What might be a reason of such behavior? Do you have any idea?

(I'm using the most recent lambdaj-2.4).

Added to fulfill Mario's request

Code is a simple enum. Category is a base class for different categories, it has code field. Moreover, it's public static class, same as all inheriting classes (if it matters).

I'll try to provide failing test.

Editing again to provide additional info. A friend of mine took a look on the code and put a fresh bright light on the issue.

I will try to reproduce our path of deduction from the very beginning.

// Given

There's an application divided into two parts, the first - base application (keeps model files) and the web application (keeps UI connected files like backing beans and so forth). Our category and code are model classes thus are located in base application. We have then a backing bean serving for the purpose of some web logic and particularly that bean or its collaborator calls our select.

// When

We're deploying application to a web server! JBoss in my case. Classes are read by loaders, some pretty complicated things that I'm not aware of happen, all to have my application running. I do some web action and that backing bean's method is called

 selectFirst(results, having(on(Category.class).getCode(), is(code)));

from web part of application.

Here comes the magic. Our Category.class and Code.class were loaded by UnifiedClassLoader on application load. We're in on(Category.class) method and the proxy of Category is going to be built. Some really tangled logic is harnessed to do that, What's the most important, proxy is instrumented with

setThreadsCallbacks(Callback[]callbacks) 

method, but Callback.class is taken from that class loader

 aCategory.getClass.getClassLoader()

thus it is a class loader that initially loaded that class, the UnifiedClassLoader . Having all this done cleanly we finally call

 getFirstInstance()

that using reflection browses Proxy class seeking for: Proxy.getDeclaredMethod("setThreadsCallbacks", new Class[]{ Callback[].class });

I omit the fact, I do not understand

 new Class[]{ Callback[].class }

What's important in our case is that Callback.class is NOT PROVIDED BY UnifiedClassLoader. Applicaiton is executed in a web tire so a call for Callback.class will be server by web app. class loader and retured Callback.class will dffer from what was previously put as an argment for mentioned setThreadsCallbacks functon. Reflection fails cruelly.

Category.class != Category.class //these two were provided by different classLoaders

That is why I was not able to provide failing test. (same class loader).

I doubt there's any solution for that case.

Was it helpful?

Solution

I have encountered a similar problem. (The same exception thrown: java.lang.ClassCastException: XXX$Category$$EnhancerByCGLIB$$XXX cannot be cast to net.sf.cglib.proxy.Factory)

In my case it turned out to be a problem with duplicated (or even tripled) CGLib library. We had as follows:

  • cglib.jar in JBoss (4.2.3) library
  • cglib-nodep.jar in our application web-library (WAR file)
  • labmdaj-2.4-with-dependencies.jar in application web-library.

Then, when using Lambda.on(SomeClass.class) we get to this method in CGLib's Enhancer class:

private static Method getCallbacksSetter(Class type, String methodName) throws NoSuchMethodException {
        return type.getDeclaredMethod(methodName, new Class[]{ Callback[].class });
}

where methodName = "CGLIB$SET_THREAD_CALLBACKS" and type is our SomeClass wrapped by Enhancer.

"CGLIB$SET_THREAD_CALLBACKS" method was present in wrapped type, but getDeclaredMethod() returned null. It appeared that inside getDeclaredMethod() there was a comparison of two net.sf.cglib.proxy.Callback.class instances. And they were different since one was loaded from cglib.jar (JBoss) and the other from cglib-nodep.jar (webapp).

The solution was to delete redundant cglib-nodep.jar and replace lambda-2.4-with-dependencies.jar with lambda-2.4.jar. Now all CGLib classes are loaded from common place and the problems have gone.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top