JSF2: How to initiate services at deployment [duplicate] - jsf

This question already has an answer here:
Using special auto start servlet to initialize on startup and share application data
(1 answer)
Closed 7 years ago.
For university project I am developing a webapplication with JSF. My excercise is to do the frontend. A fellow studend is supposed to do backend stuff. Both parts are designed to be seerate applications. Both communicate through RMI. I want to open the connection once at deployment.
I am at the point to settle up the connection now. I tried to do that with a #ApplicationScoped ManagedBean:
//Constructor of ApplicationScoped ManagedBean
public Communication() {
this.connect();
}
Is that way possible? I tried it but the managedBean seems not to be called..
Can you advice a Best Practice?
#Brian: Unfortunately I don't use EJB at all -.-
#BalusC's pot:
I created a communicationbean:
#ManagedBean(name="communication")
#ApplicationScoped
public class Communication {
public static FrontendCommInterface server;
public Communication() {
this.connect();
}
Then I created the LoginBean:
#ManagedBean
#ViewScoped
public class Login {
#ManagedProperty(value="#{communication}")
private Communication communicationBean;
public FrontendCommInterface server;
private String username;
private String password;
public Login() {
server = communicationBean.getConnection();
}
public String login(){
HttpSession session = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(true);
String sessionId = session.getId();
try {
server.login(getUsername(), getPassword(), sessionId);
return "start.xhtml";
} catch (RemoteException e) {
e.printStackTrace();
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR,"Anmeldung nicht erfolgreich: ", getUsername()+", "+getPassword()+", "+sessionId));
return "login.xhtml";
}
}
But unfortunately it throws exceptions:
com.sun.faces.mgbean.ManagedBeanCreationException: Klasse org.dhbw.stg.wwi2008c.mopro.ui.managedBeans.Login can not be instanciated.
java.lang.NullPointerException
org.dhbw.stg.wwi2008c.mopro.ui.managedBeans.Login.<init>(Login.java:28)
After debuging I found out that my ManagedProperty is Null ! It hasn't been created! How to do that? I thought referencing via managedproperty would create it -.-

The managed bean is only auto-created whenever it's been referenced by EL #{managedBeanName}, which can happen by either accessing as-is in view, or by being injected as managed property of another bean, or being manually EL-resolved by e.g. Application#evaluateExpressionGet().
In your particular case, you actually want to intialize some stuff during webapp's startup. You rather want to use ServletContextListener for this.
#WebListener
public class Config implements ServletContextListener {
public void contextInitialized(ServletContextEvent event) {
// Do stuff during webapp's startup.
}
public void contextDestroyed(ServletContextEvent event) {
// Do stuff during webapp's shutdown.
}
}
You could even pre-create an application scoped managed bean there whenever necessary (if your intent is to be able to access it from other beans by #ManagedProperty).
public void contextInitialized(ServletContextEvent event) {
event.getServletContext().setAttribute("bean", new Bean());
}
JSF stores application scoped beans as an attribute of the ServletContext and JSF won't auto-create another one when one is already present, so the one and the same as created by the above code example will be used by JSF as well.

If you can use EJB 3.1 lite {1} in your web app, then you can use a Singleton Session Bean, annotated with #Startup, and a #PostConstruct method. I have one that looks like:
#Singleton
#Startup
public class CachePrimer {
#PostConstruct
public void loadOpenRequests() {
...
}
}
{1}: EJB 3.1 lite is included in the Web Profile of JavEE 6, and is provided by web profile servers like Glassfish, JBoss 6, and Resin. When using such a web profile server, you simply include your EJBs in your .war file, no additional work is required.

Related

Getting JSF ServletContext in non-request environment in a CDI bean

I am using TomEE+ 1.7.1.
With JSF managed beans this code was working well:
#ManagedBean( eager = true )
#ApplicationScoped
public class AppBean {
#PostConstruct
public void init() {
ServletContext sc = (ServletContext) FacesContext.getCurrentInstance().getExternalContext().getContext();
if (GlobalSettings.TESTMODE) {
sc.getSessionCookieConfig().setDomain("." + GlobalSettings.APP_DOMAIN_TEST);
} else {
sc.getSessionCookieConfig().setDomain("." + GlobalSettings.APP_DOMAIN);
}
}
}
The init function ran at application startup and ServletContext was available.
I read everywhere that it's time to migrate to CDI beans instead of JSF beans. So I wanted to change #ManagedBean( eager = true ) to #Named #Eager (#Eager is from Omnifaces). Init function is running at application startup, but there is no FacesContext so I can't get ServletContext.
General question: How to get ServletContext in a non-request environment in CDI beans? (ServletContext is not a 'per request' object, so it should exist before the first request.)
Specific question: how to set the domain for the session cookies dynamically from code but before the first request occurs?
You should be using a ServletContextListener for the purpose of performing programmatic configuration on a servlet based application.
#WebListener
public class Config implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent event) {
ServletContext servletContext = event.getServletContext();
// ...
}
#Override
public void contextDestroyed(ServletContextEvent event) {
ServletContext servletContext = event.getServletContext();
// ...
}
}
A #WebListener is inherently also CDI managed and thus you can just use #Inject and friends in there.
An application scoped managed bean is intented for holding application scoped data/state which can be used/shared across requests/views/sessions.
Per the CDI spec, you can #Inject the ServletContext into a CDI bean. Just be sure to do it in a #PostConstruct, as injected fields are available only after construction:
#Inject ServletContext extCtxt;
#PostConstruct
public void doSomething(){
// do something with your injected field
}

Cannot inject CDI #SessionScoped in HttpSessionListener

I have a stateful, session scoped (CDI) EJB that holds the info about the user session.
#Stateful
#SessionScoped
public class GestorSesion implements IGestorSesionLocal, Serializable {
private final static long serialVersionUID = 1L;
private static final Logger log = Logger.getLogger(GestorSesion.class.getName());
#PostConstruct
private void init() {
log.info("Configurando información de usuario");
log.info("****************************************************************************");
}
#Override
public void cerrarSesion() {
}
#Override
public ISessionInfo getSesionInfo() {
return null;
}
}
Now, I want to call cerrarSesion() (closeSession()) from an HttpSessionListener
public class GestorSesionWeb implements HttpSessionListener {
private static final Logger log = Logger.getLogger(GestorSesionWeb.class.getName());
#Inject
private Instance<IGestorSesionLocal> gestorSesion;
#Override
public void sessionCreated(HttpSessionEvent se) {
if (log.isLoggable(Level.FINE)) {
log.fine("Iniciada sesión web");
}
gestorSesion.get().getSesionInfo();
}
#Override
public void sessionDestroyed(HttpSessionEvent se) {
if (log.isLoggable(Level.FINE)) {
log.fine("Limpiando sesión al salir");
}
try {
this.gestorSesion.get().cerrarSesion();
if (log.isLoggable(Level.FINE)) {
log.fine("Sesión limpiada sin problemas");
}
} catch (Exception e) {
log.log(Level.WARNING, "Excepción limpiando sesión", e);
}
}
}
And I access the EJB from the webapp, directly (injecting using #EJB) into the beans that I use for JSF (they are also CDI managed beans).
The issue that I face is that it seems that the HttpSessionListener seems to be in a different "session scope" than the JSF beans. Two instances of GestorSession are created; one instantiated from the JSF and other instantiated from the HttpSessionListener.
I have tried injecting the bean via #Inject Instance<IGestorSesionLocal>, #Inject IGestorSesionLocal and BeanManager, with identical results.
This bug report suggests that it should to work correctly (the bug is solved for my version), but I still cannot get around it. I have looked around, but what I find are Q&A relating to JSF managed beans (yes, I could try to wrap a reference to the EJB in a JSF managed bean, but I would like to try it "correctly" first).
Working with WilFly 8.1.0 and JDK 7
Any ideas?
I think your pb comes from here:
And I access the EJB from the webapp, directly (injecting using
#EJB) into the JSF beans.
When use CDI with EJB (by putting a #Sessionscoped on it for instance) you got an CDI bean that also has an EJB nature but not the other way around. In other word CDI is aware of the EJB nature but EJB is not aware of CDI.
So if you want to inject an EJB as a CDI bean in your code use #Inject and not #EJB. From Java EE 6 the admitted good practice is to always use #Inject except for EJB remote.
Also be sure to only use CDI managed bean and not a mix of CDI and JSF managed bean.

Using CDI in java application

I'm writing Java SE application that uses CDI.
I have bean definition:
public class BeanA {
#PostConstruct
public void init() {
System.out.println("INIT");
}
public void receive(#Observes String test) {
System.out.println("received: " + test);
}
}
Requirements:
- I need to have many instances of BeanA in application
- I'd like to use Event CDI mechanism to communicate with that objects
When I use #Dependent scope, then #PostConstruct of BeanA is called everytime a new message is received. When I use #Singleton or #ApplicationScope then I can't have many objects of BeanA type.
What is solution to my problem?

Rollback transaction inside managed bean

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.

RESTEasy on TJWS with CDI

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.

Resources