Possible to inject #ManagedBean as a #ManagedProperty into #WebServlet? - java

In my Java EE 6-webapp (running on latest GlassFish 3.1), I'm using JSF2-ManagedBeans and #ManagedProperty to inject them into other ManagedBeans. Now i would like to know if i can also inject a #ManagedBean into a #WebServlet, using #ManagedProperty. Some code:
#WebServlet(name = "vdd")
public class VddServlet extends HttpServlet
{
#ManagedProperty(value = "#{userIdentity}")
private UserIdentity identity;
}
The ManagedBean looks like this:
#ManagedBean
public class UserIdentity
{
...
}
Does it work like this? If not, what other ways do i have to inject a ManagedBean into a WebServlet (without CDI, which is currently not an option - since there are some issues in GF 3.1 B32/33 in combination with OSGi-Java EE-apps, but we are short on time)?

Using #ManagedProperty in a servlet is not possible since this works in #ManagedBean classes only. Further, injecting an object which has a lesser scope than the parent itself is also not possible since that would also only end up in concurrency problems. The injector would throw a runtimeexception for that. A servlet is in essence application scoped and shared among all users and your UserIdentity bean seems to be session scoped.
Since JSF runs on top of the Servlet API and stores the session scoped beans in, well, the session, you could in the servlet just grab it as session attribute:
UserIdentity identity = (UserIdentity) request.getSession().getAttribute("userIdentity");
Note that the FacesContext is usually also not available in a servlet other than FacesServlet, so using FacesContext in the servlet as suggested in a comment does not make any sense, that would only return null.

Related

Why does JSP (and JSTL/EL) take a different way to perform a resource injection than that taken by Servlet or JSF?

To enable Servlet, JSP, JSF etc to use a remote EJB, performing resource injection is essential. JSP however takes a different way to inject an EJB into it than that taken by the rest. let's see all of them by example.
In JSF, a remote EJB can be injected in a very simple way as can be seen in the following ManagedBean. An EJB may a stateful, a stateless or a singleton.
Suppose the interface which is implemented by the session bean is namely CustomerBeanRemote
#ManagedBean
#RequestScoped
public class AddSubscription
{
#EJB
private CustomerBeanRemote obj=null;
//Getters and setters.
}
Here the remoter EJB can be injected only with one annotation #EJB just before the interface declaration. Since EJB is injected here, the interface is able to call the methods in the session bean even if it is explicitly assigned null. null here makes no sense at all.
Servlet takes the same view as shown below.
public class Add extends HttpServlet {
{
#EJB
private CustomerBeanRemote obj=null;
//Rest of the elegant Servlet code goes here.
}
JSP (and JSTL/EL) however takes a different view as mentioned below.
try
{
obj=(CustomerBeanRemote )new
InitialContext().lookup(CustomerBeanRemote.class.getName());
}
catch(NamingException e)
{
out.println(e.getMessage());
}
obj.someEJBMethod();
or
try
{
obj=(CustomerBeanRemote )new
InitialContext().lookup("Specific JNDI");
}
catch(NamingException e)
{
out.println(e.getMessage());
}
obj.someEJBMethod();
Why JSP (or JSTL/EL) requires a JNDI (Java Naming and Directory Interface) to perform a resource injection while in Servlet and JSF it can simply be performed with only one annotation which is #EJB?
A JSP is a servlet, so you should certainly be able to use the annotation in the JSP:
<%! #EJB private CustomerBeanRemote obj=null; %>
(untested though).
But even if it doesn't work, that's not a problem because JSPs should not contain Java code anyway. JSPs should be used to generate markup from beans prepared by a controller (Servlet or action of your preferred framework) written in Java.
See How to avoid Java code in JSP files?
Also, your snippets of code are wrong: they use the obj variable although a naming exception occurred when initializing them. This will obviously lead to a NullPointerException.

JSF Named Bean, Eager application scoped (aka #ManagedBean(eager=true) )

Is there any way to initialize Named Bean annotaded by javax.inject.Named/javax.enterprise.context.ApplicationScoped like #ManagedBean(eager=true) from javax.faces package?
#Named
#ApplicationScoped
public Mail() { ... }
I want to load this class when application starts, not when webapplication refers to this bean.
ps. JSF 2.1
Bean Injected by Glassfish 3.1
You can create a CDI extension that has the #Observes AfterBeanDiscovery parameter on one of his methods. There you can instantiate the bean and thus initialize it when the container starts up.
CODI has made those things easier for you, see https://cwiki.apache.org/confluence/display/EXTCDI/Core+Usage#CoreUsage-Startup

How to register a JSF managed bean programmatically?

I'd like to register/add a Managed Bean class programmatically (from within a Servlet init()) into application scope.
How can I do that with JSF 1.2?
It is unlikely that can do do this in a programmatic manner from your application for managed beans of all scopes. BalusC has already pointed out how to do this for application scoped managed beans.
Having taken a look at how managed beans are registered in Mojarra 2.1 (a JSF 2.1 implementation); there isn't a lot of lot of elegant options available for programmatic registration of session and request scoped beans. Simply put, you either have to invoke the implementation specific classes, or you will have to create and destroy i.e. manage the beans yourself instead of relying on the JSF implementation to do this.
Populating the request and session scopes with the beans (the unmanaged way)
Note - This is referred to as the "unmanaged way" because you are constructing the beans, and not the container. Annotations like #PostConstruct and #PreDestroy will not work, unless you process them yourself and invoke the appropriate methods. Even dependency-injection won't work.
EL expressions are always evaluated at runtime, so it gives you enough opportunity to populate the scope with the beans before evaluation (which allows for shooting yourself in the foot, if you have the chance to do so). In Mojarra (and possibly other JSF implementations), the EL resolver will rely on the services of a ScopeHandler (or an equivalent class) to resolve the EL expression values. Mojarra uses the classes ApplicationScopeHandler, RequestScopeHandler and SessionScopeHandler to obtain the values from the different scopes.
You can populate the contents of the Session and Request scopes after after a new session is created, or before a request is processed by the JSF implementation.
Session scope population can be done (ideally using a HttpSessionListener) using:
HttpSession session = request.getSession(false);
session == null ? null : session.setAttribute("<keyname>", new Bean());
The keyname must match the values you are using to reference the bean in EL expressions.
In a similar manner, you can populate the request scope (ideally done in a filter) using:
ServletRequest request = ... // get the reference to the servlet request object
request.setAttribute("<keyname>", new Bean());
If you need to understand how this works, you should take a look at the classes com.sun.faces.context.SessionMap, com.sun.faces.context.RequestMap and com.sun.faces.context.ApplicationMap to see how the context maps are managed internally, and used by the SessionScopeHandler, RequestScopeHandler and ApplicationScopeHandler classes that are static inner classes of the ScopeManager (another static inner) class of the com.sun.faces.mgbean.BeanManager class. The BeanManager class is the one that contains the managed bean registrations, and the next section discusses how to "hack into" the registration process of Mojarra.
Using the Mojarra classes to register the beans
Registration of managed beans in the Mojarra implementation is done by the public void register(ManagedBeanInfo beanInfo) method of the com.sun.faces.mgbean.BeanManager class. It is not trivial to access the BeanManager class using the JSF or Servlet APIs alone. There is however the ApplicationAssociate class of Mojarra that creates the BeanManager instance, and can be accessed using the getCurrentInstance() method. The other answer by Thomas already demonstartes how to register the managed bean programmatically:
ApplicationAssociate.getCurrentInstance().getBeanManager().register(...)
There is a caveat with the above approach. It is unlikely that this approach will work in the init method of Servlet for the simple reason that the getCurrentInstance method relies on a ThreadLocal variable to retrieve the ApplicationAssociate instance. The thread local variable is initialized by the com.sun.faces.application.WebappLifecycleListener class, so you must reproduce the mechanism used by the WebappLifecycleListener class, of invoking the ApplicationAssociate getInstance(ServletContext context) method, to gain access to the ApplicationAssociate instance. The following code therefore, might be (as I have not attempted using it) a better one, if you are willing to use Mojarra specific classes:
ServletContext sc = ... //get the ServletContext reference;
ApplicationAssociate.getInstance(sc).getBeanManager().register(...)
You must still watch out for quirks arising out of this mechanism as it is quite possible that some of the Mojarra classes and instances would not have been loaded or initialized before your Servlet. I would therefore suggest loading attempting to configure your servlet with a load-on-startup value that is higher than the one used by the FacesServlet.
from within a Servlet init()
So, it concerns a non-JSF request. The FacesContext#getCurrentInstance() would return null here, so it's of no use for you here.
It's good to know that JSF application scoped managed beans are basically stored as an attribute of the ServletContext. In the init() method you've the ServletContext at your hands by the inherited getServletContext() method. So, the following should do:
#Override
public void init() {
getServletContext().setAttribute("managedBeanName", new BackingBean());
}
That's it. It'll be available in JSF by #{managedBeanName}.
Try FacesContext.currentInstance().getExternalContext().getApplicationMap().put(name, bean);, i.e. put the managed bean instance into the map using the name you want to use in your expressions.
Edit:
To register the bean, try calling: ApplicationAssociate.getCurrentInstance().getBeanManager().register(...) and pass a ManagedBeanInfo you filled.
The following code registers the managed bean correcly using FacesContext, but it requires the servlet request and response. You could use the code and initialize it lazily using servlet and not during the init.
Usage:
UserBean ub = (UserBean)
Example.getBean(servletRequest, servletResponse, "user", UserBean.class);
Source:
import javax.el.ExpressionFactory;
import javax.el.ValueExpression;
import javax.faces.FactoryFinder;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.context.FacesContextFactory;
import javax.faces.lifecycle.LifecycleFactory;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
class Example {
public static Object getBean(HttpServletRequest request, HttpServletResponse response, String beanName, Class expectedType){
FacesContext ctx = getFacesContext(request, response);
ValueExpression vex = ctx.getApplication().getExpressionFactory().createValueExpression(ctx.getELContext(), "#{"+beanName+"}", expectedType);
return vex.getValue(ctx.getELContext());
}
private static FacesContext getFacesContext(HttpServletRequest request, HttpServletResponse response) {
FacesContext facesContext = FacesContext.getCurrentInstance();
if (facesContext == null) {
facesContext = ((FacesContextFactory) FactoryFinder.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY)).
getFacesContext(request.getSession().getServletContext(), request, response,
((LifecycleFactory) FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY))
.getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE));
InnerFacesContext.setFacesContextAsCurrentInstance(facesContext);
facesContext.setViewRoot(facesContext.getApplication().getViewHandler().createView(facesContext, ""));
}
return facesContext;
}
private abstract static class InnerFacesContext extends FacesContext {
protected static void setFacesContextAsCurrentInstance(FacesContext facesContext) {
FacesContext.setCurrentInstance(facesContext);
}
}
}
Say I have registered my beans using
ApplicationAssociate.getInstance(sc).getBeanManager().register(...)
Now It is working fine, But then there is a server restart and
my beans are destroyed, How on startup I can get the same bean registered.

Inject a Entity into a ViewScoped Bean

I am new to CDI and want to use this for a JSF2 application. The class MyUser is a simple #Entity-Bean and a object is created in a #PostConstruct method in bean:
#Stateful
#Named #javax.faces.bean.SessionScoped
public class UserBean implements Serializable
{
#Named
private MyUser user;
//setter and getter
//#PostConstruct
}
Accessing the user in a JSF pages works like a charm: #{user.lastName}. But now I want to access this object from other beans, e.g. in this #ViewScopedBean:
#Named #javax.faces.bean.ViewScoped
public class TestBean implements Serializable
{
#Inject private MyUser user;
}
I want the current (logged in) MyUser user to be available in a couple of other beans, but I'm not sure how to do this. Simply #Injecting it did not work (and I'm pretty sure this would be a litte bit to simple).
13:56:22,371 ERROR [org.jboss.kernel.plugins.dependency.AbstractKernelController]
Error installing to Start: name=vfs:///Applications/Development/
jboss-6.0.0.Final/server/default/deploy/test.ear_WeldBootstrapBean state=Create:
org.jboss.weld.exceptions.DeploymentException: WELD-001408 Unsatisfied
dependencies for type [MyUser] with qualifiers [#Default] at injection
point [[field] #Inject private test.controller.mbean.TestBean.user]
What is the best approach to access the user from other beans? JSF1.2 style code like UserBean bean = (UserBean)FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("UserBean"); seems seems to be old fashioned!
First of all: You don't want to directly inject entities. Entities are pretty independently controlled by the ORM-framework, and have their own life cycle. Don't use them as managed beans.
According to this definition, JPA
entities are technically managed
beans. However, entities have their
own special lifecycle, state and
identity model and are usually
instantiated by JPA or using new.
Therefore we don't recommend directly
injecting an entity class. We
especially recommend against assigning
a scope other than #Dependent to an
entity class, since JPA is not able to
persist injected CDI proxies.
See here for details.
To answer your question: You cannot "outject" something like an (authenticated) user, even though this was possible in Seam 2, the whole proxy mechanism of CDI doesn't allow this anymore. What you need to do is the following:
Write a managed bean which handles the authentication and put it in the correct scope (probably session scope).
If login succeeds, use an attribute of this bean to store the authenticated user.
Use a producer method (probably with a qualifier like #LoggedIn) to make the user availabe in your application
Inject the user like this:
#Inject
#LoggedIn
private User user
That's the CDI-way ;-)
Is the #Inject ed bean a #Named bean, too?
If it is, has the MyUser bean a lesser scope than the TestBean. Remember that a #ViewScoped bean's managed properties have to be #ViewScoped, #SessionScoped or #ApplicationScoped
CDI does not specify a #ViewScoped annotation. This is a JSF2 annotation. The only possible annotations allowed are : #RequestScoped, #SessionScoped, #ApplicationScoped, #Dependent and #ConversationScoped. The first three are the only JSF scopes allowed by CDI.
If you need to support the #ViewScope annotation, you'll need write it yourself. Luckily, someone else has done this before.

how to Inject an EJB into a java class

I want to inject an EJB3 into a java class which is not an EJB.
these classes are both on the same server and application.
Is that possible ...and if yes ..then how ?
Thanks,
Perhaps you should supply more information about your work environment. The usage of CDI changes the whole specturm. You can inject it when you use CDI, otherwise you can only inject it into other EJB's and servlets (if your application server supports it).
Otherwise you can do a lookup using
Context ctx = new InitialContext();
MyEjb ejb = (MyEjb) ctx.lookup("java:comp/env/myEjb");
You can supply a name in the #EJB annotation you supply together with your #Stateless/#Stateful annotation.
#Stateless
#EJB(name="myEjb", beanInterface=MyEjb.class)
public class myEjbImpl implements MyEjb{
// code goes here
}
You can't inject it, but you can make a lookup for that EJB:
Look here:
http://www.roseindia.net/ejb/ejb-lookup.shtml
During the deploymentprocess of your EJB you may see, the Name of your Bean.

Categories