We are trying to use alternative bean instance injection for our integration test suite deployed on a Wildfly 10.1.0 server.
According to the CDI 1.2 spec, a possible solution to do so would be to use the #Specializes annotation on an alternative deployed in the integration test archive only.
However, the default implementation is always injected. We tried #Specializes on managed beans, session beans, and tried to select the alternatives in the beans.xml file.
The following example illustrate the issue:
BeanInterface.java
public interface BeanInterface {
void work();
}
Implementation1.java
#Dependent
public class Implementation1 implements BeanInterface {
#Override
public void work() {
System.out.println("test 1");
}
}
Implementation2
#Dependent
#Alternative
#Specializes
public class Implementation2 extends Implementation1 {
#Override
public void work() {
System.out.println("test 2");
}
}
TestSingleton.java:
#Singleton
#Startup
public class TestSingleton {
#Inject
private BeanInterface beanInterface;
#PostConstruct
public void init() {
this.beanInterface.work();
}
}
Packaging these classes in a war (with a web.xml) and deploying on wildfly, the implementation 1 is always injected in the Stateless bean.
Wildfly 10.1.0 uses weld-2.3.SP2 which implements CDI 1.2.
Thanks,
Charly
Although it does not make the #Specializes annotation work as expected, this solution suggested by John Ament allows to inject the second Implementation.
Just change the #javax.enterprise.inject.Specializes annotation with #javax.annotation.Priority (and some value):
#Dependent
#Alternative
#Priority(100)
public class Implementation2 extends Implementation1 {
#Override
public void work() {
System.out.println("test 2");
}
}
Also missing in the OP question was the beans.xml (not web.xml) packaged in the WEB-INF:
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
bean-discovery-mode="all">
</beans>
Related
I have a bean configured which have some initialization logic. I have annotated this bean using #ApplicationScoped annotation. But somehow, cdi is not picking this bean.
beans.xml content:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
bean-discovery-mode="annotated">
</beans>
Bean file:
#ApplicationScoped
public class Initializer{
#Inject #ConfigProperty(name = "app.name")
private String appName;
#Inject #ConfigProperty(name = "app.token")
private String appToken;
#Inject #ConfigProperty(name = "app.version")
private String appVersion;
#PostConstruct
public void init() {
System.out.println("flow should come here....); //but this line does not execute.
}
}
Code to read config file:
#Exclude
public class ConfigurationFile implements PropertyFileConfig {
#Override
public String getPropertyFileName() {
String env = Util.getEnv();
switch (env) {
case "dev":
case "uat":
case "prod":
return "config/" + env + "/default.properties";
default:
return "config/default.properties";
}
}
#Override
public boolean isOptional() {
return false;
}
}
I am using:
cdiL: for dependency injection,
apache-deltaspike: for reading config file.
wildfly-swarm: server
I have got the solution to this problem.
Issue is solved by changing the method declaration as follows:
public void init(#Observes #Initialized(ApplicationScoped.class) Object init) {
//................code logic here................
}
I have a JSF bean in WAR module which has an #EJB that works. When I move the JSF bean to a Java library (JAR), the #EJB doesn't works (I get a NullPointerException when I try to access the EJB). I need to move from scenario I to scenario II:
Scenario I:
Enterprise Application
WAR Module:
JSF Page:
<p>#{testController.test}</p>
JSF Bean:
#Named
public class TestController {
#EJB
private TestEjb testEjb;
public String getTest() {
return testEjb.test();
}
}
EJB Module:
#Stateless
public class TestEjbImpl implements TestEjb {
#Override
public String test() {
return (new Date()).toString();
}
}
Scenario II:
Enterprise Application
WAR Module:
JSF Page:
<p>#{testController.test}</p>
JSF Bean:
[Empty - the JSF bean was moved to a Java Library (JAR)]
EJB Module:
#Stateless
public class TestEjbImpl implements TestEjb {
#Override
public String test() {
return (new Date()).toString();
}
}
Java Library (JAR):
#Named
public class TestController {
#EJB
private TestEjb testEjb;
public String getTest() {
return testEjb.test(); // Here I get a NullPointerException
}
}
I have created an Enterprise Application Project with a Java Library that reproduces this problem. This is a Netbeans Project: https://dl.dropboxusercontent.com/u/64616807/stackoverflow/jee6.rar
I wanted to know, is there any option to call a managed bean inside of EJB bean. Imagine, we have the code:
#ManagedBean
#SessionScoped
public class MyManagedBean implements Serializable {
public String getUrl() {
return "http://www.google.com";
}
}
#Stateless
public class MyEJB {
#ManagedProperty(value = "#{myManagedBean}")
MyManagedBean myManagedBean;
public void setMyManagedBean(MyManagedBean myManagedBean) {
this.myManagedBean = myManagedBean;
}
public void call() {
// NullPointerException here
System.out.println(myManagedBean.getUrl());
}
}
I also tried this:
#Stateless
public class MyEJB {
#EJB
MyManagedBean myManagedBean;
...
}
... but it returns different MyManagedBean instance.
This is not right. With CDI managed beans instead of JSF managed beans it's possible, but it is just not right as in, bad design. The business service should not be aware about the front-end at all. It makes the business service unreusable on other front-ends than JSF.
You should do it the other way round. You should inject the EJB in the managed bean, not the other way round. The EJB should be kept entirely stateless. You should just directly pass the EJB the information it needs as method argument (and never assign it as instance variable of EJB afterwards).
E.g.
#ManagedBean
#SessionScoped // <-- Did you read https://stackoverflow.com/q/7031885?
public class MyManagedBean implements Serializable {
private String url = "http://www.google.com";
#EJB
private MyEJB myEJB;
public void submit() {
myEJB.call(url);
}
public String getUrl() {
return url;
}
}
and
#Stateless
public class MyEJB {
public void call(String url) {
// No NullPointerException here.
System.out.println(url);
}
}
See also:
JSF Service Layer
I want a managed bean to run internally on start up in my JSF web application when the application loads. How can I write this class and configure in Glassfish?
In JSF with CDI, observe the initialization of the application scope.
#Named
#ApplicationScoped
public class App {
public void startup(#Observes #Initialized(ApplicationScoped.class) Object context) {
// ...
}
public void shutdown(#Observes #Destroyed(ApplicationScoped.class) Object context) {
// ...
}
}
When having OmniFaces at hands, this can be simplified with #Eager.
#Named
#Eager
#ApplicationScoped
public class App {
#PostConstruct
public void startup() {
// ...
}
#PreDestroy
public void shutdown() {
// ...
}
}
In JSF 2.2- with the now deprecated javax.faces.bean annotations, use an application scoped managed bean which is eagerly initialized.
#ManagedBean(eager=true)
#ApplicationScoped
public class App {
#PostConstruct
public void startup() {
// ...
}
#PreDestroy
public void shutdown() {
// ...
}
}
(Java EE 6 with Glassfish 3.1)
I have a property file that I want to process only once at start up time, so I did this
public class Config implements ServletContextListener{
private static final String CONFIG_FILE_PATH = "C:\\dev\\harry\\core.cfg";
private static final String CONFIG_ATTRIBUTE_NAME = "config";
private long startupTime;
private ConfigRecord config;
#Override
public void contextInitialized(ServletContextEvent sce) {
this.startupTime = System.currentTimeMillis() / 1000;
this.config = new ConfigRecord(CONFIG_FILE_PATH); //Parse the property file
sce.getServletContext().setAttribute(CONFIG_ATTRIBUTE_NAME, this);
}
#Override
public void contextDestroyed(ServletContextEvent sce) {
//Nothing to do here
}
public ConfigRecord getConfig() {
return config;
}
public long getStartupTime() {
return startupTime;
}
}
and in web.xml, i register it as follow
<listener>
<listener-class>com.wf.docsys.core.servlet.Config</listener-class>
</listener>
Now how do I access the ConfigRecord config from the managed bean. I try this
#ManagedBean
#RequestScoped
public class DisplayInbound {
#EJB
private CoreMainEJBLocal coreMainEJBLocal;
#javax.ws.rs.core.Context
private ServletContext servletContext;
public void test(){
Config config = (Config) servletContext.getAttribute("config")
ConfigRecord configRecord = config.getConfig();
}
}
I dont think it work. Got NullPointerException.
That #Context annotation is only applicable in a JAX-RS controller, not in a JSF managed bean. You have to use #ManagedProperty instead. The ServletContext is available by ExternalContext#getContext(). The FacesContext itself is available by #{facesContext}.
#ManagedProperty(value="#{facesContext.externalContext.context}")
private ServletContext context;
Or because you stored the listener as a servletcontext attribute, which is basically the same as the JSF application scope, you could also just set it as managed property by its attribute name:
#ManagedProperty(value="#{config}")
private Config config;
But since you're on JSF 2.0, I'd suggest to use an #ApplicationScoped #ManagedBean instead which is eagerly constructed. With #PostConstruct and #PreDestroy in such a bean you have similar hooks on webapp's startup and shutdown as in a ServletContextListener.
#ManagedBean(eager=true)
#ApplicationScoped
public void Config {
#PostConstruct
public void applicationInitialized() {
// ...
}
#PreDestroy
public void applicationDestroyed() {
// ...
}
}
You can inject it in another beans the usual #ManagedProperty way and access it in the views the usual EL way.