After forced switching from Spring to EJB (EJB3) in my workspace, I find it problematic to rewrite the utility functions, that made some manual tasks like creating a few records or importing some dictionaries into database.
In Spring I could easily initialize the application by calling new ClassPathAXmlApplicationContext('spring.xml'). Is there any similar utility class for EJB? I'm using EJB implementation from Websphere 8, to be more specific.
Asking google for "Initializing EJB context" gave me nothing useful, maybe it was not correct search phrase?
In case you're starting (embeded) EJB container or doing something of this kind, singleton session bean can be annotated with #Startup annotation and it's #PostConstruct life cycle method will be invoked after container startup.
#Startup
#Singleton
public class StartupBean {
#PostConstruct
void init {
...
}
...
}
EDIT:
There's WebSpehere documentation on how to run embeded container and invoke EJB's. But note that embeded container has to support only EJB Lite specification:
The embeddable container does not support the use of Contexts and
Dependency Injection (CDI).
Related
I was wondering if EJB specifications allow to access a stateful session bean over a looked up stateful session bean.
The reason why I'm asking is, that Jboss EAP 7.0 has no problems with it, but Websphere throws a NullPointerException when I try to access the bean.
For example:
#Stateful
public class SampleServiceRoot implements SampleServiceRootRemote {
#EJB
protected SampleServiceChildLocal servicechild;
#Override
public SampleServiceChildLocal getServiceChild(){
return servicechild;
}
}
#Stateful
public class SampleServiceChild implements SampleServiceChildLocal,SampleServiceChildRemote{
#Override
public void anyMethod(){
//DO Anything
}
}
When I do a remote lookup to the SampleServiceRootRemote and call "getServiceChild()" and try to call "anyMethod()" on it, it works on JBoss EAP 7.0 but on Websphere I get a NullPointerException.
So I was wondering if this is a Bug in Websphere or is it forbidden by EJB Specification and I was just lucky with JBoss EAP 7.0?
The EJB specification does require this scenario to work, depending on some configuration options; there are configuration options that may disable it.
The fact that you are seeing a NullPointerException indicates that WebSphere is not aware of the #EJB annotation on the field in the SampleServiceRoot class. Per the EJB specification, an instance of SampleServiceRoot cannot be created if the #EJB annotation cannot be resolved. Since an instance of SampleServiceRoot has been created, then one of the following has likely occurred:
1 - The application performed a new SampleServiceRoot rather than looking it up in JNDI. This doesn't sound like your problem, but good to double check.
2 - The application contains an ejb-jar.xml with the setting metadata-complete="true". When this is set, WebSphere will not look for annotations, and so will not see or process the #EJB annotation. Either change the setting to "false" or add the <ejb-ref> or <ejb-local-ref> to the ejb-jar.xml file.
3 - The application does not have metadata-complete="true", however when the application is deployed to WebSphere the option to set metadata-complete was selected.
This option will change the metadata-complete setting to "true". Stop using this option, or add the <ejb-ref> or <ejb-local-ref> to the ejb-jar.xml file.
4 - The EJB is contained in a WAR module at level 2.4 or older. In WebSphere, annotations for older modules are not processed.
5 - The application includes a copy of the javax.ejb.EJB class. WebSphere provides the javax.ejb.EJB class, and it is loaded by the WebSphere runtime classloader. If the application also contains the javax.ejb.EJB class on the application classpath, then another instance will be loaded by the application classloader, and it will not match the instance used by the EJB Container. There should be a warning in the logs if this has occurred.
So yes, your scenario is required to be supported; however the specification does allow configurations that disable it. You just need to identify which configuration / packaging option has caused WebSphere to not see the #EJB annotation.
Thanks for the answer,
we tried to move the declarations of the Local-Interface to the Remote Interface of the SampleServiceChild. Also we did not use #EJB annotation. We managed it with doing a lookup of the SampleServiceChild over the InitialContext.
Now it works
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...
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).
I'm trying to write a servlet 3.0 web app, just to learn basic servlet handling. Normally i would use spring.
Now I have a servlet which access a DAO which queries the database. Now, what is the best way to instantiate that DAO and use it? My best guess would be to have a private property in the servlet and create an instance of the DAO when the servlet is created.
But, will the servlet be created multiple times?
Is there something similar to springs dependency injection available in servlet 3.0?
EJB 3 dependency injection is extremely simple to use. A single annotation, #EJB, causes the injection of a declared bean. The injection of the SomeDAO bean into a Servlet 3.0 looks like this:
#WebServlet(name="Messenger", urlPatterns={"/Messenger"})
public class Messenger extends HttpServlet {
#EJB
SomeDAO someDAO;
}
The injected SomeDAO bean could be an interface or a no-interface view bean. As long as there is only one implementation of the interface, it will be injected without any ceremony.
javax.servlet API is one of the technologies included in java-ee.
CDI, is the Context and Dependency Injection technology in java-ee
So to answer your question, your use case could be solved by using only CDI and Servlets.
But most of the application servers that supports above (e.g. TomEE, Glassfish webprofiles), will also support EJB (which uses cdi) and JPA. EJB+JPA can be used to implement DAOs.
Arjan Tijms has put together a nice link overview of what is included and happening in the java-ee-7 world
The following is the situation :
I have a business layer, that is an EJB project. In fact, there is only one EJB that is created. This EJB is responsible to expose the service classes to other layers, that calls the EJB. I want to introduce spring (to use DI feature) in this layer.
My concern is, what is the best way to load the spring context in this business layer, so that the spring context does not get loaded again and again, whenever the EJB gets called ?
(In a Web project, there is an advantage rather to configure the spring context in contextLoaderListener, and it gets loaded once only when the application gets started)
I have thought of including spring in the same layer because :
Configure the dependencies of all DAO and service classes and inject them wherever necessary.
To use spring support for hibernate in the business layer.
Ease of Unit testing, by injecting the properties into classes and simulating the same. Don't need to run the other layers again and again, to test my business classes/methods.
To be able to use AOP (Aspect Oriented Programming) for Logging and method level auditing.
Kindly help me suggesting the best way, to load the spring context in an EJB project. I also want to know , if there are any alternatives if I can load the same in the app server (I am using Web sphere app server).
Thanks and Regards,
Jitendriya Dash
Spring should be configured as part of your application in the normal way that you always set it up. Then you need to access the Spring beans from the EJB layer. To access (adapted from this post), create a Spring bean as follows:
#Component
public class SpringApplicationContext implements ApplicationContextAware {
private static ApplicationContext CONTEXT;
public void setApplicationContext(ApplicationContext context) throws BeansException {
CONTEXT = context;
}
public static Object getBean(String beanName) {
return CONTEXT.getBean(beanName);
}
}
Then, to call the bean in question from the legacy app:
SomeService someService = (SomeService)SpringApplicationContext.getBean("someServiceImpl");
The Spring context is initialized once, and your EJB layer can access at will.
For EJB3, Spring recommends using an EJB3 Injection Interceptor. Basically you specify your Spring context using a ContextSingletonBeanFactoryLocator which entails creating your Spring context in a beanContextRef.xml in your classpath. Probably as part of your EAR. The SpringBeanAutowiringInterceptor injects your bean into your EJB.
Marking the EJB been as a Singleton (#Singleton). And storing the spring context in a variable in this bean, after it is created once, so that you can return the same context again and again.