Not terribly experienced using EJB, and I ran into the following problem with which I hope one of you guys can help out.
Suppose the following situation: a set of #Local beans have been defined to provide access to a database. These beans are very simple, and are deployed on application server A (Weblogic 10.3.3). We want to provide access to these local beans via remote, and since we already have a "services" module set-up for providing external access to our services, our idea was to create a new #Remote service that uses the local beans described above (injected via #EJB). These service beans are also deployed on application server A. For example:
#Local
public interface DatabaseBeanLocal { doStuff(); }
#Stateless(name = "ejb/DatabaseBean", mappedName = "DatabaseBean")
public class DatabaseBean implements DatabaseBeanLocal { doStuff() { ... } ; }
#Remote
public interface ServiceBean { doSomeOtherStuff(); }
#Stateless
public class ServiceBeanImpl implements ServiceBean
{
#EJB(name = "ejb/DatabaseBean", mappedName = "DatabaseBean")
private DatabaseBeanLocal myDatabaseBean;
... methods etc. ...
}
The client that will actually use these remote beans is actually run on a different application server; application server B (also Weblogic 10.3.3). When we look-up the ServiceBean bean from our client, that works fine. When we call a method on it that needs access to DatabaseBean however, the call fails. The Weblogic server says it cannot find a bean for the DatabaseBean interface.
My questions: is this set-up even possible? In other words: will Weblogic (or another container) inject the local bean into the remote bean so that the client gets an instance of the remote bean that is capable of calling actions on the local bean (I assume not, but I'm asking anyway to be sure)?
If not, the I guess we'll have no other choice than to skip the service layer and provide direct access to the DatabaseBean example above via #Remote.
Update 1
After doing some tests, simply defining DatabaseBean above as #Remote rather than #Local "fixes" this issue. Ofcourse, this is not really a fix since this will call the DatabaseBean remotely, which is ridiculous because it's in the same module as the service. I'm starting to suspect that wrapping a local EJB with a remote EJB is simply not possible.
Update 2
What we've found so far:
We've not been able to manually inject the local EJB so far, because we cannot actually find it at runtime.
Weblogic apparently does not include local EJBs in the JNDI tree.
Calling the ServiceBean from outside the AS on which it is deployed still does not work, because the dependency on the local EJB is never resolved, or resolved client-side which means it's not found.
Local means local to EAR not to AS.
Your local and remote Beans must be in same EAR (not only in same AS). Are they?
-- edit --
Hmm.. If they're in the same EAR then it should work. I.e. answer to your question "is such a setup even possible?" is Yes.
Unfortunately now we're talking abt pure and simple debugging. First thing I would do is try and check if teh local bean (DatabaseBean I guess) is actually registered and working using WebPhere UTC equivalent in WebLogic (I never worked in WebLogic). I can list a 100 other things you can check for more logs/traces/symptoms but well, that's the way debugging goes.
Managed to get this issue resolved. the following configuration works for Weblogic 10.3.3 and allows for a remote EJB to use a local EJB, where the remote EJB can be called from anywhere.
Ultimately — after a lot of testing — the trick was apparently specifying a beanName value for the #EJB annotation used to mark the local bean as a dependency of the remote bean. Doh!
Deployment
#Local EJB is deployed on AS-A in EAR-1 (in its own module/JAR)
#Remote EJB is deployed on AS-A in EAR-1 (in its own module/JAR)
Client code that calls the remote service is deployed on AS-B in its own EAR archive
Annotations
The local EJB is a very simple and straightforward EJB bean, with the following interface and implementation:
#Local
public interface LocalBeanLocal {
// Implementation omitted
}
#Stateless(name = "LocalBean")
public class LocalBean implements LocalBeanLocal {
// Implementation omitted
}
The remote EJB is again a relatively simple EJB bean, except that it has a dependency on LocalBean. This dependency is expressed through the #EJB annotation; but it would seem that the beanName attribute is required for Weblogic to resolve this dependency correctly. Without the beanName attribute, calling the remote EJB would not work for us (in the sense that there would always be some kind of error when the local bean was involved)!
#Remote
public interface RemoteBeanRemote {
// Implementation omitted
}
#Stateless(name = "RemoteBean")
public class RemoteBean implements RemoteBeanRemote {
#EJB(beanName = "LocalBean")
private LocalBeanLocal localBean;
// Implementation omitted
}
What is apparently important here is that the beanName attribute in the remote service dependency declaration (#EJB(beanName = "LocalBean")) should have the same value as the bean name defined in the local bean implementation (#Stateless(name = "LocalBean")).
Lookup
Getting a reference to your remote EJB is done the traditional way, there doesn't seem to be a specific requirement. In our case, we look-up the remote service via JNDI.
Related
I have an EAR application with three modules:
beans are in "app-ejb" module
remote interfaces are in "app-remote"
web services are in "app-war"
app-ejb and app-war use app-remote as library.
all are packaged in "app.ear".
This is working fine, but now I have to use the same beans outside the EAR application, and injection is not working.
I have in app-ejb:
#Stateless
#LocalBean
public class Services implements ServicesRemote {
[...]
}
and his remote interface in app-remote:
#Remote
public interface ServicesRemote {
[...]
}
In my app-war I can inject the remote bean without problem:
#Stateless
#LocalBean
public class UseServices {
#EJB
private ServicesRemote services;
[...]
}
Anyway in my external ejb application, deployed as stand-alone and using the same ejb-remote as library, if I try to inject the same EJB like this:
#Stateless
#LocalBean
public class UseServicesFromAnotherApp {
#EJB
private ServicesRemote services;
[...]
}
Glassfish (4.1) give me an error "Class [ Lcom/[...]/ServicesRemote; ] not found".
Is this expected? How can I inject the remote bean correctly?
Injection doesn't work with remote interfaces. Beans that are "injectable", live inside container's JVM and are available for injection to other beans inside the same application. The same holds true for accessing beans in another application in the same container, although applications may live in the same JVM. Since remote methods are originated from another JVM or another application, injection is not possible. You must use JNDI lookup instead to get a reference to a remote bean.
As a matter or personal taste, I would stay away from EJB Remote interfaces, and instead I would use another "remoting" technique such as REST.
The problem was probably generated by a number of hot deploys, made glassfish unstable.
When I restarted glassfish my code start to work properly (it's actually still working).
Sorry for posting here without trying to restart glassfish first.
I tried inject a stateless EJB in my application client and I get the following error
SEVERE: Exception while preparing the app : Unable to load the EJB module. DeploymentContext does not contain any EJB. Check the archive to ensure correct packaging for C:\glassfish3\glassfish\domains\domain1\applications\EJB.
If you use EJB component annotations to define the EJB, and an ejb or web deployment descriptor is also used, please make sure that the deployment descriptor references a Java EE 5 or higher version schema, and that the metadata-complete attribute is not set to true, so the component annotations can be processed as expected
My client is defined thus:
#EJB
private static Sless sless;
public static void main(String[] args) {
System.out.println("Sless says: " + sless.hello());
}
The class Sless is my interface which is defined thus:
#Remote
public interface Sless {
public String hello();
}
I also have a class implementing Sless
#Stateless
public class SlessBean implements Sless{
#Override
public String hello() {
return "hello, world!\n";
}
}
Any help as to where the problem is coming from would be appreciated.
1) Standalone clients or non-managed POJOs(POJOs which are not maintained by Containers) do not support annotation injection. So #EJB will not work.
2) You will have to do a manual JNDI look up from your standalone client.
What you need for this ?
1) Your EJB extending Remote interface. Because Local interface does not expose EJB to Cross Application or Remote calls.
2) Dependency jar files in your standalone client. ie: ORB Jar
3) You will need to know RMI/IIOP port on your server.
4) You will also have to take care if Standalone client and Server use different JDK/JRE implementations. For ex: 1 using SUN JDK and other using IBM JDK.
Follow this for connecting to EJB on Glassfish from a Standalone client >> https://glassfish.java.net/javaee5/ejb/EJB_FAQ.html#StandaloneRemoteEJB
I'm currently migrating a JBoss service class from AS5.1 to AS6 (not going to AS7 for a variety of reasons).
For AS5.1, the service implements a {serviceName}MBean and has a jboss-service.xml with attribute values. It's packaged in a jboss-sar, which is packaged in an EAR to be deployed. When deployed, the service fields are populated with the values from jboss-service.xml, and the service is automatically registered into JMX.
I would like to achieve the same thing using AS6, but would like the service to support CDI - so I'd like its new #Inject injection points to be satisfied. I need these to be satisfied in the object registered with JMX, so that methods called via JMX can reference injected fields, but I'm struggling to achieve this.
I've had to package the service in a jar, instead of a jboss-sar, for classloader reasons, but let's say it's otherwise unchanged. When deployed to AS6, all works as before - service goes into JMX, values from XML propagate to the object. However, the instance created does not have its CDI injection points satisfied, and neither does the object registered in JMX.
If I annotate the service class with #Startup and #javax.ejb.Singleton, but keep its interface and the jboss-service.xml, the object registered into JMX still does not have its CDI injection points satisfied. However if I programmattically deregister that bean, and re-register the instance in a #PostConstruct method, then the bean in JMX DOES have its injection points satisfied. However that bean no longer has the values specified in the jboss-service.xml.
So how can I get the best of both worlds? CDI and the usual JBoss service behaviour? What is the correct way to implement a JBoss service with CDI? I've been unable to find documentation on this. Hope someone can help.
Thanks,
Ben
As a worst-case fallback, you should be able to use the CDI extension API to get your service to be injected. I don't think you would need to write a fully-fledged extension, but if you have an initialisation hook in the service object, you can do this (lifted with minor editing from the docs, not compiled or tested):
public static <T> void inject(T object) {
BeanManager beanManager = (BeanManager)new InitialContext().lookup("java:comp/BeanManager");
AnnotatedType<T> type = beanManager.createAnnotatedType(object.getClass());
InjectionTarget<T> it = beanManager.createInjectionTarget(type);
CreationalContext ctx = beanManager.createCreationalContext(null);
it.inject(object, ctx);
it.postConstruct(object);
}
Basically, any object to that method will get injected. All the usual CDI annotations should work. Hopefully.
As mentioned in the comments above, it seems Tom's right - there's no 'nice' way of created a CDIed, JMX bean in one go, you either have to put your JMX bean in CDI, as is suggested above, or put you CDIed bean into JMX. We tried the former, but it appears the BeanManager isn't bound to JNDI at the point the service starts up.
So instead we went with CDI bean -> JMX. We're creating the services as Singleton EJBs, so their injection points are satisfied, and they then get registered/unregistered to JMX in their PostConstruct/PreDestroy methods, using German Escobar's excellent CDI portable extension (germanescobar.net/2010/01/cdi-portable-extension-jmx.html, community.jboss.org/thread/148750 is also helpful).
May try to use ApplicationScoped beans and get them to start by observing a ContainerInitialized(?) event, however, as we don't need all the features of an EJB. Haven't tried that yet, mind...
I have a war (using struts 1) nested in an ear with session ejbs. If I lookup normal session beans from the war with lookup("earname/beanname/local") everything works. If I lookup a #Service ejb the same way it fails from the war with a NamingException.
To make sure I was doing the lookup correctly I looked up one #Service bean from another's start() method (instead of injecting with annotations) and the jndi lookup succeeded.
What could be the cause of this behavior? I am currently stuck with JBoss 5.0.
After going through the jmx-console like Nicholas recommended I was able to track down my mistake.
It turns out my two lookups were in fact different. The war was using the service name that I wanted, everything else (which was working) was using the class name of the implementation of the #Local interface.
To further the confusion I used #Service(objectName="blah") instead of #Service(name="blah").
I am now able to successfully lookup my #Service ejbs from my war.
I have been working through this tutorial. Halfway though it creates an interface and facades for an EJB. Can anyone tell me when I reference the interface using the #EJB annotation, where does it actually make the link between the interface and the actual enterprise java bean itself.
Thanks for the help.
~ Kyle.
It is AFAIK not mandated by the J2EE specification how this is actually solved or implemented by the application server. The most common solution is that the app server uses its own mapping between bean class names and JNDI names, so that depending on the bean class name, it is bound to a specific JNDI path when deploying the application and the same class name -> JNDI path conversion is used for injecting the EJB reference on the "client side".
It will be done inside the EJB container.
When you add this annotation, you'll actually be telling the IoC container of your application server which implementation of the given EJB you want.