EJB: Passing instance of concrete class to an EJB method - java

Lets say there is an EJB method that accepts an object implementing some interface.
MyStatelessBean.method(InterfaceA aObj);
So the server implements MyStatelessBean and the InterfaceA is available in its classpath.
Now the concrete classes of InterfaceA are implemented at the Client side. These concrete classes are not in the classpath of the server.
Now when the client makes a call to the server and passes an instance of the concrete class to EJB method,
InterfaceA concreteObjA = new InterfaceA() { ... }
bean = context.lookup(ejbLookupStr);
bean.method(concreteObjA);
what is the expected behavior?
My testing in WildFly 9.0.2 resulted in ClassNotFoundException being reported in the Client side logs (but it seems like it is reporting a server side issue).
Exception in thread "main" javax.ejb.EJBException:
java.lang.ClassNotFoundException:
org.ktest.study.javaee.ejb.CallBackClient from [Module
"deployment.wildfly-ear.ear.wildfly-ejb.jar:main" from Service Module
Loader] at
org.jboss.ejb.client.EJBInvocationHandler.doInvoke(EJBInvocationHandler.java:238)
at
org.jboss.ejb.client.EJBInvocationHandler.doInvoke(EJBInvocationHandler.java:183)
at
org.jboss.ejb.client.EJBInvocationHandler.invoke(EJBInvocationHandler.java:146)
at com.sun.proxy.$Proxy0.register(Unknown Source) at
org.ktest.study.javaee.ejb.ClientObject.main(ClientObject.java:21)

Answering my own question:
Actually my requirement is server to client callback using RMI. The server provides CallBack Interface. But it's upto the Client to provide the concrete implementation. Since EJB uses serialization to pass objects from clients to server, it requires that the concrete class be available at server side for deserialization.
I had a bug in my RMI code which was causing the error.

Related

ServiceConfigurationError caused by LinkageError in WebSphere

I have an application that is deployed on WebSphere 9.0.5.2 where I want to use CXF for webservice calls and I'm getting this This ServiceConfigurationError
WrapperedException { java.util.ServiceConfigurationError: javax.xml.ws.spi.Provider: Provider org.apache.cxf.jaxws.spi.ProviderImpl could not be instantiated
Further down the stacktrace, I see that this is caused by a LinkageError
Caused by: java.lang.LinkageError: loading constraint violation when overriding method
"javax/xml/ws/spi/ServiceDelegate.createDispatch(Ljavax/xml/namespace/QName;Ljavax/xml/bind/JAXBContext;Ljavax/xml/ws/Service$Mode;)Ljavax/xml/ws/Dispatch;" during creation of class
"org/apache/cxf/jaxws/ServiceImpl": loader
"com/ibm/ws/classloader/CompoundClassLoader#832133d2" of class
"org/apache/cxf/jaxws/ServiceImpl" and loader
"com/ibm/oti/vm/BootstrapClassLoader#2eec706a" of class
"javax/xml/ws/spi/ServiceDelegate" have different types for the method signature
I understand that this could be caused by multiple libraries that have different definitions for QName or JAXBContext, but I think I've ruled those out.
I also understand that WebSphere has it's own JAX-WS with it's own method signature for ServiceDelegate.createDispatch and I've tried setting DisableIBMJAXWSEngine to true in WebSphere as instructed here
Using a third-party JAX-WS web services engine
I still haven't been able to get rid of the error and am pretty much at a loss as to what to try next. Any suggestions is appreciated!!!
The issue here is a cross-linkage between the application and server's copies of the JAXB API, JAX-WS API, and JAX-WS implementation. The application (configured with parent-last class loading delegation, so it searches locally before delegating to parents) contains a JAX-WS implementation and JAXB API/impl, but it does not contain the JAX-WS API. Since JAX-WS references JAXB, we get the following linkage pattern:
1) Some application class references the JAX-WS API. Because it's not packaged in the application, it's loaded from the server libraries.
2) When the API class is linked, it uses its local class loader to find the JAXB API. That, in turn, links it to the server's version as well.
3) JAX-WS uses the thread context class loader to load its implementation. It finds it in the parent-last application.
4) When the implementation class is linked, it uses its local class loader to find the JAXB API, which finds it locally in the application.
5) The implementation now is linked to two versions of the same class:
JAXWSImpl -> JAXWSAPI -> JAXB (server)
JAXWSImpl -> JAXB (app)
The JVM can't tolerate that, and a LinkageError is thrown.
The solution can be approached one of two ways:
1) Add the JAX-WS API to the application - this prevents the initial delegation in #1 above
2) Remove the JAXB API (and implementation, if provided) from the application - this causes the linkage in #4 to delegate down to the server for JAXB, so it's always loaded from the same place.
Either way, the trick is to ensure that all the necessary classes are available to the application through the same class loader.

Correct way to lookup local EJB in websphere - Getting ClassCastException

I have an EJB which is exposed by both local and remote interfaces
package com.sam.enqueue;
import javax.ejb.Local;
import javax.ejb.Remote;
import javax.ejb.Singleton;
#Singleton
#Local(SamEnqueueLocal.class)
#Remote(SamEnqueueRemote.class)
public class SamEnqueue implements SamEnqueueRemote, SamEnqueueLocal {
}
// remote interface
package com.sam.enqueue;
import javax.ejb.Remote;
#Remote
public interface SamEnqueueRemote {
}
// local interface
package com.sam.enqueue;
#Local
public interface SamEnqueueLocal {
}
My app container is websphere 8.0 and I am not overriding the default JNDI names which the server assigns.
During server startup I get the following default bindings in logs:
CNTR0167I: The server is binding the com.sam.enqueue.SamEnqueueRemote interface of the SamEnqueue enterprise bean in the SAM_ENQUEUE.jar module of the SAM_ENQUEUE application. The binding location is: ejb/SAM_ENQUEUE/SAM_ENQUEUE.jar/SamEnqueue#com.sam.enqueue.SamEnqueueRemote
CNTR0167I: The server is binding the com.sam.enqueue.SamEnqueueRemote interface of the SamEnqueue enterprise bean in the SAM_ENQUEUE.jar module of the SAM_ENQUEUE application. The binding location is: com.sam.enqueue.SamEnqueueRemote
CNTR0167I: The server is binding the com.sam.enqueue.SamEnqueueRemote interface of the SamEnqueue enterprise bean in the SAM_ENQUEUE.jar module of the SAM_ENQUEUE application. The binding location is: java:global/SAM_ENQUEUE/SamEnqueue!com.sam.enqueue.SamEnqueueRemote
CNTR0167I: The server is binding the com.sam.enqueue.SamEnqueueLocal interface of the SamEnqueue enterprise bean in the SAM_ENQUEUE.jar module of the SAM_ENQUEUE application. The binding location is: ejblocal:SAM_ENQUEUE/SAM_ENQUEUE.jar/SamEnqueue#com.sam.enqueue.SamEnqueueLocal
CNTR0167I: The server is binding the com.sam.enqueue.SamEnqueueLocal interface of the SamEnqueue enterprise bean in the SAM_ENQUEUE.jar module of the SAM_ENQUEUE application. The binding location is: ejblocal:com.sam.enqueue.SamEnqueueLocal
CNTR0167I: The server is binding the com.sam.enqueue.SamEnqueueLocal interface of the SamEnqueue enterprise bean in the SAM_ENQUEUE.jar module of the SAM_ENQUEUE application. The binding location is: java:global/SAM_ENQUEUE/SamEnqueue!com.sam.enqueue.SamEnqueueLocal
The lookup class is just a simple java class in a different EAR in the same server with the following code:
Context ctx = new InitialContext();
Object local = ctx.lookup("java:global/SAM_ENQUEUE/SamEnqueue!com.sam.enqueue.SamEnqueueLocal");
SamEnqueueLocal samEnqueue = (SamEnqueueLocal) local;
The lookup is working with any of the three JNDI names for the local but it's not getting cast to SamEnqueueLocal. The exception trace is:
SystemErr R java.lang.ClassCastException: com.sam.enqueue.EJSLocal0SGSamEnqueue_cf56ba6f incompatible with com.sam.enqueue.SamEnqueueLocal
... rest ommited
I've made a shared library and put the stub of destination EAR in it. The library is the classpath of the source lookup EAR with the Classes loaded with local class loader first (parent last) policy. The library is not isolated. If I remove the stub, I get a java.lang.ClassNotFoundException: com.sam.enqueue.SamEnqueueLocal as expected.
Update:
While using Dependency Injection:
#EJB(lookup="ejblocal:com.sam.enqueue.SamEnqueueLocal")
private SamEnqueueLocal samEnqueueLocal;
The error I get is:
javax.ejb.EJBException: Injection failure; nested exception is: java.lang.IllegalArgumentException: Can not set com.sam.enqueue.SamEnqueueLocal field com.some.SomeBean.samEnqueueLocal to com.sam.enqueue.EJSLocal0SGSamEnqueue_cf56ba6f
Caused by: java.lang.IllegalArgumentException: Can not set com.sam.enqueue.SamEnqueueLocal field com.some.SomeBean.samEnqueueLocal to com.sam.enqueue.EJSLocal0SGSamEnqueue_cf56ba6f
So it's basically the same.
You are getting java.lang.ClassCastException because you are retrieving a reference to an EJB that exists in a different class loader than the deployment unit (ejb-jar, war, etc) that is trying to inject it.
Using local EJB references between applications is vendor dependent if possible at all. You may be able to deploy your SamEnqueue bean in a separate EJB module and try to reference it via a manifest Class-Path: entry from each application. Be sure that there are no copies of SamEnqueueLocal in either EAR file.
Alternatively, just use the SamEnqueueRemote interface.
Refer to chapter 8 of the Java EE specification for more information.
See the "Local client views" section of the EJB modules topic in the knowledge center:
The EJB specification only requires local client views to be supported
for EJBs packaged within the same application. This includes local
homes, local business interfaces, and the no-interface view.
WebSphere® Application Server permits access to local client views to
EJBs packaged within a separate application with some restrictions
The local interface and all parameter, return, and exception types used by the local interface must be visible to the class loader of both the calling application and the target EJB application. You can ensure this by either using a shared library associated with a server class loader or by using an isolated shared library associated with both applications. Read the Creating shared libraries topic for more information.
...
From the link provided by in bkail's answer, These are the steps that I followed to make it work.
Take out the Local and Remote interfaces i.e. SamEnqueueRemote and SamEnqueueLocal from my source EJB jar and package then into a separate jar file. Although just taking out the Local interface will also work.
Make a shared library and put this jar into it. The shared library must be isolated so that same version of class is loaded by caller and callee.
In the caller EAR, get a reference to the local interface with either a lookup or injection.
Deploy both the caller and callee to the server and make sure to add the shared library in the classpath of both the EARs.
One of the approaches mentioned in this link is similar.
One way to prevent this is to use a remote interface. As Paul mentioned, there is optimization that happens when the caller and callee are in the same JVM, so it's not as expensive as if they're in separate JVMs. The ORB has classloader machinery that ensures the classes for the caller and callee are loaded using classloaders compatible with each side of the call.
Option 2, including the ejb jar within the new ear, won't solve the problem. Even though the classes will be available in both classloaders, the object passed by-reference from callee back to caller will still be instanciated using the other application's classloader and will not be type-assignable. Same with Option 3.
The 2nd way to make it work is to place the classes used by caller and callee in a "WAS shared library" and configure both applications to use that shared library. The subject of shared libraries and how to configure them is described in the WAS InfoCenter documentation...search on "shared library."
The 3rd way to make it work, which is the least desirable of the three, is to change the WAS server classloader policy to "one classloader per server." The default, as I mentioned at the top, is "one classloader per application (EAR file)." Changing to one classloader per server ensures that everything gets loaded by the same classloader and is thus type-compatible, but deprives your applications of the isolation/security benefits that come from having each app in its own classloader.

How does client know the EJB bean implementation is remote or local in ejb3

I'm a newbie to EJB3. I want to know that how client know the EJB bean implementation is in remote or local. When i access the bean using InitialContext in client class i want to know wether that bean is local or remote? Sorry if I'm asking stupid question?
The type of interface is determined via annotations.
These can be put next to Interface class declaration:
#Local - declares a local business interface
#Remote - declares a remote business interface
Then when an EJB extends such interfaces, it uses the interface as a local/remote view. If it extends multiple interfaces, it has multiple views.
These can be put next to EJB class declaration:
#Local(com.example.LocalInterfaceClass) - declares a local business interface
#Remote(com.example.SomeRemoteInterfaceClass) - declares a remote business interface
#LocalBean - declares a no-interface view (the full bean definition used as an interface)
If multiple of above annotations are used in combination, the EJB has multiple interface views. If all are ommitted, the bean defaults to a no-interface view.
You can use JDK inbuilt annotation processing to process annotations during compile time (via javax.annotation.processing classes and javac commandline options). E.g. you could generate code or set options/switches.
You can use reflection to determine annotations at runtime.
Probably cleanest and simplest of all not to have dynamic lookup & behaviour, but just to commit to either Local or Remote for each client and hard-code the appropriate behaviour.
There are two different interfaces available when you are writing an EJB. One is remote and one is Local. Remote, as it name suggests is for remote client that want to remotely call (or fire) functions and get some results. On the other hand Local is designed to be used in a local environment, for example in a case if another EJB or even a POJO in your system is using that. The usage would be the same as when you want to use an EJB using its Remote interface. However, it has less headache for the server to handle that. That's the only reason you might want to use a Local interface instead of Remote interface. Local interface is not local to JVM but local like an other POJO class.
Local client view cannot be accessed:
When an EJB or web component is packaged in a different application's EAR packages.
When a web component is deployed in a web container, and EJBs are deployed in an EJB container, and these containers are separated (even if they are running on the same machine)
These are main factors in considering a Local or Remote interface:
Client: if your client is not a web component or another bean do not use Local
Beans & Beans: Are the beans loosely coupled? then it is a good idea to use Local
Scalability: Remote is always better for scalability
Local interfaces are recommended for entity beans, which helps with performance issue.
More to read:
http://onjava.com/pub/a/onjava/2004/11/03/localremote.html
http://www.conceptgo.com/gsejb/ov06.html
Using #Remote of your interface you can use as Remote interface
#Remote
public interface Cart {
}
Now, Implement this interface to EJB bean.
#Stateful
public class CartBean implements Cart {
}
Similarly using #Local annotation you can use as Local interface.

How can I make a java.lang.reflect.Proxy from two separate classloaders?

I use the CXF JAXRSClientFactory to create proxy instances out of various JAX-RS interfaces. Deep inside, this factory invokes Proxy.newProxyInstance(), passing in my interface and the CXF Client interface.
Under OSGi, this works great if the bundle that contains my interface class imports org.apache.cxf.jaxrs.client. But one day I decided I wanted to hide CXF from my application classes, so I created a bundle that encapsulates the client factory in a service. Now I get IllegalArgumentException from Proxy saying "MyInterface is not visible from class loader" or "Client is not visible from class loader".
The issue is that my factory bundle imports CXF but not my application. And my application bundle does not import CXF. So there's no classloader anywhere in the container that can see both the CXF Client class and my JAX-RS interface.
Is there a way to solve this without importing CXF into my application bundles? For example, could my factory bundle dynamically make a new classloader that's the union of the two classloaders, so it can see both CXF and my application classes? Or can I fool Proxy into accepting the two interfaces anyway?
This is now a solvable problem as of CXF 2.6.1. The closed issue CXF-4290 adds a new API to pass a custom classloader to JAXRSClientFactory. With that and the new org.apache.cxf.jaxrs.client.ProxyClassLoader class, I can now make proxies for arbitrary application classes.
ProxyClassLoader classLoader = new ProxyClassLoader();
classLoader.addLoader(resourceApiClass.getClassLoader());
classLoader.addLoader(JAXRSClientFactoryBean.class.getClassLoader());
JAXRSClientFactoryBean bean = new JAXRSClientFactoryBean();
bean.setAddress(baseUrl);
bean.setServiceClass(resourceApiClass);
bean.setClassLoader(classLoader);
return bean.create(resourceApiClass);

EJB 3.0. Provide interfaces to clients

Suppose I am writing enterprise session bean(v 3.0) for using it as remote bean.
I have written remote bussines interface, implemented interface's methods in a bean class(bean also uses another java objects to transfer data to client, that classes specified as serializable to be marshalled over the network) and now I want to provide that bean to clients. To use bean functionality client needs bussiness interface of that bean and also classes that are used to transfer data from bean to client.
To solve this problem I made a jar from that bean and provide it to clients. Clients then have to add this jar library to client's project and after that find the bean implementation using lookup method from Context class.
But when I make a jar it puts all bean classes to jar archive, so client can decompile jar's bytecode and to see the real implementation of my interface. It's not very good practice.
So how can I provide all the necessary interfaces to clients without providing the real implementation?
Put the client interface into a different package or project, and when you build the client jar, only include the class files for that client interface.

Categories

Resources