Injecting an EJB into a dynamic mapped servlet - java

I have a filter where I am dinamically mapping servlet classes:
#Override
public void init( FilterConfig filterConfig ) throws ServletException {
servletContext = filterConfig.getServletContext();
File directory = getConventionDirectory();
FileSystemInspector fileInspector = new FileSystemInspector();
Set<ActionInfoData> actions = fileInspector.getActions( directory );
for ( ActionInfoData action : actions ) {
servletContext
.addServlet( action.getServletName(), action.getClassName() )
.addMapping( action.getServletMapping() );
}
}
Then when I access a given mapping the EJB is not injected.
#EJB
private I18nManager i18nManager;
#Override
protected void doGet( HttpServletRequest request, HttpServletResponse response )
throws ServletException, IOException {
I18nManager i18n = i18nManager; //null
}
If I manually create a mapping in the web.xml the given EJB is working in that servlet.
It makes me wonder if the fact I am registering the servlets at runtime the container does not consider those servlets as managed.
If that is the case what is the proper way to inject the EJBs into my servlets without changing the way they are dinamically registered via filter?
Is via JNDI the only way to inject my EJBs?
EDIT 1:
I have tried to implement a ServletContextListener class as suggested by "Will" using the following code in the web.xml:
<listener>
<listener-class>com.megafone.web.filter.convention.InitServlet</listener-class>
</listener>
And the relevant part of the implementation:
...
#Override
public void contextInitialized( ServletContextEvent sce ) {
ServletContext servletContext = sce.getServletContext();
FileSystemInspector fileInspector = new FileSystemInspector();
Set<ActionInfoData> actions = fileInspector.getActions( getConventionDirectory() );
for ( ActionInfoData action : actions ) {
servletContext
.addServlet( action.getServletName(), action.getClassName() )
.addMapping( action.getServletMapping() );
}
}
...
Unfortunately it does not make the container inject the EJBs and the null pointer remains. I am currently making a custom type safe JNDI lookup to the service. Obviously this is far more expensive than using the proper injection (correct me if I am wrong, have done no experiments regarding performance yet).
Using:
Java EE 6
JBoss AS 7.1

The problem seems related to this reported bug which is as yet unresolved. Resource resolution works just fine for Managed Beans as defined by the JSF specification, but not for CDI Managed Beans. Simply annotating your dynamic servlet classes with #javax.faces.bean.ManagedBean should fix the problem (yes, its quite an ugly solution):
#ManagedBean
public class DynServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
#EJB
private LoginService loginService;
public DynServlet() {
super();
}
#Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
response.getOutputStream().println(
"Request made to: " + getClass().getSimpleName());
response.getOutputStream().println("Login Service: " + loginService);
return;
}
}
#WebListener
public class DynamicServletLoadListener implements ServletContextListener {
public DynamicServletLoadListener() {
super();
}
#Override
public void contextDestroyed(final ServletContextEvent contextEvent) {
return;
}
#Override
public void contextInitialized(final ServletContextEvent contextEvent) {
contextEvent.getServletContext().addServlet("dynservlet", DynServlet.class)
.addMapping("/services/dynservlet");
}
}
Tested with JEE6 (ofc) and both JBoss 7.1.1 and 7.2.0 (EAP 6.1.0 Alpha).
Edit:
The problem with dynamic mapped servlets is actually pretty deep in the base JBoss architecture. They use JBossWeb (a forked version of Tomcat) as the servlet implementation, and in the guts of its context management code it makes a determination wether to instantiate new components via injection or regular new. Afaik as of date, your servlets would need to be annotated in some fashion in order for them to be processed via injection: I had mentioned #ManagedBean in my original answer but it looks like annotating with #WebServlet works as well.

Servlet 3.0 Spec, Sect. 4.4.3.5
Resource injection [e.g. #EJB] on all components (Servlets, Filters and Listeners) added
programmatically or created programmatically, other than the ones added via the
methods that takes an instance, will only be supported when the component is a
Managed Bean. For details about what is a Managed Bean please refer to the
Managed Bean specification defined as part of Java EE 6 and JSR 299.
Managed Bean Declaration
A Java EE 6 managed bean is annotated #javax.annotation.ManagedBean and has a no-arg constructor. A JSR 299 (CDI) managed bean merely needs a no-arg constructor or a constructor annotated #javax.inject.Inject.
Answer
To enable resource injection you need to:
place #ManagedBean annotation on dynamically added servlet
OR
enable CDI & include an empty beans.xml
Edit
Even though you're creating the servlet dynamically, it's important that the container does the creation. Don't think creation within ServletContext will support injection. Servlet doc very vague here.
With CDI try:
servletContext.addServlet("your servlet name", #Inject YourServletClass servlet)

First, in my test, this worked fine using a version of Glassfish V3.
But, second, you may well be running afoul of this clause of the Servlet 3.0 spec.
The following methods are added to ServletContext since Servlet 3.0 to
enable programmatic definition of servlets, filters and the url
pattern that they map to. These methods can only be called during the
initialization of the application either from the contexInitialized
method of a ServletContextListener implementation or from the
onStartup method of a ServletContainerInitializer implementation.
Notably, these methods can NOT be called from a Filter.init() method. I originally tried this in a Servlet.init() method, and the init method failed because the context was already initialized.
So, my experiment did not duplicate your test exactly -- I did not use a Filter.init() method for this, rather I put the code in a ServletContextListener. And when I did that, my #EJB annotation was honored.
Edit:
As un-helpful as it sounds, I would suggest this is a bug in JBoss. When I initially tried your original code with the injection from the Filter, Glassfish threw an exception, since you're not allowed to do the injection save where I mentioned earlier. Now perhaps that's an "added feature" of JBoss, but apparently the #EJB injection processing simply isn't working. According to spec, this should work as advertised.

Related

In JBoss How do I trigger the code in my war? [duplicate]

I need to get some configuration and connect to external resources/objects/systems somewhere and store it in application scope.
I can see two ways to setup my application:
Overriding the init() in the existing servlets and required code there and keeping all constructed objects inside that same servlet.
Having some kind of an initialisation servlet and using its init() to do the work. Then storing created objects in ServletContext to share it with my other servlets.
Which out of above is better approach? Is there any better way to share objects between servlets? Calling them directly from one another or so...?
None of both is the better approach. Servlets are intended to listen on HTTP events (HTTP requests), not on deployment events (startup/shutdown).
CDI/EJB unavailable? Use ServletContextListener
#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.
}
}
If you're not on Servlet 3.0 yet and can't upgrade (it would be about time because Servlet 3.0 was introduced more than a decade ago), and thus can't use #WebListener annotation, then you need to manually register it in /WEB-INF/web.xml like below:
<listener>
<listener-class>com.example.Config</listener-class>
</listener>
To store and obtain objects in the application scope (so that all servlets can access them), use ServletContext#setAttribute() and #getAttribute().
Here's an example which lets the listener store itself in the application scope:
public void contextInitialized(ServletContextEvent event) {
event.getServletContext().setAttribute("config", this);
// ...
}
and then obtain it in a servlet:
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
Config config = (Config) getServletContext().getAttribute("config");
// ...
}
It's also available in JSP EL by ${config}. So you could make it a simple bean as well.
CDI available? Use #Observes on ApplicationScoped.class
import jakarta.enterprise.context.ApplicationScoped; // And thus NOT e.g. jakarta.faces.bean.ApplicationScoped
#ApplicationScoped
public class Config {
public void init(#Observes #Initialized(ApplicationScoped.class) ServletContext context) {
// Do stuff during webapp's startup.
}
public void destroy(#Observes #Destroyed(ApplicationScoped.class) ServletContext context) {
// Do stuff during webapp's shutdown.
}
}
This is available in a servlet via #Inject. Make it if necessary also #Named so it's available via #{config} in EL as well.
Noted should be that this is new since CDI 1.1. If you're still on CDI 1.0 and can't upgrade, then pick another approach.
In case you're curious how to install CDI on a non-JEE server such as Tomcat, head to: How to install and use CDI on Tomcat?
EJB available? Consider #Startup#Singleton
#Startup
#Singleton
public class Config {
#PostConstruct
public void init() {
// Do stuff during webapp's startup.
}
#PreDestroy
public void destroy() {
// Do stuff during webapp's shutdown.
}
}
This is available in a servlet via #EJB. The difference with other approaches is that it's by default transactional and in case of #Singleton also read/write locked. So if you would ever need to inject a random EJB (e.g. #Stateless) into a #WebListener or an #ApplicationScoped then you could basically as good merge both into a single #Startup #Singleton.
See also:
How to run a background task in a servlet based web application?
ServletContainerInitializer vs ServletContextListener
How do I force an application-scoped bean to instantiate at application startup?

Java servlet to remember object instance [duplicate]

I need to get some configuration and connect to external resources/objects/systems somewhere and store it in application scope.
I can see two ways to setup my application:
Overriding the init() in the existing servlets and required code there and keeping all constructed objects inside that same servlet.
Having some kind of an initialisation servlet and using its init() to do the work. Then storing created objects in ServletContext to share it with my other servlets.
Which out of above is better approach? Is there any better way to share objects between servlets? Calling them directly from one another or so...?
None of both is the better approach. Servlets are intended to listen on HTTP events (HTTP requests), not on deployment events (startup/shutdown).
CDI/EJB unavailable? Use ServletContextListener
#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.
}
}
If you're not on Servlet 3.0 yet and can't upgrade (it would be about time because Servlet 3.0 was introduced more than a decade ago), and thus can't use #WebListener annotation, then you need to manually register it in /WEB-INF/web.xml like below:
<listener>
<listener-class>com.example.Config</listener-class>
</listener>
To store and obtain objects in the application scope (so that all servlets can access them), use ServletContext#setAttribute() and #getAttribute().
Here's an example which lets the listener store itself in the application scope:
public void contextInitialized(ServletContextEvent event) {
event.getServletContext().setAttribute("config", this);
// ...
}
and then obtain it in a servlet:
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
Config config = (Config) getServletContext().getAttribute("config");
// ...
}
It's also available in JSP EL by ${config}. So you could make it a simple bean as well.
CDI available? Use #Observes on ApplicationScoped.class
import jakarta.enterprise.context.ApplicationScoped; // And thus NOT e.g. jakarta.faces.bean.ApplicationScoped
#ApplicationScoped
public class Config {
public void init(#Observes #Initialized(ApplicationScoped.class) ServletContext context) {
// Do stuff during webapp's startup.
}
public void destroy(#Observes #Destroyed(ApplicationScoped.class) ServletContext context) {
// Do stuff during webapp's shutdown.
}
}
This is available in a servlet via #Inject. Make it if necessary also #Named so it's available via #{config} in EL as well.
Noted should be that this is new since CDI 1.1. If you're still on CDI 1.0 and can't upgrade, then pick another approach.
In case you're curious how to install CDI on a non-JEE server such as Tomcat, head to: How to install and use CDI on Tomcat?
EJB available? Consider #Startup#Singleton
#Startup
#Singleton
public class Config {
#PostConstruct
public void init() {
// Do stuff during webapp's startup.
}
#PreDestroy
public void destroy() {
// Do stuff during webapp's shutdown.
}
}
This is available in a servlet via #EJB. The difference with other approaches is that it's by default transactional and in case of #Singleton also read/write locked. So if you would ever need to inject a random EJB (e.g. #Stateless) into a #WebListener or an #ApplicationScoped then you could basically as good merge both into a single #Startup #Singleton.
See also:
How to run a background task in a servlet based web application?
ServletContainerInitializer vs ServletContextListener
How do I force an application-scoped bean to instantiate at application startup?

Configuring the vaadin- prefix

From the 7.6 release notes
Declarative format is now using "vaadin-" as a default prefix instead of the "v-" prefix used in 7.5. This default can be changed in deployment configuration.
How can we configure this prefix? I'm having trouble locating the setting, I'd guess that it's an "InitParameter" but I can't seem to locate a list. I don't see it mentioned on DeploymentConfiguration classes either. annotation based/javaconfig preferred, but I could probably reverse an XML.
I "believe" this will work in Spring, though on reflection I'm not sure we actually use this feature.
public class VaadinWebApplicationInitializer implements WebApplicationInitializer
{
#Override
public void onStartup( final ServletContext servletContext ) throws ServletException
{
Dynamic dynamic = servletContext.addServlet( "Vaadin Application Servlet", ApplicationServlet.class );
dynamic.setInitParameter( Constants.SERVLET_PARAMETER_LEGACY_DESIGN_PREFIX, Boolean.TRUE.toString() );
...

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.

Spring #Autowired in Servlet

I am using Spring framework (2.5.4) in my app with Load time weaving and everything works fine everywhere (in Spring beans, in non-Spring entities), except when I try to autowire field in a servlet annotated as #Configurable, then I get a nice NullPointerException...
#Configurable(dependencyCheck=true)
public class CaptchaServlet extends HttpServlet{
#Autowired
private CaptchaServiceIface captchaService;
#Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
// ApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(config.getServletContext());
// captchaService = (CaptchaServiceIface) ctx.getBean("captchaService");
}
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Captcha c = captchaService.getCatpcha();
req.getSession().setAttribute("captchaAnswer", c.getAnswer());
resp.setContentType("image/png");
ImageIO.write(c.getImage(), "png", resp.getOutputStream());
}
}
<context:load-time-weaver/>
<context:spring-configured/>
<context:component-scan base-package="cz.flexibla2" />
Any suggestions about what am I doing incorrectly?
Thanks.
This is likely because the Servlet is being instantiated and initialized by the servlet container, before the Spring context is being initialized, and it's the Spring context which handles the load-time weaving.
Is your <context:load-time-weaver/> stuff being handle inside the servlet Spring context/ or at the webapp-level? The former almost certainly won't work (for the reasons specified above), but a webapp-level config might work (using ContextLoaderListener).
See also mailing list discussion and bug report at https:// bugs.eclipse.org/bugs/show_bug.cgi?id=317874 . I agree that intuitively the #Configurable annotation on the servlet should be enough to indicate to the spring framework that the servlet when instantiated will be configured by spring, when using <context:spring-configured/>. I have also observed that the desired behavior is achievable when using the -javaagent:/path/to/aspectjweaver.jar instead of spring-instrument*.jar or spring-agent.jar. Please raise an issue with Spring Jira at https:// jira.springframework.org/browse/SPR. I believe that the problem may be that the servlet class - not an instance of the servlet, but the class itself - is loaded before the spring ContextLoaderListener is called, thus the spring framework does not have a chance to instrument the servlet class before it is loaded.
The spring instrumentation for load-time-weaving appears to be based on being able to transform class bytecode before it is loaded. If the servlet container is holding onto an instance of the Class object that was obtained before it was transformed by spring, then it (the servlet container) would not be able to produce instances of the transformed class, nor would spring be able to instrument instances created using the factory methods on that Class object.

Categories