How to bridge Spring Application Context events to an other context - java

I have a Spring web application with two contexts: one (applicationContext) built by ContextLoaderListener and a second (webContext) built by DispatcherServlet.
Within the applicationContext is a bean (org.springframework.security.authentication.DefaultAuthenticationEventPublisher) that fires spring context events.
But the receiver for the event is defined in the webContext. And that receiver did not get the event. (If put the receiver for test purpose in the applicationContext then it get the event, but I can not do this, because I need the webContexts for its functionality.)
So my question is, how to bridges the events from the applicationContext to webContext?

I had the same problem, solved mine by moving the beans creating the event to web-context. However you can solve your problem by manually wiring your event listener, something like this (this code is not compiled therefore it is untested):
#Component
public class BeanInWebContext implements ApplicationListener<SomeEvent> {
#Autowired
private ApplicationContext webContext;
#PostConstruct
public void registerAsListener() {
// get parent context
AbstractApplicationContext appContext = (AbstractApplicationContext) webContext.getParent();
// register self as a listener, this method is in AbstractApplicationContext
appContext.addApplicationListener(this);
}
#Override
public void onApplicationEvent(SomeEvent event) {
}
}

I think the actual answer is that you may want to configure your app differently (so that you only have one context)
I think in your web.xml you need to do something like this :
<servlet>
<servlet-name>example</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:/META-INF/applicationSpringConfig.xml
</param-value>
</init-param>
</servlet>
But to answer the deeper question. Someone else points out that you can use includes in your spring file (indeed in the above you can have more than one springconfig specified in your dispatcher servlet).
But when you include other context files you do not share instances of beans, only definitions.
Modularising Spring applications has been the only real downside of spring in comparison with EJB etc. That led spring into using OSGi.
And the answer to your underlying question of how to share spring context, officially you share spring bean instances between contexts using OSGi (spring dm)

Try moving the event publisher to the web context file, where it should have visibility over the whole application context. A similar issue occurs when configuring method security in the parent application context. The parent application context (loaded by ContextLoaderListener) isn't aware of the child (web) context.
You can also use a single application context for the entire application if you don't really need the parent-child relationship between the two. Often it just gets in the way and it is easier if all beans were defined in the same space.

As stated in documentation for the spring framework the simple ApplicationEvent mechanism is only designed to be used within the same application context, I am not aware that it is possible to propagate events to child contexts.
If you need a more advanced solution you might look into using a more enhanced solution like Java Message Service or Spring Integration.
http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#context-functionality-events

We can use the import tag to import/bridge the 2 different contexts created in a way the visibility of the events/beans are available and shared.
<import resource="applicationContext_name.xml"/>
In this import the context xml which is configured to be created from ContextLoaderListener in the context xml of the DispatcherServlet.

Related

Spring MVC: autowire ignored in servlets

For some reasons, I can autowire in my controllers but not in a servlet I created.
This is the top part of my servlet:
#Component
public class MyServlet extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {
#Autowired
private CobiService cobiService;
In my web.xml this is the related configuration:
<servlet>
<servlet-name>convservlet</servlet-name>
<servlet-class>com.gim.servlets.MyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>convservlet</servlet-name>
<url-pattern>/MyServlet</url-pattern>
</servlet-mapping>
And this is the way I tell spring to scan for components:
<context:component-scan base-package="com.gim" />
For some reason my autowired object cobiService is null. Did I forget anything? What should I change?
Servlets are not managed by Spring, they are managed by the Servlet container (like Tomcat). Therefore Spring cannot inject dependencies into Servlets in a normal Spring way. You can however do something like the following:
public class MyServlet extends javax.servlet.http.HttpServlet {
private CobiService cobiService;
#Override
public void init() throws ServletException {
super.init();
ApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
cobiService = applicationContext.getBean(CobiService.class);
}
}
You can override your servlet init method, and do
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext (this);
SpringBeanAutowiringSupport
There is no need to do
implements javax.servlet.Servlet, since you are extending HttpServlet
There are two servlets being created. One is being created by the servlet container/Java EE container when the application initializes and reads the configured <servlet> tag inside the web.xml file. The other would be created when the Spring IOC container performs its component scanning upon initialization.
In this case, the first instance is not able to participate in dependency injection because it was not created within the Spring IOC container. In order to participate in dependency injection, a bean must be managed by the Spring IOC container. When the servlet container/Java EE container instantiates the servlet it has no knowledge of Spring's IOC container.
Unfortunately, when a request comes in that satisfies the url-pattern specified in the web.xml file for the servlet, the request is redirected to the first instance created by the servlet container/Java EE container which is not a bean and not a candidate for autowiring.
If you were to remove the servlet from web.xml and add a #RequestMapping annotation to the servlet, the second instance (which is an actual bean able to utilize autowiring) would be utilized for requests fullfilling the specified url pattern in the #RequestMapping, however at that point you pretty much have a controller.
So in summary, my recommendation would be to adhere to Spring conventions and use a controller class. The controller class will match and exceed the functionality provided by the Servlet.

application context. What is this?

My colleagues very often use word "application context". In many articles this collocation used very often too.
My current understanding: application context is single xml file.
But I understand that if I was right, people wouldn't use "application context" instead of configuration xml file.
Can you help me to deal with this issue?
#feak gives a straight answer about the meaning of ApplicationContext in terms of Spring. In short, it is an object that loads the configuration (usually a XML file annotation based) and then Spring will start managing the beans and its benefits:
Beans declared in package
Beans declared by annotations
Constructor and method autowiring
Bean injection
Configuration, .properties and .yaml file loading
etc
To start an application context, you may use one of the following:
Manually load the application context at the beginning of your application. This is done for sample purposes or in standalone applications:
public class Foo {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("path/to/applicationContext.xml");
//use the context as you wish...
}
}
In case of Java web applications using Spring MVC, the DispatchServlet will load the application context for you, so you only have to create a springapp-servlet.xml file in WEB-INF folder of the application.
Note that an application context is associated to a single configuration (XML based or not). Period.
After understanding this, you could also understand that you can have more than a single application context per application. This is, having two or more ApplicationContexts in the same application. From the last example in the console application, this is easy to check:
public class Foo {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("path/to/applicationContext.xml");
ApplicationContext context2 =
new ClassPathXmlApplicationContext("path/to/applicationContext.xml");
//use the context as you wish...
}
}
Note that we have two application contexts using the same XML configuration. Can you do this? Yes, you're actually seeing it here. What's the difference, then? The main difference is that Spring beans singleton scopes are singleton per application context, this mean when retrieving a Bar bean that's configured in applicationContext.xml file from context will not be the same as retrieving it from context2, but several retrieves from context will return the same Bar bean instance.
Is this considered a good or bad practice? Neither, it will depend on the problem to be solved (in case of last example, I would say it is a bad practice). Most people would recommend having all your beans configured in a single place (via XML or another) and loaded by a single application context.
Let's understand this in simple words.
The ApplicationContext is the central interface within a Spring application that is used for providing configuration information to the application. It's created when the application starts running.
It provides the entire configuration needed by our application:
Bean Factory - Responsible for creation of java objects called beans. One example is components in the application.
Application listeners - all listeners needed for events.
WebServer information.
Application current environment specific information.
Resource pattern resolver - resource loader with path matcher.
Life cycle Processor.
Class Loader.
Start and shutdown monitor.
Servlet Context.
Reader and Scanner.
Logger
etc.
package com.srmhitter9062.spring;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class ApplicationContextUtils implements ApplicationContextAware {
private static ApplicationContext ctx;
#Override
public void setApplicationContext(ApplicationContext appContext)
throws BeansException {
ctx = appContext;
}
public static ApplicationContext getApplicationContext() {
return ctx;
}
}
We can get some idea about the Application object in below snapshot.
In summary, we can say the Application context is a Configuration object created for application to run.
The applicationContext.xml defines the beans for the "root webapp context". It's a web aware ApplicationContext.
It is used to have beans that are shared between all servlets in a web application.
I hope this is helpful.
I guess that you colleagues meant the loaded spring application context, which allows access to:
configuration of application,
initialized beans,
application events api,
etc
From the javadoc:
Central interface to provide configuration for an application. This is
read-only while the application is running, but may be reloaded if the
implementation supports this.
An ApplicationContext provides: [...]

How to mage java servlet managed (or DI) in Spring?

I have a project with a few old HTTPServlets, I would like a way to manage the creation/life-cycle of the Servlet in Spring so I can do a few things like DI and pass in database objects through Spring/Hibernate integration.
First I have setup the Spring application context in web.xml as follows;
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
Next I have my servlet definition in web.xml;
<servlet>
<servlet-name>oldHttpServlet</servlet-name>
<display-name>oldHttpServlet</display-name>
<servlet-class>com.package.MyServlet</servlet-class>
</servlet>
In my Spring application-context.xml I would like to make some bean def as follows;
<bean id="oldHttpServlet" class="com.package.MyServlet"></bean>
I think the above I need to implement some interface within my servlet, keep the bean definition as above in the Spring app-context.xml and then make a change to the servlet definition in the web.xml... I'm not sure what is the simplest change to make as there is some inheritance to worry about on the MyServelet.java side, which looks like this;
class MyServlet extends MyAbstractServlet{
void doStuff(){
//YOu know...
}
}
And MyAbstractServlet;
class MyAbstractServlet extends HttpServlet{
doPost(){
//Some post implementation here...
}
}
What is the best way to wrap MyServlet in Spring and have it loaded from there rather than being instantiated via web.xml?
I'm assuming the best way would be to use Springs HttpRequestHandler and then use the HttpRequestHandlerServlet in the web.xml in place of the current servlet. However I'm not sure how I go about implementing the HttpRequestHandler interface to work with the existing doPost() stuff in the myAbstractServlet...
If you need to inject dependencies into a servlet than I would go with Spring's HttpRequestHandlerServlet. You create an implementation of HttpRequestHandler (it has a method:
public void handleRequest(HttpServletRequest request, HttpServletResponse httpServletResponse) throws ServletException, IOException;
that you need to implement - it is the equivalent of what you'd have in a doGet / doPost.
The instance of all your HttpRequestHandler implementatins should be handled by spring (set up an annotation driven config and annotate it with, say, #Component) or use an XML config to set up those beans.
In your web.xml create the mapping for HttpRequestHandlerServlet. If you name the servlet the same as you did your HttpRequestHandler then Spring will make HttpRequestHandlerServlet automatically handle the request with the matching HttpRequestHandler.
Since HttpRequestHandler is a Spring bean, you can make Spring inject all the services you need into it.
If you still do not want to use HttpRequestHandler in lieu of Servlets than all that is left is some "programming judo", like retrieving WebApplicationContext with Spring utils and getting beans from there by a "per name" basis. Servlets are not instantiated by Spring so it cannot manage them. You would have to create a base class for your servlets and manage the DI as an initialization step yourself.
The code would look like this (inside a servlet):
WebApplicationContext webContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
CertainBeanClass bean = (CertainBeanClass) webContext.getBean("certainBean");
[EDIT]
Indeed, as one of the commenters suggested, there IS one more option. It still requires you to do a manual setup in your servlets init method though. Here is how this would look like:
public void init(ServletConfig config) {
super.init(config);
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
}
You may also try your luck with #Configurable annotation on Servlet with weaving, but this might or might not work (I think Servlet might get initialized before Spring context is set up, so it still won't have the possibility to do its magic).
The ideal use of Servlet should be as Controller and underlying container manages its life cycle so we don't need Spring to manage its instance
so I can do a few things like DI and pass in database objects through Spring/Hibernate integration.
You should never have dependency of Servlet in your dao/services, Your servlet should be calling services which is perfectly suitable to manage with Spring DI context
regarding SpringBeanAutowiringSupport, note from the javadoc:
NOTE: If there is an explicit way to access the ServletContext, prefer such a way over using this class. The WebApplicationContextUtils class allows for easy access to the Spring root web application context based on the ServletContext.

Spring enable a stand alone Java library

[New to Spring]
There is a service that is Spring enabled and does some stuff. When this service starts up, it loads the Spring Application context and everyone is happy.
Now, I need to create a library that will be used by the above mentioned service and I want to Springify this library package as well. But then, when/how does this library's application context get initialized? Am stumped!
I assume many people must have done this. What is the best practice?
I was thinking, may be a static block in the library's entry point interface is the right place to initialize the application context? (so, it gets init only once)
Is that the right approach? Is it even going to work or am missing I something? Appreciate your help.
You can give as many applicatoin context xml files as you want, If you are using the library in a web application,
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:context1.xml
classpath:context2.xml
...
</param-value>
</context-param>
basically you need to provide the relative path classpath:<relativepathofcontextfile>.
If it is inside a jar file and your jar is in classpath the above one works.
If it is for standalone, You can use ClassPathXmlApplicationContext.
public class SomeClass {
private static final ApplicationContext ac=
new ClassPathXmlApplicationContext("classpath:context1.xml");
public static void main(String[] args) {
MyIntf bean= (MyIntf) ac.getBean("myBean");
bean.myMethod();
}
}
No, with using static block you are enforcing Spring context to load disregard the fact that it might be loaded yet in another classloader. So the good approach would be to provide some factory method to get business objects, which will track if the context is loaded yet and create one if necessary. Or use pre-existing context.
Also it might be a good idea to review Service Locator pattern and http://docs.oracle.com/javase/1.4.2/docs/guide/jar/jar.html#Service%20Provider

Spring singleton created multiple times

I have a bean defined in my spring web application and I am expecting to have only one instance of this bean, here is my bean definition:
<bean id="accessControl" class="my.spring.app.AccessControl" />
In the constructor of AccessControl, I assign an identifier to the object, something like this:
public class AccessControl {
private long id = 0;
public AccessControl() {
id = System.currentTimeMillis();
}
public long getAccessControlId() {
return id;
}
}
In a different class, I try to get hold of the instance of AccessControl, something like this:
ApplicationContext ctx =
new ClassPathXmlApplicationContext("acbean.xml");
AccessControl ac = (AccessControl) ctx.getBean("accessControl");
LOGGER_.info("AccessControl Identifier : " + ac.getAccessControlId());
I am expecting the "id" value to be the same because the value of "id" is set in the constructor and constructor should not get called again and again but that is exactly what is happening. Infact, I added a log statement to the constructor and a new object is created everytime.
I have read: http://www.digizenstudio.com/blog/2006/09/14/a-spring-singleton-is-not-a-singleton/
but I don't think I am dealing with the same class defined twice with two different bean identifiers and the application context is the same.
Can anyone share what is wrong with the way I have defined the bean?
I have also experimented with singleton="true" and scope="singleton" but they do not make any differece.
Thanks.
the singelton-ness in spring is per application context, every time you create a new instance of the application context (like the first line in your second code sample) all the singletons are instantiated.
You need to have a single application context and reuse it around in your application
You are creating a new application context with each call of:
ApplicationContext ctx = new ClassPathXmlApplicationContext("acbean.xml");
So, you end up with a new spring container, which means that your beans are all re-created by the new container.
Also, you mentioned that you had this in a web application. If so, you need to allow the web application to load the spring context and to obtain and use that context as necessary.
Add to web.xml:
<context-param>
<description>Core Spring context.</description>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/classes/applicationContext.xml</param-value>
</context-param>
<listener>
<description>Spring loader.</description>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
or something similar. Obtain the web context via a servletcontext as needed.
One other note: one point of spring is to provide inversion of control, commonly with dependency injection. You should consider allowing spring to inject any dependencies for you rather than obtaining the context and pulling beans yourself.
In a Spring app you should not be explicitly creating an application context of your own.
Ideally the singleton should be injected into your class, or you should be implementing ApplicationContextAware (docs, and some notes). I prefer injection; easier.

Categories

Resources