Why only JNDI is used to access remote EJB (different JVM, differente host)? why not use the # EJB annotation?
In all EJB books mention that you can access remote EJB using #EJB annotation.
Example: http://docs.oracle.com/javaee/7/tutorial/doc/ejb-intro004.htm
32.4.4 Remote Clients
A remote client of an enterprise bean has the following traits.
It can run on a different machine and a different JVM from the enterprise bean it
accesses. (It is not required to run on a different JVM.)
It can be a web component, an application client, or another enterprise bean
It can be a web component, an application client, or another enterprise bean.
To a remote client, the location of the enterprise bean is transparent.
The enterprise bean must implement a business interface. That is, remote clients may not
access an enterprise bean through a no-interface view.
To create an enterprise bean that allows remote access, you must either:
Decorate the business interface of the enterprise bean with the #Remote annotation:
#Remote public interface InterfaceName { ... }
Or decorate the bean class with #Remote, specifying the business interface or
interfaces:
#Remote(InterfaceName.class)
public class BeanName implements InterfaceName { ... }
Client access to an enterprise bean that implements a remote business interface is
accomplished through either dependency injection or JNDI lookup.
To obtain a reference to the remote business interface of an enterprise bean through
dependency injection, use the javax.ejb.EJB annotation and specify the enterprise bean's
remote business interface name:
#EJB Example example;
To obtain a reference to a remote business interface of an enterprise bean through JNDI
lookup, use the javax.naming.InitialContext interface's lookup method:
ExampleRemote example = (ExampleRemote)
InitialContext.lookup("java:global/myApp/ExampleRemote");
The text above is incorrect? I have not seen any code that uses Dependency Injection (#EJB) to access a remote EJB. It is not possible?
Several post say it is not possible to use the # EJB annotation for calls to remote ejb:
https://stackoverflow.com/a/7842345/3167508
https://stackoverflow.com/a/16518704/3167508
PD: Excuse me. My English is basic.
I believe the main reason is that injection of remote (different JVM) EJB instances through
#EJB(lookup = "jndi_name")
is only supported by some Application Servers and only with specific configuration.
i.e. JBoss 7+ supports it only when you define the remote-outbound-connection in the standalone file (and add a jboss-ejb-jar.xml in the META-INF folder of deployed packages containing the reference to that connection).
Furthermore:
#EJB can only work in CDI managed beans
Using this approach forces you to define connection properties once per connection, while with the programmatic lookup you may vary them on every request (and you are free to handle contexts and connections manually, if needed).
Related
After the migration of a JEE application (from JBoss 7.1.1 to WildFly 8.2.1) our method to get CDI Beans stopped working. The application have several modules (independent JAR files) grouped into one WAR file deployed now on WildFly.
The Bean to be injected is in the module service and it is implementing the interface IProcessor:
#Loggable
#Monitorable
#Singleton
#ConcurrencyManagement(CONTAINER)
#Lock(READ)
#LocalBean
#ApplicationScoped
public class Processor implements IProcessor {
[...]
In another module of the application (common) there is the rest of the logic: the interface IProcessor and the class where we search for it.
This is how the BeanManager is retrieved:
public void keepBeanManager(#Observes AfterBeanDiscovery abd, BeanManager beanManager) {
bm = beanManager;
}
And this is how the call is done:
Set<Bean<?>> jobBeans = bm.getBeans(IProcessor.class);
I've tried printing out all deployed beans using Adam Bien's sample, at the same point as the getBeans method is called, and I can see the Processor in them. Also, if providing the full package and class name of the Processor works as a temporal hack, but using the IProcessor interface never works, jobBeans is always empty.
The module service is not visible to the module common, and this is why the injection is done using the interface.
As it was working before in JBoss and not in WildFly I assume it is related with the way the AS is handling the Beans, could it be something missing in the configuration of WildFly after the migration?
As pointed by Xavier Dury in the comments, the Bean was not found because it was annotated as #LocalBean. Removing the #LocalBean annotation fixed the issue.
From the JEE definition of LocalBean:
Designates that a session bean exposes a no-interface view
As Processor is implementing the interface IProcessor, the annotation #LocalBean should not be used.
What is strange still for me is why this was working on JBoss...
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.
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.
I am trying to understand what the purpose and why we need the different client views in EJB. Could someone try to explain?
Remote client view
When your EJB and its clients will be in a distributed environment - meaning EJBs and clients will reside on separate Java virtual machines. Example : EJBs hosted on a WebSphere Application Server and Servlets that consume EJB APIs hosted on a Tomcat server.
Local client view
Only when it is guaranteed that other enterprise beans or clients will only address the bean within a single JVM. Example, EJBs as well as the Servlets deployed on the same WebSphere server.
No-Interface view
Is almost same as local client view, but there are differences. Your bean class is not required to implement client view interfaces in this case. All public methods of the bean class are automatically exposed to the caller. no-interface view always acquires an EJB reference - just like local or remote views - either through injection or JNDI lookup; but, Java type of the EJB reference is the bean class type rather than the type of a local interface. This is a convenience introduced as part of Java EE6.
Difference between local client view and no-interface view
In case of no-interface view, the client and the target bean must be packaged in the same application (EAR). In case of local view, client can be packaged in a separate application than the enterprise application. So, this gives more flexibility in terms of fine-graining your components.
You may use local client view vs no-interface view depending on your API usage scenario. It is very likely for no-interface view to receive flexible features in future specs.
Reason
Historically or otherwise, a client wishing to use EJB services was supposed to "look up" the bean on the container ( with certain initial contexts ). That was because all invocations are made through a special EJB reference(proxy) provided by the container. This allows the container to provide all the additional bean services such as pooling, container-managed transactions etc. So, a client can not explicitly instantiate an EJB with new operator. The client view is provided via certain interfaces that the client would have access to. The proxy realization at server side is done based on these interfaces. Different client views are defined to suite different deployment scenarios as mentioned above.
As per Section 3.2.2 of EJB 3.1 Specification:
Access to an enterprise bean through the local client view is only
required to be supported for local clients packaged within the same
application as the enterprise bean that provides the local client
view. Compliant implementations of this specification may optionally
support access to the local client view of an enterprise bean from a
local client packaged in a different application. The configuration
requirements for inter-application access to the local client view are
vendor-specific and are outside the scope of this specification.
Applications relying on inter-application access to the local client
view are non-portable.
No-interface view is just a convenience feature that allows a bean to
expose a local client view without having to declare a separate
interface.
I have a #Stateless #Local Bean successfully deployed in an ear. I can see the new EJB 3.1 standard global JNDI name when I browse the JNDI tree. (java:global/product/product-ejb/ProductManagement)
I want to use this EJB in a different application on the same app server. Do I need to add a remote interface for this EJB?
Inter-application access to the local client view is not required by the specification but might be optionally supported by your container. If you want your application to be portable, you shouldn't rely on it and use a Remote interface (a decent container should optimize calls inside a same JVM anyway). From the EJB 3.1 specification:
3.2.2 Local Clients
Session beans may have local clients.
A local client is a client that is
collocated in the same JVM with the
session bean that provides the local
client view and which may be tightly
coupled to the bean. A local client of
a session bean may be another
enterprise bean or a web component.
Access to an enterprise bean through
the local client view requires the
collocation in the same JVM of both
the local client and the enterprise
bean that provides the local client
view. The local client view therefore
does not provide the location
transparency provided by the remote
client view.
Access to an enterprise bean through
the local client view is only required
to be supported for local clients
packaged within the same application
as the enterprise bean that provides
the local client view. Compliant
implementations of this specification
may optionally support access to the
local client view of an enterprise
bean from a local client packaged in a
different application. The
configuration requirements for
inter-application access to the local
client view are vendor-specific and
are outside the scope of this
specification. Applications relying on
inter-application access to the local
client view are non-portable.
...
References
EJB 3.1 Specification
Section 3.2.2 "Local Clients"