I've an EJB service.
#Stateless
public class SomeService {}
I'd like to inject this in a viewscoped bean and initialize with it:
#ManagedBean
#ViewScoped
public class ViewBean implements Serializable {
#EJB
private SomeService someService;
public ViewBean() {
System.out.println(someService.getEntity());
}
}
However, it throws the following exception:
com.sun.faces.mgbean.ManagedBeanCreationException: Cant instantiate class: com.example.ViewBean.
at com.sun.faces.mgbean.BeanBuilder.newBeanInstance(BeanBuilder.java:193)
at com.sun.faces.mgbean.BeanBuilder.build(BeanBuilder.java:102)
at com.sun.faces.mgbean.BeanManager.createAndPush(BeanManager.java:409)
at com.sun.faces.mgbean.BeanManager.create(BeanManager.java:269)
at com.sun.faces.el.ManagedBeanELResolver.resolveBean(ManagedBeanELResolver.java:244)
at com.sun.faces.el.ManagedBeanELResolver.getValue(ManagedBeanELResolver.java:116)
at com.sun.faces.el.DemuxCompositeELResolver._getValue(DemuxCompositeELResolver.java:176)
at com.sun.faces.el.DemuxCompositeELResolver.getValue(DemuxCompositeELResolver.java:203)
[snip]
Caused by: java.lang.NullPointerException
at com.example.ViewBean.<init>(ViewBean.java:42)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:525)
at java.lang.Class.newInstance0(Class.java:374)
at java.lang.Class.newInstance(Class.java:327)
at com.sun.faces.mgbean.BeanBuilder.newBeanInstance(BeanBuilder.java:188)
... 62 more
How is this caused and how can I solve it?
In other words, you're expecting that EJB injection works under the covers as follows:
ViewBean viewBean;
viewBean.someService = new SomeService(); // EJB injected, so that constructor can access it.
viewBean = new ViewBean(); // ViewBean constructed.
However, this is technically impossible. It's not possible to assign an instance variable when the instance isn't been constructed at all.
The canonical approach to perform a task based on injected dependencies directly after construction is to use a #PostConstruct annotated method.
So, to fix your concrete problem, just replace
public ViewBean() {
by
#PostConstruct
public void init() { // Note: Method name is fully free to your choice.
This way the process would under the covers be roughly as follows:
ViewBean viewBean;
viewBean = new ViewBean(); // ShiftBean constructed.
viewBean.someService = new SomeService(); // EJB injected.
viewBean.init(); // PostConstruct invoked.
Please note that the concrete problem has completely nothing to do with the view scope. You'd have had exactly the same problem when using a request, session or application scoped bean. This is thus another evidence that you have never actually excluded it from being the cause by testing using a different scope.
Related
I was reading/testing the following tutorial about CDI: [link].
And I got an exception when I added a producer class to the code.
Basically, there's an interface with a default implementation:
public interface ATMTransport {
public void communicateWithBank(byte[] datapacket);
}
#Default
public class StandardAtmTransport implements ATMTransport {
public void communicateWithBank(byte[] datapacket) {
System.out.println("communicating with bank via Standard transport");
}
}
Next, there's another class which injects the ATMTransport interface:
#Named("atm")
public class AutomatedTellerMachineImpl implements AutomatedTellerMachine {
#Inject
private ATMTransport transport;
public void deposit(BigDecimal bd) {
transport.communicateWithBank(null);
}
}
Everything is OK so far. So in the section about 'producers', they show a new class:
public class TransportFactory {
#Produces ATMTransport createTransport() {
System.out.println("ATMTransport created with producer");
return new StandardAtmTransport();
}
}
Then, by adding the producer class I got this exception:
WELD-001409: Ambiguous dependencies for type ATMTransport with qualifiers #Default
at injection point [BackedAnnotatedField] #Inject private AutomatedTellerMachineImpl.transport at AutomatedTellerMachineImpl.transport(AutomatedTellerMachineImpl.java:0)
Possible dependencies:
- Managed Bean [class StandardAtmTransport] with qualifiers [#Default #Any],
- Producer Method [ATMTransport] with qualifiers [#Any #Default] declared as [[BackedAnnotatedMethod] #Produces TransportFactory.createTransport()]
I solved the problem by using qualifiers, but I really don't know why.
My question is, why does the producer class cause that exception? (They didn't mention anything about it on their tutorial).
I really need a little explanation.
Based on the code you supplied, you ended up creating a default implementation of ATMTransport that is a managed bean. The exception is caused by having both beans available with the same injection targets. #Default is the qualifier added to all CDI beans. Basically, there is no need for the producer method since the one provided by the class definition by itself is the same.
I guess the bigger question - what were you trying to do with the producer method?
I tried to implement solution from another SO question:
https://stackoverflow.com/a/10059245/1943765 but it is not working in all cases.
When I put this code in CDI bean (bean code is shown on bottom):
#Inject #HttpParam
private String code;
everything works fine. But when I try to define value for #HttpParam annotation (so, not default one) Weld is unable to start:
#Inject #HttpParam(value="code")
private String token;
I get this exception:
Caused by: org.jboss.weld.exceptions.DeploymentException: WELD-001408: Unsatisfied dependencies for type String with qualifiers #HttpParam
at injection point [BackedAnnotatedField] #Inject #HttpParam private org.test.site.jsf.beans.request.ActivationBean.token
at org.test.site.jsf.beans.request.ActivationBean.token(ActivationBean.java:0)
WELD-001475: The following beans match by type, but none have matching qualifiers:
- Producer Method [String] with qualifiers [#HttpParam #Any] declared as [[BackedAnnotatedMethod] #Produces #HttpParam public org.test.site.cdi.producer.HttpParamProducer.getHttpParameter(InjectionPoint)]
at org.jboss.weld.bootstrap.Validator.validateInjectionPointForDeploymentProblems(Validator.java:359)
at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:281)
at org.jboss.weld.bootstrap.Validator.validateGeneralBean(Validator.java:134)
at org.jboss.weld.bootstrap.Validator.validateRIBean(Validator.java:155)
at org.jboss.weld.bootstrap.Validator.validateBean(Validator.java:518)
at org.jboss.weld.bootstrap.ConcurrentValidator$1.doWork(ConcurrentValidator.java:68)
at org.jboss.weld.bootstrap.ConcurrentValidator$1.doWork(ConcurrentValidator.java:66)
at org.jboss.weld.executor.IterativeWorkerTaskFactory$1.call(IterativeWorkerTaskFactory.java:63)
at org.jboss.weld.executor.IterativeWorkerTaskFactory$1.call(IterativeWorkerTaskFactory.java:56)
... 4 more
The code I used is similar to linked SO question.
The custom #HttpParam annotation:
#Qualifier
#Retention(RUNTIME)
#Target({METHOD, FIELD, PARAMETER})
public #interface HttpParam {
public String value() default "";
}
The annotation value producer:
public class HttpParamProducer {
#Inject
FacesContext facesContext;
#Produces
#HttpParam
String getHttpParameter(InjectionPoint ip) {
String name = ip.getAnnotated().getAnnotation(HttpParam.class).value();
if ("".equals(name)) name = ip.getMember().getName();
return facesContext.getExternalContext()
.getRequestParameterMap()
.get(name);
}
}
And CDI bean using it is something like:
#Named("activation")
#RequestScoped
public class ActivationBean implements Serializable{
#Inject
#HttpParam(value="code")
private String token;
// rest of valid class code
}
Also I am using Tomcat 8 server with Weld Servlet 2.3.1.Final.
So.. what am I doing wrong? :-/
You need to add #Nonbinding to your value attribute so that its value does not necessarily select which producer method to invoke, but allows for a single factory style producer inspecting your injection point.
You may also want to review what is the purpose of #Nonbinding annotation in a Qualifier supposed to be in Java EE7?
By default , attributes in CDI Qualifier also are used to determine which bean to be injected.
Since you inject #HttpParam(value="code") , but your producer just produces #HttpParam(value=""). They are not matched and CDI don't know what to inject.
To make CDI ignore attributes in CDI Qualifier when determining which bean to be injected, you can mark #Nonbinding on these attributes
#Qualifier
#Retention(RUNTIME)
#Target({METHOD, FIELD, PARAMETER})
public #interface HttpParam {
#Nonbinding
public String value() default "";
}
I am late to add this, but for the sake of others who may be using this question to write similar parameter based injections, let me add this.
Adding a specific which I did not find above. I faced the same situation, but with a twist in the flavor.My Qualifier and the Producer method were in a module (say injector-module) different from the one in which I was doing the injection (say injected-module). All NonBinding annotations were given.
However, I had the beans.xml only in the injected-module.
The injection failed.
Adding the beans.xml in the META-INF of the injector-module did the trick.
I've a session scoped bean:
#Named
#SessionScoped
public class SessionBean implements Serializable {
private String someProperty;
public String getSomeProperty() {
return someProperty;
}
}
I'd like to inject this in a request scoped bean and initialize with it:
#Named
#RequestScoped
public class RequestBean {
#Inject
private SessionBean sessionBean;
public RequestBean() {
System.out.println(sessionBean.getProperty());
}
}
However, it throws the following exception:
java.lang.NullPointerException
at com.example.RequestBean.<init>(RequestBean.java:42)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:525)
at org.jboss.weld.introspector.jlr.WeldConstructorImpl.newInstance(WeldConstructorImpl.java:206)
at org.jboss.weld.injection.ConstructorInjectionPoint.newInstance(ConstructorInjectionPoint.java:117)
at org.jboss.weld.bean.ManagedBean.createInstance(ManagedBean.java:336)
at org.jboss.weld.bean.ManagedBean$ManagedBeanInjectionTarget.produce(ManagedBean.java:200)
at org.jboss.weld.bean.ManagedBean.create(ManagedBean.java:292)
...
How is this caused and how can I solve it?
You're expecting that the injected dependency is available before the bean is constructed. You're expecting that it works like this:
RequestBean requestBean;
requestBean.sessionBean = sessionBean; // Injection.
requestBean = new RequestBean(); // Constructor invoked.
This is however not true and technically impossible. The dependencies are injected after construction.
RequestBean requestBean;
requestBean = new RequestBean(); // Constructor invoked.
requestBean.sessionBean = sessionBean; // Injection.
You should be using a #PostConstruct method instead if you intend to perform business logic based on injected dependencies directly after bean's construction.
Remove the constructor and add this method:
#PostConstruct
public void init() {
System.out.println(sessionBean.getSomeProperty());
}
BalusC's reply is correct, but is does reflect the assignment phase of a object creation, that did not run at this time. But anyway the CDI bean should be accessible if you grep it programatically via:
javax.enterprise.inject.spi.CDI.current().select(SessionBean.class).get()
I would like to rollback transaction not inside EJB but inside JSF managed bean. Inside EJB we can use SessionContext.setRollBackOnly() but what can I use in managed bean ?
#Stateless
#Local(AccountLocal.class)
public class AccountBean implements AccountLocal {
public void test1() throws CustomException(){
...
}
public void test2() throws CustomException(){
...
throw new CustomException();
}
public void test3() throws CustomException(){
...
}
public void all() throws CustomException(){
test1();
test2();
test3();
}
}
In my managed bean :
#SessionScoped
public class LoginBean implements Serializable{
public void test(){
try{
accountBean.test1();
accountBean.test2();
accountBean.test3();
}catch(CustomException e){
// WHAT HERE TO ROLLBACK TRANSACTION ?
}
}
}
EDIT : How can I ensure that if one of the test1, test2 or test3 rolls back, others will roll back too ?
I tested this code and accountBean.test1(); is validated even if accountBean.test2(); rolls back.
Could the solution be only to nest this 3 methods inside one EJB method ?
#SessionScoped
public class LoginBean implements Serializable{
public void test(){
try{
accountBean.all();
}catch(CustomException e){
...
}
}
}
Transactions are automatically rolled back by the EJB container if an unchecked exception is thrown (note that JPA's PersistenceException is such one). Your CustomException seems to be a checked exception. If changing it to extend RuntimeException as follows
public class CustomException extends RuntimeException {
// ...
}
or creating a new one is not an option, then you need to set the #ApplicationException annotation on the class with the rollback attribute set to true.
E.g.
#ApplicationException(rollback=true)
public class CustomException extends Exception {
// ...
}
Please note that the concrete problem has nothing to do with JSF. The service layer and managing transactions is completely outside the responsibility of JSF. It's the responsibility of EJB instead. JSF should merely act as "view" in this perspective.
See also:
JSF Service Layer
Handling service layer exception in Java EE frontend method
I'm playing the Devil's advocate here, since BalusC's advice that you should not let your backing beans act as services is absolutely true.
But, purely as a technical excersise, it -is- possible to start a JTA transaction in a backing bean and then control start and commit or rollback programmatically.
You can do this by injecting a UserTransaction via #Resource. Prior to calling your EJB methods, call start on this instance, and after the last call either commit or rollback.
Again, this is a purely theoretical answer. In practice, don't do this and let the backing bean call 1 EJB method that calls out to other EJB beans if needed.
I am using PrimeFaces UI library and JSF 2.
I have a backing bean:
public class JobMgmtBean extends ClientBeanBase implements Serializable
and
public class ClientBeanBase extends BeanBase
(so inheritance is JobMgmtBean:ClientBeanBase:BeanBase).
I wanted to set my JobMgmtBean from request scoped to view scoped, but after a while my sessionVars which is defined in BeanBase becomes null and the bean is not functional anymore.
I initialize sessionVars in the BeanBase like this:
protected Map<String,Object> sessionVars = null;
ex = FacesContext.getCurrentInstance().getExternalContext();
sessionVars = ex.getSessionMap();
I refresh some of my PrimeFaces UI components on the page every 5 seconds (using <p:poll interval="5"...>), and after a few refreshes sessionVars becomes null.
Why does this happen?
You can use View scope provided you can assemble the state of object during de-serialization.
Java provides method hooks for a serializable class where you can perform custom logic.
private void writeObject(java.io.ObjectOutputStream stream) throws java.io.IOException {
//custom logic
stream.defaultWriteObject();
}
private void readObject(java.io.ObjectInputStream stream) throws java.io.IOException, ClassNotFoundException {
stream.defaultReadObject();
// custom logic
}
Any bean reference you think you dont want serialize you can mark it as transient.
private transient Bean bean.
this bean wont get serialized but the problem is you are responsible
to set the reference back when it is deserailized in method hook
"readObject"
private void readObject(java.io.ObjectInputStream stream) throws java.io.IOException, ClassNotFoundException {
stream.defaultReadObject();
// custom logic
this.bean = ................
}
ViewScoped beans require objects to be Serialized, and my class extends many classes with too many object which all need to be Serialized which is not possible. This means that I can not use ViewScoped at all here.