Skip to main content

How to use custom ExpressionFactory? - glassfish classloader problems

11 replies [Last post]
cmdrk
Offline
Joined: 2009-04-29
Points: 0

I want to use a custom EL ExpressionFactory to get around a problem in coerceType(...) within the default implementation of jsf-ri.

my web.xml has this:

</p>
<p>	com.sun.faces.expressionFactory<br />
	com.tuv.bielefeld.web.ExpressionFactoryImpl</p>
<p>

Now either one of these problems arise:

- If I deploy this class within WEB-INF/classes or as a jar in WEB-INF/lib, it is not found by the classloader (see stack trace below)

- Only if I put this class as a jar in glassfish's /lib folder, it gets picked up but of course it cannot find the webapps other classes. Anyway, I wouldn't want to put an application specific jar into /lib.

Seems like glassfish does not use the same classloader for loading its jsf-impl and loading deployed webapp classes.

Any ideas how I can get around this problem?

<br />
java.lang.ClassNotFoundException: com.tuv.bielefeld.web.ExpressionFactoryImpl<br />
	at com.sun.appserv.server.util.ASURLClassLoader.loadClass(ASURLClassLoader.java:129)<br />
	at java.lang.ClassLoader.loadClass(ClassLoader.java:252)<br />
	at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)<br />
	at java.lang.Class.forName0(Native Method)<br />
	at java.lang.Class.forName(Class.java:169)<br />
	at com.sun.faces.config.ConfigureListener.installExpressionFactory(ConfigureListener.java:1521)<br />
	at com.sun.faces.config.ConfigureListener.registerELResolverAndListenerWithJsp(ConfigureListener.java:1540)<br />
	at com.sun.faces.config.ConfigureListener.contextInitialized(ConfigureListener.java:403)<br />
	at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4632)<br />
	at org.apache.catalina.core.StandardContext.start(StandardContext.java:5312)<br />
	at com.sun.enterprise.web.WebModule.start(WebModule.java:353)<br />
	at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:989)<br />
	at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:973)<br />
	at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:704)<br />
	at com.sun.enterprise.web.WebContainer.loadWebModule(WebContainer.java:1627)<br />
	at com.sun.enterprise.web.WebContainer.loadWebModule(WebContainer.java:1232)<br />
	at com.sun.enterprise.web.WebContainer.loadJ2EEApplicationWebModules(WebContainer.java:1159)<br />
	at com.sun.enterprise.server.TomcatApplicationLoader.doLoad(TomcatApplicationLoader.java:141)<br />
	at com.sun.enterprise.server.ExtendedApplicationLoader.doLoad(ExtendedApplicationLoader.java:134)<br />
	at com.sun.enterprise.server.AbstractLoader.load(AbstractLoader.java:238)<br />
	at com.sun.enterprise.server.ApplicationManager.applicationDeployed(ApplicationManager.java:336)<br />
	at com.sun.enterprise.server.ApplicationManager.applicationDeployed(ApplicationManager.java:210)<br />
	at com.sun.enterprise.server.ApplicationManager.applicationDeployed(ApplicationManager.java:645)<br />
	at com.sun.enterprise.admin.event.AdminEventMulticaster.invokeApplicationDeployEventListener(AdminEventMulticaster.java:959)<br />
	at com.sun.enterprise.admin.event.AdminEventMulticaster.handleApplicationDeployEvent(AdminEventMulticaster.java:943)<br />
	at com.sun.enterprise.admin.event.AdminEventMulticaster.processEvent(AdminEventMulticaster.java:467)<br />
	at com.sun.enterprise.admin.event.AdminEventMulticaster.multicastEvent(AdminEventMulticaster.java:182)<br />
	at com.sun.enterprise.admin.server.core.DeploymentNotificationHelper.multicastEvent(DeploymentNotificationHelper.java:308)<br />
	at com.sun.enterprise.deployment.phasing.DeploymentServiceUtils.multicastEvent(DeploymentServiceUtils.java:231)<br />
	at com.sun.enterprise.deployment.phasing.ServerDeploymentTarget.sendStartEvent(ServerDeploymentTarget.java:298)<br />
	at com.sun.enterprise.deployment.phasing.ApplicationStartPhase.runPhase(ApplicationStartPhase.java:132)<br />
	at com.sun.enterprise.deployment.phasing.DeploymentPhase.executePhase(DeploymentPhase.java:108)<br />
	at com.sun.enterprise.deployment.phasing.PEDeploymentService.executePhases(PEDeploymentService.java:966)<br />
	at com.sun.enterprise.deployment.phasing.PEDeploymentService.start(PEDeploymentService.java:609)<br />
	at com.sun.enterprise.deployment.phasing.PEDeploymentService.start(PEDeploymentService.java:653)<br />
	at com.sun.enterprise.admin.mbeans.ApplicationsConfigMBean.start(ApplicationsConfigMBean.java:773)<br />
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)<br />
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)<br />
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)<br />
	at java.lang.reflect.Method.invoke(Method.java:597)<br />
	at com.sun.enterprise.admin.MBeanHelper.invokeOperationInBean(MBeanHelper.java:381)<br />
	at com.sun.enterprise.admin.MBeanHelper.invokeOperationInBean(MBeanHelper.java:364)<br />
	at com.sun.enterprise.admin.config.BaseConfigMBean.invoke(BaseConfigMBean.java:477)<br />
	at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:836)<br />
	at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:761)<br />
	at sun.reflect.GeneratedMethodAccessor14.invoke(Unknown Source)<br />
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)<br />
	at java.lang.reflect.Method.invoke(Method.java:597)<br />
	at com.sun.enterprise.admin.util.proxy.ProxyClass.invoke(ProxyClass.java:90)<br />
	at $Proxy1.invoke(Unknown Source)<br />
	at com.sun.enterprise.admin.server.core.jmx.SunoneInterceptor.invoke(SunoneInterceptor.java:304)<br />
	at com.sun.enterprise.interceptor.DynamicInterceptor.invoke(DynamicInterceptor.java:174)<br />
	at com.sun.enterprise.admin.jmx.remote.server.callers.InvokeCaller.call(InvokeCaller.java:69)<br />
	at com.sun.enterprise.admin.jmx.remote.server.MBeanServerRequestHandler.handle(MBeanServerRequestHandler.java:155)<br />
	at com.sun.enterprise.admin.jmx.remote.server.servlet.RemoteJmxConnectorServlet.processRequest(RemoteJmxConnectorServlet.java:122)<br />
	at com.sun.enterprise.admin.jmx.remote.server.servlet.RemoteJmxConnectorServlet.doPost(RemoteJmxConnectorServlet.java:193)<br />
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:754)<br />
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:847)<br />
	at org.apache.catalina.core.ApplicationFilterChain.servletService(ApplicationFilterChain.java:427)<br />
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:315)<br />
	at org.apache.catalina.core.StandardContextValve.invokeInternal(StandardContextValve.java:287)<br />
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:218)<br />
	at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:648)<br />
	at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:593)<br />
	at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:94)<br />
	at com.sun.enterprise.web.PESessionLockingStandardPipeline.invoke(PESessionLockingStandardPipeline.java:98)<br />
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:222)<br />
	at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:648)<br />
	at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:593)<br />
	at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:587)<br />
	at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:1096)<br />
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:166)<br />
	at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:648)<br />
	at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:593)<br />
	at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:587)<br />
	at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:1096)<br />
	at org.apache.coyote.tomcat5.CoyoteAdapter.service(CoyoteAdapter.java:288)<br />
	at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.invokeAdapter(DefaultProcessorTask.java:647)<br />
	at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.doProcess(DefaultProcessorTask.java:579)<br />
	at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.process(DefaultProcessorTask.java:831)<br />
	at com.sun.enterprise.web.connector.grizzly.DefaultReadTask.executeProcessorTask(DefaultReadTask.java:341)<br />
	at com.sun.enterprise.web.connector.grizzly.DefaultReadTask.doTask(DefaultReadTask.java:263)<br />
	at com.sun.enterprise.web.connector.grizzly.DefaultReadTask.doTask(DefaultReadTask.java:214)<br />
	at com.sun.enterprise.web.connector.grizzly.TaskBase.run(TaskBase.java:265)<br />
	at com.sun.enterprise.web.connector.grizzly.WorkerThreadImpl.run(WorkerThreadImpl.java:116)<br />

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
mriem
Offline
Joined: 2004-03-26
Points: 0

What problem are you trying to get around?

cmdrk
Offline
Joined: 2009-04-29
Points: 0

I use hibernate as a jpa provider. thus some objects that are used within h:selectonemenu are 'real' objects, others are just hibernate proxy objects, acting as if they were real ones. When the implementation of that selectonemenu tries to validate if a submitted value is one of the objects in that list, ExpressionFactory.coerceToType(Object obj,Class targetType) gets called.

Sometimes, obj is a real object (e.g. of class MyClass), but targetType is the class of a proxy (like MyClass$$EnhancerByCGLIB$$66ab1395_2e). In this case coerceType fails and throws illegalargumentexception which leads the selectonemenu to set its value to null.

To get around this, I made an ExpressionFactory which uses the original one as a delegate. I modified coerceToType to support proxys and use the delegate in all other cases. This works just fine but now the classloader problems above arise if I want to access my applications other classes.

mriem
Offline
Joined: 2004-03-26
Points: 0

Have you considered defining an interface that your real objects implement and then use that in the h:selectOneMenu?

cmdrk
Offline
Joined: 2009-04-29
Points: 0

I don't see how this can solve this problem. Actually all my entities implement a common interface, but hibernate proxy object are always of a synthetic subclass of the object's concrete class, not of the interface.

mriem
Offline
Joined: 2004-03-26
Points: 0

Can you post the actuall stacktrace for the IllegalArgumentException?

cmdrk
Offline
Joined: 2009-04-29
Points: 0

That IAE is caught, thus no stacktrace:

This is from UISelectOne source from jsf1.2_04
[code]
//Coerce the item value type before comparing values.
Class type = value.getClass();
Object newValue;
try {
newValue = getFacesContext().getApplication().
getExpressionFactory().coerceToType(item.getValue(), type);
} catch (Exception e) {
// this should catch an ELException, but there is a bug
// in ExpressionFactory.coerceToType() in GF
newValue = null;
}
[/code]

This is from com.sun.el.ExpressionFactory:
[code]
public Object coerceToType(Object obj, Class type) {
return ELSupport.coerceToType(obj, type);
}
[/code]

And this is from ELSupport
[code]
public final static Object coerceToType(final Object obj, final Class type)
throws IllegalArgumentException {
if (type == null || Object.class.equals(type)) {
return obj;
}
if (String.class.equals(type)) {
return coerceToString(obj);
}
if (ELArithmetic.isNumberType(type)) {
return coerceToNumber(obj, type);
}
if (Character.class.equals(type) || Character.TYPE == type) {
return coerceToCharacter(obj);
}
if (Boolean.class.equals(type) || Boolean.TYPE == type) {
return coerceToBoolean(obj);
}
if (type.isEnum()) {
return coerceToEnum(obj, type);
}
if (obj != null && type.isAssignableFrom(obj.getClass())) {
return obj;
}

// new to spec
if (obj == null)
return null;
if (obj instanceof String) {
if ("".equals(obj))
return null;
PropertyEditor editor = PropertyEditorManager.findEditor(type);
if (editor != null) {
editor.setAsText((String) obj);
return editor.getValue();
}
}
throw new IllegalArgumentException(MessageFactory.get("error.convert",
obj, obj.getClass(), type));
}

[/code]

So if item.getValue() is of type MyClass, but type is of class MyClass$$EnhancerByCGLib$3453453453 which is a subclass of MyClass, this results in newValue being set to null.
This is because type.isAssignableFrom(obj.getClass()) return false, which is is correct behaviour. But I want hibernate proxy objects being handled as if they were real objects so I implemented a custom Expressionfactory which checks first if this situation exists and passes everything else to the standard implementation.
This custom ExpressionFactory is working ok, but i don't want to put it into glassfish's lib folder. Instead I want to deploy it within the webapp, but there it won't be picked up by the classloader that is used while starting the jsf implementation.

mriem
Offline
Joined: 2004-03-26
Points: 0

Can you upgrade to the latest released version of JSF? See https://javaserverfaces.dev.java.net/servlets/ProjectDocumentList?folder...

cmdrk
Offline
Joined: 2009-04-29
Points: 0

Upgrading to _12 did the job! I looked at tha mojarra source and they changed that one line from newValue=null to newValue=value.
[code]}
} catch (IllegalArgumentException iae) {
// If coerceToType fails, per the docs it should throw
// an ELException, however, GF 9.0 and 9.0u1 will throw
// an IllegalArgumentException instead (see GF issue 1527).
newValue = value;
}
[/code]

Thanks for pointing me to this one.

edit: This seemed to be a bug in jsf ri that was fixed in 05: https://javaserverfaces.dev.java.net/issues/show_bug.cgi?id=600

Still curious about the classloader problem though...

mriem
Offline
Joined: 2004-03-26
Points: 0

JSF is in one of the application servers classloaders and that one does not see the web application classloader hence the ClassNotFoundException. If you'd like I can dig up the documentation for it.

cmdrk
Offline
Joined: 2009-04-29
Points: 0

Yes, please. I'd like to see that documentation.
After updating to 1.2_12 I have da different problem, but I'll open a new thread for that one ;)

mriem
Offline
Joined: 2004-03-26
Points: 0

Can you submit another message for asking about Glassfish classloaders?