I have a JAX-RS webservice with a resource for generating testdata. During tests I found out, that the injected EJB is not reinitialized and still contains data from the last request.
I have a jar file server.jar containing my business logic with EJBs. To show my problem I have created a stateless bean:
#Stateless
public class TestService
{
#EJB
SubsequentTestService state2Service;
private String value;
public void testIt()
{
System.out.println("####### VALUE: " + value);
value = "TestValue";
state2Service.testIt();
}
}
I am using the subsequent call to SubsequentTestService to show the odd behaviour also exists for call of another stateless EJB:
#Stateless
public class SubsequentTestService
{
private String value;
public void testIt()
{
System.out.println("####### VALUE2: " + value);
value = "TestValue2";
}
}
Changing the annotation form #EJB to #Inject does not change anything.
In my web.war I have simple JAX-RS beans. The one which is called to show the strange behaviour is defined as follows:
#Path("/test")
public class TestResource
{
#Inject
TestService testService;
#GET
#Path("/state")
public void testState()
{
testService.testIt();
}
}
The JAX-RS application configuration looks as follows:
#ApplicationPath("/api")
public class JaxRsConfiguration extends Application
{
}
The war file contains the beans.xml, but no other configuration file. Everything is packaged into an ear file and is deployed in wildfly 10.0.0.Final. If I call the webservice as GET request via http://localhost:8080/api/test/state I get the expected output:
INFO [stdout] (default task-7) ####### VALUE: null
INFO [stdout] (default task-7) ####### VALUE2: null
But on the second request I get following unexpected output:
INFO [stdout] (default task-8) ####### VALUE: TestValue
INFO [stdout] (default task-8) ####### VALUE2: TestValue2
What is my problem here? Might be anything misconfigured in the wildfly? But I have only changed the logging and the datasource definition.
You have the meaning of #Stateless backwards.
This does not mean like so:
Hey container, here's an arbitrary class, please make it a stateless bean.
This actually means like so:
Hey container, here's a stateless class, you can safely use it as a stateless bean.
You have a stateful class. You should mark it as a #Stateful bean. Otherwise, get rid of all the state (unmanaged instance variables) so you can safely use it as a #Stateless bean.
See also:
Why Stateless session beans?
JSF request scoped bean keeps recreating new Stateful session beans on every request?
When using #EJB, does each managed bean get its own #EJB instance?
Related
I have a configuration class, which I inject into another class.
Configuration class looks as follows:
#RequestScoped
public class Configuration {
#ConfigProperty(name = "VALUE")
public String value;
public String getValue() {
return value;
}
}
This configuration is injected into the following place:
#ApplicationScoped
public class EndpointImpl implements Endpoint {
private Configuration configuration;
#Inject
public EndpointImpl(Configuration configuration) {
this.configuration = configuration;
System.out.println("configuration.value=" + configuration.value);
System.out.println("configuration.getValue()=" + configuration.getValue());
System.out.println("configuration.value=" + configuration.value);
}
Logs printed after going through constructor:
configuration.value=null
configuration.getValue()=someValue
configuration.value=null
I added postConstructor with the below similar code:
#PostConstruct
public void postConstruct(){
System.out.println("configuration.value="+configuration.value);
System.out.println("configuration.getValue()="+configuration.getValue());
System.out.println("configuration.value="+configuration.value);
}
and the result is the same.
Everything works fine as long as the value is retrieved through the getter method. When I try to retrieve it through the public field it returns null. I thought that, when CDI completes the process of building the bean, the fields inside the class will be set to appropriate values.
Why does it behave in such a way?
This is expected, because your Configuration bean is #RequestScoped. The request scope is one of so-called normal scopes, and whenever you #Inject a normal-scoped bean, you will always get a so-called client proxy.
The client proxy exists to lookup the correct bean instance and forward method invocations to it. In this case, it will lookup the bean instance for current request.
So, when you're accessing fields, you're accessing them on the client proxy -- not on the instance that pertains to the current request.
Whenever you inject a normal-scoped bean, never access fields directly. Always call methods. You may access fields directly on #Singleton and #Dependent beans, because they don't come up with a client proxy.
when I tried to inject request scoped bean in application scope bean I got the following error.
Method threw 'org.jboss.weld.contexts.ContextNotActiveException' exception. Cannot evaluate com.example.flow.txn.TxnMessageProcessor$Proxy$_$$_WeldClientProxy.toString()
Code Reference:
#ApplicationScoped
public class TxnMessageObserver {
private static final Logger logger = LoggerFactory.getLogger(TxnMessageObserver.class);
#Inject
private TxnMessageProcessor processor;
//message is observed here
public void postConstruct(#Observes #Initialized(ApplicationScoped.class) Object o) {
logger.info("Subscribing to queue [{}] for msg.", queue);
consumer.subscribe(queue);
}
}
#RequestScoped
public class TxnMessageProcessor {
private static final Logger logger = LoggerFactory.getLogger(TxnMessageProcessor.class);
//all processing happens here
}
I need to process every message in request scope.
If the applicationscoped bean is constructed eagerly when the servlet context is initialized (like is the case here), there is no request context and hence no requestscoped bean.
Since it is completely unclear what you try to achieve (your code is not a minimal reproducible example, let me point you to
JEE6 #ApplicationScoped bean and concurrency
Why #Singleton over #ApplicationScoped in Producers?
(All found via this search)
Injecting a requestscoped bean is therefor dangerous and I strongly suggest to retrieve the required requestscoped bean in the specific methods or do it the other way around, inject the applicationscoped bean in the requestscoped one and call a method on it, passing in itself.
I do not know what exactly the problem cause is. But I can confirm that you can inject #ReqeustScoped beant into #ApplicationScoped. I do that in many applications with hundreds of classes and it worked out of the box.
I'm using JEE version 5.0.2. I'm calling a WebMethod in a #WebService annotated class. This annotated class calls a Stateless #EJB that contains the business logic (the implementation) of the WebMethod I'm calling.
My WebService class is defined as follows:
#WebService
HandlerChain(file = "MyXService_handle.xml")
public class MyXService {
#EJB(mappedName="MyXBean", beanName="MyXBean")
public MyXBean myXBean;
#WebMethod
public String sayHello(#WebParam(name="nTimes") int nTimes) {
return myXBean.sayHello(nTimes);
}
}
My EJB is defined as follows:
#Stateless(name="MyXBean", mappedName="MyXBean")
#TransactionManagement(TransactionManagementType.BEAN)
public class MyXBean {
#PersistenceContext name="persistence/my_PU", unitName="my_PU")
protected EntityManager entityManager;
public String sayHello(int nTimes) {
String s = "";
for (int i = 0; i < nTimes; i++) {
s += "Hello";
}
return s;
}
}
I'm getting the following error:
Caused by: com.sun.enterprise.InjectionException: Exception attempting to inject Unresolved Ejb-Ref com.my.domain.MyXService/myXBean#jndi: MyXBean#null#com.my.domain.MyXBean#Session#MyXBean into class com.sun.enterprise.webservice.JAXWSServlet
at com.sun.enterprise.util.InjectionManagerImpl._inject(InjectionManagerImpl.java:387)
at com.sun.enterprise.util.InjectionManagerImpl.inject(InjectionManagerImpl.java:206)
at com.sun.enterprise.util.InjectionManagerImpl.injectInstance(InjectionManagerImpl.java:117)
at com.sun.web.server.J2EEInstanceListener.handleBeforeEvent(J2EEInstanceListener.java:259)
... 31 more
Caused by: javax.naming.NameNotFoundException: MyXBean#com.my.domain.MyXBean not found
at com.sun.enterprise.naming.TransientContext.doLookup(TransientContext.java:216)
at com.sun.enterprise.naming.TransientContext.lookup(TransientContext.java:188)
at com.sun.enterprise.naming.SerialContextProviderImpl.lookup(SerialContextProviderImpl.java:74)
at com.sun.enterprise.naming.LocalSerialContextProviderImpl.lookup(LocalSerialContextProviderImpl.java:111)
at com.sun.enterprise.naming.SerialContext.lookup(SerialContext.java:409)
at javax.naming.InitialContext.lookup(InitialContext.java:392)
at com.sun.enterprise.naming.NamingManagerImpl.lookup(NamingManagerImpl.java:944)
at com.sun.enterprise.naming.java.javaURLContext.lookup(javaURLContext.java:173)
at com.sun.enterprise.naming.SerialContext.lookup(SerialContext.java:407)
at javax.naming.InitialContext.lookup(InitialContext.java:392)
at com.sun.enterprise.util.InjectionManagerImpl._inject(InjectionManagerImpl.java:287)
... 34 more
|#]
The WebService class and the EJB are exactly in the same module in the same WAR. This WAR is being deployed in a Glassfish Server version 2.1.1. I only get this warning in the deploy log (server.log):
[#|2018-12-10T22:51:59.045+0100|WARNING|sun-appserver2.1|javax.enterprise.system.tools.deployment|_ThreadID=78;_ThreadName=Thread-946;_RequestID=bf2b9b34-5240-4eca-8b4b-9d16baad7c5b;|Unresolved <ejb-link>: MyXBean|#]
I'm following the Java EE 5 specification to implement all of this, so I'm trying to do my architecture "by the book".
I already tried to implement an interface as explained in here https://docs.oracle.com/javaee/5/tutorial/doc/bnbmg.html but with no avail, the error persisted.
I have no xml configuration concerning the beans. I was expecting all to work via the annotations but I'm getting the javax.Naming.NameNotFoundException explained above.
Am I missing something? I'm available to give more details if needed.
Thank you.
You are using the ejb name and the mapped name. The usage of mapped name is vendor specific. Glassfish is using this as a "global JNDI name". Since you are using both, I don't know which one will take precedence over the other. With your example, empty #EJB and #Stateless anntoations should work.
I am new to the Struts2 framework and to EJB as well. I have a class LoginDAO which implements checkUser method of an interface LoginDAOLocal. I don't understand why I see different behavior for the following scenarios:
If I use an EJB (LoginDAO is stateless session bean) as follows, method call works perfectly without any error.
#EJB
private LoginDAOLocal loginDao;
loginDao.checkUser(userName,password);
If I use Struts2 as follows, it gives a Null pointer exception for the method call.
public class LoginAction extends ActionSupport {
// Getters setters for userName and password)
private LoginDAOLocal loginDao;
loginDao.checkUser(this.userName,this.password);
}
If I use a simple Java application (no EJB or Struts2), the method call creates a compile time error saying loginDao is not initialized
public static void main(String[] args) {
LoginDAOLocal loginDao;
loginDao.checkUser(userName,password);
}
Can someone explain why this different behavior ?
Without getting too much into the Java EE spec: EJBs are managed by an EJB container that exists in J2EE servers (JBoss \ Websphere etc..). The container takes control of bean lifecycle and is responsible for creating \ destroying beans according to the application needs.
When running out of container (simple java application) your beans won't get initialized and you don't have a JNDI context to get beans from, even if you add #EJB annotation to the field member.
We can say that there are two ways to manage the beans, using the container (managed by the container), or by another component (managed by a servlet, listener or filter).
Using components managed by the container, the container injects the references. e.g.:
#WebServlet("/test")
public class MyServlet extends HttpServlet {
#Resource(lookup = "jdbc/TestDS")
private DataSource testDS;
}
By contrast, a component managed by a bean, e.g.:
#Namespace("/User")
#ResultPath(value = "/")
#Result(name = "success", location = "pages/login.jsp")
public class LoginAction extends ActionSupport {
}
is managed by the filter org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter. The latter should be responsible for performing dependency injection. Spring, for example, takes care of injecting all necessary dependencies.
When I try to access EJB from another module inside one Application I got NullPointerException.
I'll explain an example to be more exact.
EAR Structure following:
EAR
|
— core.jar (EJB-module with core EJB) — application core
|
— app.jar (another EJB-module with EJBs) — business logic here
|
— web.war (servlets)
At code.jar there is EJB:
#LocalBean
#Singleton
#Startup
public class AppInfo(){
private int counter;
public void incCounter(){
counter++;
}
public int getCounterValue(){
return counter;
}
}
At module app.jar we have Stateless EJB which tries to read counter.
#Stateless
public class SomeBean{
#EJB private AppInfo appinfo;
public void run(){
int counter = appInfo.getCounterValue(); // here method throws with NPE
System.out.println("Counter value is: "+counter);
}
}
At module web.war there is servlet that increment counter value for every request:
public class MyServlet extends HttpServlet{
#EJB private AppInfo appInfo;
protected void doPost(params){
appInfo.incCounter();
...
other code
}
}
While debugging, I detected that:
At servlet injection work well: when entering doPost() appInfo is Proxy object for AppInfo EJB.
At SomeBean there is NPE: at run() entry point value of appInfo = null.
How I do correctly inject EJB from core.jar to another EJB from App?
P.S. I deploy EAR to JBoss 6.1 EAP
P.S.S. I also tried using #Remote: not working (appInfo is still equals null)
I think Local should work - this is in the same Application.
Try the fully qualified package name:
#EJB(beanName="my.package.AppInfo")
It seems to be that this is a JBoss deployment ordering issue. When the container creates a new instance of SomeBean and tries to inject the AppInfo bean, this one hasn't been created/deployed yet.
Try to rename your jars file, indicating the correct dependency order. (eg. a_core.jar and b_app.jar).
Other possibility could be to inject the singleton bean on a setter method.
To access EJB from another module, you need to do with following ways:
Use Remote access instead of Local access. or
Use JNDI.
For details, read this tutorial.