I've built a simple ear application consisting of an EJB and a WAR module. I'm using CDI interceptor to intercept the call to JSF actions to do some operation. Everything is working fine except for a strange behaviour that arises when I annotate the action method using the interceptorbiding annotation. To be more precise, here is the code:
This is the interceptor biding class
#Inherited
#Documented
#InterceptorBinding
#Retention(RUNTIME)
#Target({METHOD, TYPE})
public #interface SecurityCheck {
}
This is the interceptor class:
#Interceptor
#SecurityCheck
#Priority(Interceptor.Priority.APPLICATION)
public class SecurityCheckInterceptor implements Serializable {
#Inject
SecurityService secutiyService;
#AroundInvoke
public Object securityIntercept(InvocationContext ic) throws Exception {
String target = // build the fully qualified method name from ic
System.out.println("---- ENTER: ["+target+"]");
Object out = ic.proceed();
System.out.println("---- EXIT: ["+target+"]");
return out;
}
}
And these are the two alternative of usage of the interceptor in my JSF bean class:
// METHOD N. 1
#Named
#ViewScoped
public class IndexController implements Serializable {
...
#Interceptors({SecurityCheckInterceptor.class})
public void doAction() {
System.out.println("indexController.doAction()");
}
}
and
// METHOD N. 2
#Named
#ViewScoped
public class IndexController implements Serializable {
...
#SecurityCheck
public void doAction() {
System.out.println("indexController.doAction()");
}
}
when I use the method N. 1 the output is as follow:
---- ENTER: [it.univaq.we2018.tutor.controller.IndexController.doAction()]
indexController.doAction()
---- EXIT: [it.univaq.we2018.tutor.controller.IndexController.doAction()]
so everything is as expected, but using Method n.2 (which I prefer) the output is as follow:
---- ENTER: [it.univaq.we2018.tutor.controller.IndexController.doAction()]
---- ENTER: [it.univaq.we2018.tutor.controller.IndexController.doAction()]
indexController.doAction()
---- EXIT: [it.univaq.we2018.tutor.controller.IndexController.doAction()]
---- EXIT: [it.univaq.we2018.tutor.controller.IndexController.doAction()]
The interceptor is called twice in a "nested" fashion.
Is this a bug? Have I missed something?
I'm using Java 1.8_172, Payara 5.181, NetBeans 8.2 and JavaEE 7 profile. The application is based on the javaee7 maven archetype. All the annotation are CDI and not the JSF ones (eg. ViewScoped).
Thank you.
How can I inject a dependency like #EJB, #PersistenceContext, #Inject, #AutoWired, etc in a #FacesConverter? In my specific case I need to inject an EJB via #EJB:
#FacesConverter
public class MyConverter implements Converter {
#EJB
protected MyService myService;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
// myService.doSomething
}
}
However, it didn't get injected and it remains null, resulting in NPEs. It seems that #PersistenceContext and #Inject also doesn't work.
How do I inject a service dependency in my converter so that I can access the DB?
Can I use #EJB to inject my service into a #FacesConverter?
No, not until JSF 2.3 is released. The JSF/CDI guys are working on that for JSF 2.3. See also JSF spec issue 1349 and this related "What's new in JSF 2.3?" article of my fellow Arjan Tijms. Only then dependency injection like #EJB, #PersistenceContext, #Inject, etc will work in a #FacesConverter when you explicitly add managed=true attribute to the annotation.
#FacesConverter(value="yourConverter", managed=true)
public class YourConverter implements Converter {
#Inject
private YourService service;
// ...
}
If not, what's the "correct" way to do this?
Before JSF 2.3, you have several options:
Make it a managed bean instead. You can make it a JSF, CDI or Spring managed bean via #ManagedBean, #Named or #Component. The below example makes it a JSF managed bean.
#ManagedBean
#RequestScoped
public class YourConverter implements Converter {
#EJB
private YourService service;
// ...
}
And the below example makes it a CDI managed bean.
#Named
#RequestScoped
public class YourConverter implements Converter {
#Inject
private YourService service;
// ...
}
Reference it as <h:inputXxx converter="#{yourConverter}"> instead of <h:inputXxx converter="yourConverter">, or as <f:converter binding="#{yourConverter}"> instead of <f:converter converterId="yourConverter">. Don't forget to remove the #FacesConverter annotation!
The disadvantage is that you cannot specify forClass and thus need to manually define the converter everywhere in the view where necessary.
Inject it in a regular managed bean instead.
#ManagedBean
#RequestScoped
public class YourBean {
#EJB
private YourService service;
// ...
}
And in your converter, grab or call it via EL.
YourBean yourBean = context.getApplication().evaluateExpressionGet(context, "#{yourBean}", YourBean.class);
// Then e.g. either
YourEntity yourEntity = yourBean.getService().findByStringId(value);
// Or
YourEntity yourEntity = yourBean.findEntityByStringId(value);
This way you can keep using #FacesConverter.
Manually grab the EJB from JNDI.
YourService yourService = (YourService) new InitialContext().lookup("java:global/appName/YourService");
The disadvantage is that there is a certain risk that this is not entirely portable. See also Inject EJB bean from JSF managed bean programmatically.
Install OmniFaces. Since version 1.6, it transparently adds support for #EJB (and #Inject) in a #FacesConverter without any further modification. See also the showcase. If you happen to need the converter for <f:selectItem(s)>, then the alternative is to use its SelectItemsConverter which will automatically perform the conversion job based on select items without the need for any database interaction.
<h:selectOneMenu ... converter="omnifaces.SelectItemsConverter">
See also Conversion Error setting value for 'null Converter'.
See also:
How to inject in #FacesValidator with #EJB, #PersistenceContext, #Inject, #Autowired
CDI Injection into a FacesConverter
Getting an #EJB in a #FacesValidator and #FacesConverter
The answer is Yes if you can accommodate Seam Faces module in your web application. Please check this post Injection of EntityManager or CDI Bean in FacesConverter. You can use #EJB in similar fashion.
You could access it indirectly through FacesContext, which is a parameter in both Converter methods.
The converter could be also annotated CDI Named with Application scope. When accessing the facade, two instances of the same class are used. One is the converter instance itself, dumb, without knowing EJB annotation. Another instance retains in application scope and could be accessed via the FacesContext. That instance is a Named object, thus it knows the EJB annotation. As everything is done in a single class, access could be kept protected.
See the following example:
#FacesConverter(forClass=Product.class)
#Named
#ApplicationScoped
public class ProductConverter implements Converter{
#EJB protected ProductFacade facade;
protected ProductFacade getFacadeFromConverter(FacesContext ctx){
if(facade==null){
facade = ((ProductConverter) ctx.getApplication()
.evaluateExpressionGet(ctx,"#{productConverter}",ProductConverter.class))
.facade;
}
return facade;
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
return getFacadeFromConverter(context).find(Long.parseLong(value));
}
...
#Inject will only works in CDI managed instances
This only works at least Java EE 7 and CDI 1.1 server:
#FacesConverter
public class MyConverter implements Converter {
protected MyService myService;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
myService = CDI.current().select(MyService .class).get();
myService.doSomething();
}
}
https://docs.oracle.com/javaee/7/api/javax/enterprise/inject/spi/CDI.html
https://stackoverflow.com/a/33017416/5626568
By Luis Chacon, Sv
Works fine, tested
definition EJB :
#Stateless
#LocalBean
public class RubroEJB {
#PersistenceContext(unitName = "xxxxx")
private EntityManager em;
public List<CfgRubroPres> getAllCfgRubroPres(){
List<CfgRubroPres> rubros = null;
Query q = em.createNamedQuery("xxxxxxx");
rubros = q.getResultList();
return rubros;
}
}
define bean with the Aplication bean scope, for get the EJB Object
#ManagedBean(name="cuentaPresService", eager = true)
#ApplicationScoped
public class CuentaPresService {
#EJB
private RubroEJB cfgCuentaEJB;
public RubroEJB getCfgCuentaEJB() {
return cfgCuentaEJB;
}
public void setCfgCuentaEJB(RubroEJB cfgCuentaEJB) {
this.cfgCuentaEJB = cfgCuentaEJB;
}
}
final Access to Ejb Object from Converter:
#FacesConverter("cuentaPresConverter")
public class CuentaPresConverter implements Converter {
#EJB
RubroEJB rubroEJB;
public Object getAsObject(FacesContext fc, UIComponent uic, String value) {
if(value != null && value.trim().length() > 0) {
try {
CuentaPresService service = (CuentaPresService) fc.getExternalContext().getApplicationMap().get("cuentaPresService");
List<CfgCuentaPres> listCuentas=service.getCfgCuentaEJB().getAllCfgCuentaPres();
................
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 am trying DeltaSpike Data Module on Wildfly i followed the things mentioned in the document, when I try to run a Servlet having a Repository i am getting a NullPointerException while using the repository
#Inject
private OrdersRepository orderRep;
List<OrderDto> dao = orderRep.findByRetailer("MyRetail"); // NullPointer
Code
#Repository(forEntity = Order.class)
#MappingConfig(OrderMapper.class)
#EntityManagerConfig(entityManagerResolver = MyDBResolver.class)
public abstract class OrdersRepository extends AbstractEntityRepository<OrderDto, String> {
#Query(named = Order.ORDER_BY_RETAILER, max = 1)
public abstract List<OrderDto> findByRetailer(String retailer);
}
...
Am I missing anything here ?
Try adding #Dependent to your repository classes.
CDI 1.1 used in WildFly has implicit bean archives by default, i.e. candidate bean classes require a bean defining annotation.
I try to use TJWS Embeddable Servlet Container to start RestEasy application using this userguide http://docs.jboss.org/resteasy/docs/2.3.3.Final/userguide/html/RESTEasy_Embedded_Container.html#d0e2640
Application work correct in JBOSS7-AS. I want to use TJWS for debuging and unit testing, but have problem with dependency injection.
I create resource class UserResource, which using CDI to inject utility class UserManager:
#Path("users")
#SessionScoped
class UserResource {
#Inject
UserManager userManager; // simple interface and imlementation
public UserResource() {} // constructor with no parameters for bean
#Path("list")
#GET
public List<User> list() {
List<User> userList = userManager.getList(); // NullPointerException
return userList;
}
}
Start TJWS in main:
public static void main(String[] args) throws IOException {
TJWSEmbeddedJaxrsServer tjws = new TJWSEmbeddedJaxrsServer();
tjws.setPort(9997);
tjws.start();
tjws.getDeployment().getRegistry().addPerRequestResource(User.class);
}
When i try to get http://localhost/users/list via browser, i get NullPointerException in UserResource.list() method, because userManager not injected and is null.
Is there any way to inject userManager?
TJWS is a standalone servlet container and web server that does not support EE annotations like #Inject. For the code to work you have to use EE container such as JBoss AS.