Undertow Websocket Bean Injection CDI Issue - java

I don't understand why CDI use of injection doesn't work with websockets, using undertow.
Below is the code I have for a simple websocket endpoint.
#ServerEndpoint("/")
public class TestWebSocketEndpoint {
#Inject
private RetrieveAccessor retrieveAccessor;
private final Logger logger = Logger.getLogger(this.getClass().getName());
#OnOpen
public void onConnectionOpen(Session session) {
logger.info("Connection opened ... " + session.getId());
}
#OnMessage
public String onMessage(String message) {
if (!message.isEmpty()) {
return message;
}
System.out.println("RETRIEVE BEAN -> " + retrieveAccessor);
if (retrieveAccessor != null) {
return "BEAN NOT NULL";
}
return ":(";
}
#OnClose
public void onConnectionClose(Session session) {
logger.info("Connection close .... " + session.getId());
}
}
Of course the issue is that the injected property is null. I have no problems of course using the rest side of things for this deployment and injection of the stateless bean described below. Is there a work around for this, what are the problems I could run into if I just init properties I need that are beans? Because that definitely works.
RetrieveAccessor retrieveAccessor = new.... {code}

An easy way to get injection working on your #ServerEndpoint annotated classes is to set a custom configurator that handles the creation of your endpoint instance by overriding the getEndpointInstance(Class endpointClass) method to instantiate with CDI.
https://tyrus.java.net/documentation/1.13/user-guide.html#d0e464
Annotated endpoint:
#ServerEndpoint(value = "/", configurator = CDIEndpointConfigurator.class)
public class TestWebSocketEndpoint {
...
}
Custom configurator:
public class CDIEndpointConfigurator extends ServerEndpointConfig.Configurator {
#Override
public <T> T getEndpointInstance(Class<T> endpointClass) throws InstantiationException {
return CDI.current().select(endpointClass).get();
}
}

Undertow is only a servlet container. Weld (or OWB) provide CDI support. I'm not sure how you're instantiating Undertow, but you need to leverage Weld (or some other CDI implementation).
Here's one example how to do it. Leverage a CDI Extension to find the endpoints, and once you have them you can register them in Undertow
Feel free to leverage Hammock for this.

Related

Can Spring 3 supports WebSocket [duplicate]

I'm using Spring 3.2.5 without full new JSR-356 WebSockets support.
I would like to have singleton-bean reference in my #ServerEndpoint WebSocket server, which is instantiated by servlet container itself, not in Spring context.
What is the clean way to do it?
My current solution: I made #Service singleton bean with instance in static field:
#Service
public class WebSocketSupportBean {
private volatile static WebSocketSupportBean instance = null;
public static WebSocketSupportBean getInstance() {
return instance;
}
public WebSocketSupportBean() {
instance = this;
}
and just getting it in #ServerEndpoint by static method, disconnecting user if null returned (if bean not jet created during server startup but user connects):
You can setup websockets with spring framework 3.x
I developed a small proof-of-concept application to demonstrate how, based on Rossen Stoyanchev's SpringConfiguration released with spring-core 4.0.
The application sets up a websocket server endpoint with uri /wstest which will use a #Autowired spring bean to select a greeting word and reply to a websocket message.
The websocket connection is initiated and messages sent by an html page (index.html) running in a browser that supports websockets.
The Endpoint registration is made by a ServletContextListener at context initialization and when the endpoint is instantiated it will be wired with spring:
#WebListener
public class MyApplication implements ServletContextListener {
private final static String SERVER_CONTAINER_ATTRIBUTE = "javax.websocket.server.ServerContainer";
#Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext container = sce.getServletContext();
final ServerContainer serverContainer = (ServerContainer) container.getAttribute(SERVER_CONTAINER_ATTRIBUTE);
try {
serverContainer.addEndpoint(new MyEndpointConfig(MyEndpoint.class, "/wstest"));
} catch (DeploymentException e) {
e.printStackTrace();
}
}
}
And the Endpoint is:
#Component
public class MyEndpoint extends Endpoint {
#Autowired
MyService myService;
#Override
public void onOpen(Session session, EndpointConfig config) {
session.addMessageHandler(new MyMessageHandler(session));
}
class MyMessageHandler implements MessageHandler.Whole<String> {
final Session session;
public MyMessageHandler(Session session) {
this.session = session;
}
#Override
public void onMessage(String message) {
try {
String greeting = myService.getGreeting();
session.getBasicRemote().sendText(greeting + ", got your message (" + message + "). Thanks ! (session: " + session.getId() + ")");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Checkout the full source and ready to run example on my Github page.
You have to add bean definition in the configuration of spring.
The solution i found to integrate JSR 356 websocket #ServerEndpoint is to turn off the Servlet container's scan for WebSocket endpoints by spring which can be done by registering #Bean in your Spring Configuration. By this spring not overrides normal JSR 356 websocket by spring STOMP websocket which is the part of the websocket.
#ServerEndpoint(value="/chatMessage")
public class ChatEndpoint{
// Normal websocket body goes here.
}
Adding Beans in your Configuration as:
#Configuration
public class WebsocketConfig{
#Bean
public ChatEndpoint chatEndpoint(){
return new ChatEndpoint();
}
// main one is ServerEndpointExporter which prevents Servlet container's scan for WebSocket
#Bean
public ServerEndpointExporter endpointExporter(){
return new ServerEndpointExporter();
}
}
This all done for you. But you should remove configurator = SpringConfigurator.class from #ServerEndpoint.
I am using Spring Websocket 4.0.0 and it works fine.
You can also see this Link.
If you alright then follow this Link also for concept.
Note that, Normally you should make websocket configuration separately from the main configuration of your spring.
Try
#ServerEndpoint(value = "/ws", configurator = SpringConfigurator.class)
And add maven dependency
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
</dependency>
You can make your #ServerEndpoint object extend SpringBeanAutowiringSupport. Then just make it aware of beans that gets constructed within a Spring-based web application this way:
#PostConstruct
public void init() {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
}
This way #Autowired annotation will worl correctly:
#Autowired MyService myService;
try this,it works for me
#Component
#ServerEndpoint(value = "/instantMessageServer",configurator = SpringConfigurator.class)
public class InstantMessageServer{
private static IChatService chatService;
#Autowired
public InstantMessageServer(IChatService chatService){
this.chatService = chatService;
}
public InstantMessageServer(){}
}
I found this solution on https://spring.io/blog/2013/05/23/spring-framework-4-0-m1-websocket-support
but there is one more glitch,the class annotated with #ServerEndpoint cant acquire httpsession with SpringConfigurator,there is no a override of method modifyhandler in it.Maybe we create a seperate Configurator extends SpringConfigurator and override that method would be a workaroud.
It is better to build a real-time web application with spring-websocket and messaging api,I think.
public class ModifiedServerEndpointConfigurator extends SpringConfigurator{
#Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
HttpSession httpSession = (HttpSession) request.getHttpSession();
sec.getUserProperties().put(HttpSession.class.getName(),httpSession);
super.modifyHandshake(sec, request, response);
}
}

Spring Jersey to inject ContainerRequestContext on request scoped class

I already looked at a lot of posts and nothing seems to work quite as i liked it to.
I want to inject an object into ContainerRequestContext properties from a filter and retrieve it later in other classes.
here is my filter:
#Priority(Priorities.AUTHENTICATION)
public class AuthorizationFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext containerRequestContext) throws IOException {
containerRequestContext.setProperty("myObject", new Object());
}
}
here is the class I want access to ContainerRequestContext:
#Provider
public class SessionContextProvider implements ISessionContextProvider {
#Context
private ContainerRequestContext request;
#Override
public Object getSessionContext() {
return request.getProperty("mySessionContext");
}
}
and my spring config:
#Bean(name="sessionContextProvider")
#Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES)
public ISessionContextProvider sessionContextProvider() {
return new SessionContextProvider();
}
Everything works as expected if I inject ContainerRequestContext into my web resource. However if call my provider class ContainerRequestContext is always null.
I don't seem why this would not work.
Reagrds
Jonas
The problem is that with the Jersey/Spring integration, it allows us to successfully inject Spring beans into Jersey components, but this is not always true the other way around.
Jersey has it's own DI framework, HK21, and it is responsible for handle the injections with Jersey components. With the Jersey Spring integration, Jersey will lookup the Spring Bean, and take it as is, it won't inject it with any dependencies, I guess assuming Spring should take care of it's own injections.
That being said, if you don't require the ISessionContextProvider to be a Spring bean, then you can just make it an HK2 service. It's pretty simple. If you don't require any special initialization, you can just let HK2 create it. Here a simple configuration
public JerseyConfig extends ResourceConfig {
public JerseyConfig() {
register(new AbstractBinder() {
bind(SessionContextProvider.class)
.to(ISessionContextProvider.class)
.in(RequestScoped.class);
});
}
}
And that's it. You have an injectable ISessionContextProvider2.
If you require the ISessionContextProvider provider to be a Spring bean, then another option is to grab the bean from the Spring ApplicatinContext, and explicitly inject it yourself, using HK2's analogue of the ApplicationContext, its ServiceLocator. To do that we would need to use a Factory to do all the work transparently, so you can still inject the bean without doing any extra work on the outside
import javax.inject.Inject;
import org.glassfish.hk2.api.Factory;
import org.glassfish.hk2.api.ServiceLocator;
import org.springframework.context.ApplicationContext;
public class SessionContextProviderFactory
implements Factory<SessionContextProvider> {
private final ISessionContextProvider provider;
#Inject
public SessionContextProviderFactory(ApplicationContext ctx,
ServiceLocator locator) {
provider = ctx.getBean(ISessionContextProvider.class);
locator.inject(provider);
}
#Override
public ISessionContextProvider provide() {
return provider;
}
#Override
public void dispost(ISessionContextProvider provider) { /* noop */ }
}
Then just register the factory
public JerseyConfig extends ResourceConfig {
public JerseyConfig() {
register(new AbstractBinder() {
bindFactory(SessionContextProviderFactory.class)
.to(ISessionContextProvider.class)
.in(RequestScoped.class);
});
}
}
1 - hk2
2 - See also Dependency injection with Jersey 2.0
I found a workaround. I could inject the Spring HttpServletRequest into my AuthorizationFilter and set the SessionContext on this one.
#Autowired
private HttpServletRequest request;
....
request.setAttribute("mySessionContext", sessionContext);
And then because HttpServletRequest is known to spring and besically represents the same thing in my SessionContextProvider I do the same:
#Autowired
private HttpServletRequest request;
#Override
public SessionContext getSessionContext() {
return (SessionContext) request.getAttribute("mySessionContext");
}
I dont think this is the best solution possible. But it works. Ill wait for peeskillet for any other input if there is a better solution.
Regards
Jonas

How to instantiate Spring managed beans at runtime?

I stuck with a simple refactoring from plain Java to Spring. Application has a "Container" object which instantiates its parts at runtime. Let me explain with the code:
public class Container {
private List<RuntimeBean> runtimeBeans = new ArrayList<RuntimeBean>();
public void load() {
// repeated several times depending on external data/environment
RuntimeBean beanRuntime = createRuntimeBean();
runtimeBeans.add(beanRuntime);
}
public RuntimeBean createRuntimeBean() {
// should create bean which internally can have some
// spring annotations or in other words
// should be managed by spring
}
}
Basically, during load container asks some external system to provide him information about number and configuration of each RuntimeBean and then it create beans according to given spec.
The problem is: usually when we do in Spring
ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfiguration.class);
Container container = (Container) context.getBean("container");
our object is fully configured and have all dependencies injected. But in my case I have to instantiate some objects which also needs dependency injection after I execute load() method.
How can I achieve that?
I am using a Java-based config. I already tried making a factory for RuntimeBeans:
public class BeanRuntimeFactory {
#Bean
public RuntimeBean createRuntimeBean() {
return new RuntimeBean();
}
}
Expecting #Bean to work in so called 'lite' mode. http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/Bean.html Unfortunately, I found no difference with simply doing new RuntimeBean();
Here is a post with a similar issue: How to get beans created by FactoryBean spring managed?
There is also http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/annotation/Configurable.html but it looks like a hammer in my case.
I also tried ApplicationContext.getBean("runtimeBean", args) where runtimeBean has a "Prototype" scope, but getBean is an awful solution.
Update 1
To be more concrete I am trying to refactor this class:
https://github.com/apache/lucene-solr/blob/trunk/solr/core/src/java/org/apache/solr/core/CoreContainer.java
#see #load() method and find "return create(cd, false);"
Update 2
I found quite interesting thing called "lookup method injection" in spring documentation:
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#beans-factory-lookup-method-injection
And also an interesting jira ticket https://jira.spring.io/browse/SPR-5192 where Phil Webb says https://jira.spring.io/browse/SPR-5192?focusedCommentId=86051&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-86051 that javax.inject.Provider should be used here (it reminds me Guice).
Update 3
There is also http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/config/ServiceLocatorFactoryBean.html
Update 4
The issue with all these 'lookup' methods is they don't support passing any arguments.. I also need to pass arguments as I would do with applicationContext.getBean("runtimeBean", arg1, arg2). Looks like it was fixed at some point with https://jira.spring.io/browse/SPR-7431
Update 5
Google Guice have a neat feature for it called AssistedInject. https://github.com/google/guice/wiki/AssistedInject
Looks like I found a solution. As I am using java based configuration it is even simpler than you can imagine. Alternative way in xml would be lookup-method, however only from spring version 4.1.X as it supports passing arguments to the method.
Here is a complete working example:
public class Container {
private List<RuntimeBean> runtimeBeans = new ArrayList<RuntimeBean>();
private RuntimeBeanFactory runtimeBeanFactory;
public void load() {
// repeated several times depending on external data/environment
runtimeBeans.add(createRuntimeBean("Some external info1"));
runtimeBeans.add(createRuntimeBean("Some external info2"));
}
public RuntimeBean createRuntimeBean(String info) {
// should create bean which internally can have some
// spring annotations or in other words
// should be managed by spring
return runtimeBeanFactory.createRuntimeBean(info);
}
public void setRuntimeBeanFactory(RuntimeBeanFactory runtimeBeanFactory) {
this.runtimeBeanFactory = runtimeBeanFactory;
}
}
public interface RuntimeBeanFactory {
RuntimeBean createRuntimeBean(String info);
}
//and finally
#Configuration
public class ApplicationConfiguration {
#Bean
Container container() {
Container container = new Container(beanToInject());
container.setBeanRuntimeFactory(runtimeBeanFactory());
return container;
}
// LOOK HOW IT IS SIMPLE IN THE JAVA CONFIGURATION
#Bean
public BeanRuntimeFactory runtimeBeanFactory() {
return new BeanRuntimeFactory() {
public RuntimeBean createRuntimeBean(String beanName) {
return runtimeBean(beanName);
}
};
}
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
RuntimeBean runtimeBean(String beanName) {
return new RuntimeBean(beanName);
}
}
class RuntimeBean {
#Autowired
Container container;
}
That's it.
Thanks everyone.
i think that your concept is wrong by using
RuntimeBean beanRuntime = createRuntimeBean();
you are bypassing Spring container and resorting to using regular java constructor therefore any annotations on factory method are ignored and this bean is never managed by Spring
here is the solution to create multiple prototype beans in one method, not pretty looking but should work, I autowired container in RuntimeBean as proof of autowiring shown in log also you can see in log that every bean is new instance of prototype when you run this .
'
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
ApplicationContext context = new AnnotationConfigApplicationContext(Application.class);
Container container = (Container) context.getBean("container");
container.load();
}
}
#Component
class Container {
private List<RuntimeBean> runtimeBeans = new ArrayList<RuntimeBean>();
#Autowired
ApplicationContext context;
#Autowired
private ObjectFactory<RuntimeBean> myBeanFactory;
public void load() {
// repeated several times depending on external data/environment
for (int i = 0; i < 10; i++) {
// **************************************
// COMENTED OUT THE WRONG STUFFF
// RuntimeBean beanRuntime = context.getBean(RuntimeBean.class);
// createRuntimeBean();
//
// **************************************
RuntimeBean beanRuntime = myBeanFactory.getObject();
runtimeBeans.add(beanRuntime);
System.out.println(beanRuntime + " " + beanRuntime.container);
}
}
#Bean
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
public RuntimeBean createRuntimeBean() {
return new RuntimeBean();
}
}
// #Component
class RuntimeBean {
#Autowired
Container container;
} '
A simple approach:
#Component
public class RuntimeBeanBuilder {
#Autowired
private ApplicationContext applicationContext;
public MyObject load(String beanName, MyObject myObject) {
ConfigurableApplicationContext configContext = (ConfigurableApplicationContext) applicationContext;
SingletonBeanRegistry beanRegistry = configContext.getBeanFactory();
if (beanRegistry.containsSingleton(beanName)) {
return beanRegistry.getSingleton(beanName);
} else {
beanRegistry.registerSingleton(beanName, myObject);
return beanRegistry.getSingleton(beanName);
}
}
}
#Service
public MyService{
//inject your builder and create or load beans
#Autowired
private RuntimeBeanBuilder builder;
//do something
}
Instead of using SingletonBeanRegistry you can use this:
BeanFactory beanFactory = configContext.getBeanFactory();
Anyway SingletonBeanBuilder extends HierarchicalBeanFactory and HierarchicalBeanFactory extends BeanFactory
You don't need the Container because all of the runtime objects should be created, held and managed by ApplicationContext. Think about a web application, they are much the same. Each request contains external data/environment info as you mentioned above. What you need is a prototype/request scoped bean like ExternalData or EnvironmentInfo which can read and hold runtime data through a static way, let's say a static factory method.
<bean id="externalData" class="ExternalData"
factory-method="read" scope="prototype"></bean>
<bean id="environmentInfo" class="EnvironmentInfo"
factory-method="read" scope="prototype/singleton"></bean>
<bean class="RuntimeBean" scope="prototype">
<property name="externalData" ref="externalData">
<property name="environmentInfo" ref="environmentInfo">
</bean>
If you do need a container to save the runtime objects, code should be
class Container {
List list;
ApplicationContext context;//injected by spring if Container is not a prototype bean
public void load() {// no loop inside, each time call load() will load a runtime object
RuntimeBean bean = context.getBean(RuntimeBean.class); // see official doc
list.add(bean);// do whatever
}
}
Official doc Singleton beans with prototype-bean dependencies.
It is possible to register beans dynamically by using BeanFactoryPostProcesor. Here you can do that while the application is booting (spring's application context has been initialized). You can not register beans latest, but on the other hand, you can make use of dependency injection for your beans, as they become "true" Spring beans.
public class DynamicBeansRegistar implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
if (! (beanFactory instanceof BeanDefinitionRegistry)) {
throw new RuntimeException("BeanFactory is not instance of BeanDefinitionRegistry");
}
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
// here you can fire your logic to get definition for your beans at runtime and
// then register all beans you need (possibly inside a loop)
BeanDefinition dynamicBean = BeanDefinitionBuilder.
.rootBeanDefinition(TheClassOfYourDynamicBean.class) // here you define the class
.setScope(BeanDefinition.SCOPE_SINGLETON)
.addDependsOn("someOtherBean") // make sure all other needed beans are initialized
// you can set factory method, constructor args using other methods of this builder
.getBeanDefinition();
registry.registerBeanDefinition("your.bean.name", dynamicBean);
}
#Component
class SomeOtherClass {
// NOTE: it is possible to autowire the bean
#Autowired
private TheClassOfYourDynamicBean myDynamicBean;
}
As presented above, you can still utilize Spring's Dependency Injection, because the post processor works on the actual Application Context.

how to use an Object injected with #Inject within a server endpoints #OnOpen method

I have a Bean, which creates an instance of an object, which i need to inject into other beans. Everything works fine, i can print a property of the injected object in the #PostConstruct method, but if i try to call a method from that injected object inside the #OnOpen method of a ServerEndpoint it gives me a NullPointerException.
Here is my ServerEndpoint
#Named
#ApplicationScoped
#ServerEndpoint(value = "/websocket")
public class BeanThree {
private String message = "test";
#Inject private User user;
#PostConstruct
public void init() { System.out.println(user.getUserName()); } <-- displayed in the console correctly
public String getMessage() { return this.message; }
#OnOpen
public void onOpen(Session session) {
System.out.println("onOpen");
System.out.println(user.getUserName()); <-- causes NullPointerException
}
}
Is it possible to fix this?
Edit1:
Im using cdi 1.2, jetty 9.1, jsf 2.2, java-ee7 and websockets from java-ee7
The problem is that BeanThree is declared both a CDI bean and an endpoint at the same time.
It has to be split into two different beans:
#ServerEndpoint("/endpoint")
public class BeanThree {
#Inject
ApplicationScopedBean bean;
#OnOpen
public String onOpen(Session s) { System.out.println(bean); }
#OnMessage
public String onMessage(String message) { System.out.println(bean); }
}
#ApplicationScoped
public class ApplicationScopedBean { ... }
But there's another issue.
CDI / Websocket integration is very limited: out of the box you can inject #ApplicationScoped and probably #Dependent beans only.
From your snippet it seems you intend to use #SessionScoped User bean withing a Websocket session. That's not going to work because Websocket and HTTP sessions differ.
You'll have to manage Websocket sessions and session-bound data by yourself. Here's an example.
One way to do this is to allow CDI to instantiate it as a CDI bean.
Then subclasses the following classes: ServerEndpointConfig.Configurator
#ServerEndpoint(..., configurator=MyCustomConfigurator.class)
#SessionScoped
#Named("myMessageHandler")
public class MyMessageHandler{
#Inject
private MyInjectable instance;
...
}
public class MyCustomConfigurator extends ServerEndpointConfig.Configurator{
public <T extends Object> getEndpointInstance(Class<T> endpointClass) throws InstantiationException{
//do cdi lookup for endpoint using the simple name.
}
To have a reference to the cdi BeanManager, have a look at this thread:
http://dominikdorn.com/2010/04/cdi-weld-manual-bean-lookup/
In your case, you dont have a reference to FacesContext, so use the ServletContext

Why Weld says "Injection into Listeners is not supported" on Tomcat?

I have a web project using Resteasy (which in turn uses Weld) and is deployed to Tomcat 7.0.22 (I put the specific version here in case this issue is particular to this version).
I have a ServletContextListener that looks like this:
#WebListener
public class ApplicationInitialisationListener implements ServletContextListener {
// create a logger here
#Inject
HealthCheck healthCheck;
#Override
public void contextInitialized(ServletContextEvent event) {
if (healthCheck == null) {
log.error("healthCheck is null");
}
}
#Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
}
}
After deploying to Tomcat, healthCheck is null was logged, and I also noticed this line in the log:
<2013-11-13 13:27:40,191> <pack> INFO pool-2-thread-1 org.jboss.weld.environment.tomcat7.Tomcat7Container - Tomcat 7 detected, CDI injection will be available in Servlets and Filters. Injection into Listeners is not supported
Question 1: why is CDI injection not available in Listeners?
I looked into this answer, and it says Load on startup via #Startup. There is currently no equivalent to this in CDI.
Question 2: is the issue described in Question 1 a consequence of this?
Question 3: I am using org.jboss.weld.servlet:weld-servlet:1.2.0.Beta1. Is there any update on startup support in later versions?
Related Questions I Looked
startup class in Weld
Here is a workaround I discovered that can inject CDI beans when an application starts.
The requirement of the problem can be summarized as:
inject a CDI bean when the application starts
do something with the bean
Solution outline line:
Create a WebListener that calls BeanManager.fireEvent(new SomeDummyEvent())
Create an ApplicationScoped bean that responds to SomeDummyEvent and injects the CDI bean
Example code:
#WebListener
public class ApplicationInitialisationListener implements ServletContextListener {
private static final Logger LOG = Logger.getLogger(ApplicationInitialisationListener.class);
#Override
public void contextInitialized(ServletContextEvent event) {
BeanManager beanManager = lookUpBeanManager();
if (beanManager != null) {
beanManager.fireEvent(new SomeDummyEvent());
LOG.info("beanManager fired SomeDummyEvent.");
} else {
LOG.error("beanManager is null. Cannot fire startup event.");
}
}
#Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
}
public BeanManager lookUpBeanManager() {
try {
// See reference below about how I came up with this
InitialContext iniCtx = new InitialContext();
BeanManager result = (BeanManager) iniCtx.lookup("java:comp/env/BeanManager");
return result;
} catch (NamingException e) {
LOG.error("Could not construct BeanManager.", e);
return null;
}
}
public static class SomeDummyEvent implements Serializable {
}
}
#ApplicationScoped
public class InitializationResourceBean {
private static final Logger LOG = Logger.getLogger(InitializationResourceBean.class);
#Inject
HealthCheck healthCheck;
public void listen(#Observes ApplicationInitialisationListener.SomeDummyEvent event) {
}
#PostConstruct
public void init() {
// Do something with healthCheck
}
#PreDestroy
public void destroy() {
// Do some other thing with healthCheck
}
}
References:
http://struberg.wordpress.com/tag/cdi/
From: http://docs.jboss.org/weld/reference/latest-master/en-US/html/environments.html#_tomcat
"Tomcat 7 and 8 are supported. Context activation/deactivation and dependency injection into Servlets and Filters works out of the box. Injection into Servlet listeners works on Tomcat 7.0.50 and newer."
So perhaps you can upgrade your Tomcat?
Now, all this is much easy to do with deltaspike servlet module
#ApplicationScoped
public class InitializationResourceBean {
#Inject
HealthCheck healthCheck;
public void onCreate(#Observes #Initialized ServletContext context) {
//Do initialisation stuff here.
if(HealthCheck != null) {
;
}
}
public void onDestroy(#Observes #Destroyed ServletContext context) {
System.out.println("Destroyed ServletContext: " + context.getServletContextName());
}
}
http://deltaspike.apache.org/documentation/servlet.html

Categories

Resources