EnableLoadTimeWeaving did not weave other config in web application context - spring

I try to use spring weblogic LTW in my project to do some aop stuff. my project is a simple webapp servlet2.5 use spring mvc 3.2.6, running on weblogic 10.0.
I have following app level configuration setup in web.xml
#Configuration
#EnableLoadTimeWeaving
public class AppConfig {
}
#Configuration
#EnableTransactionManagement
#ComponentScan(basePackages = { "com.blabla.model" })
public class CoreConfig {
}
I also have a mvc level configuration setup in my web.xml
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = { "com.blabla.controller" })
public class MVCConfig extends WebMvcConfigurerAdapter {
}
here is my simplified web.xml
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>AppConfig,CoreConfig
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>MVCConfig
</param-value>
</init-param>
<init-param>
<param-name>wl-dispatch-policy</param-name>
<param-value>RestWorkManager</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
so what happens is, MVCConfig and its scanned components are all woven by LTW and works great. but CoreConfig and its scanned components (all the DAO) are not picked up by LTW.
I guess that the CoreConfig and AppConfig is in the same level, so when AppConfig and CoreConfig are loaded, the LTW is not triggered yet.
And I tried to put the CoreConfig in the same level as MVCConfig, it got picked up by LTW.
but CoreConfig is supposed to be application level, not dispatchservlet level. Since many spring web MVC applications use a root context and a child for the DispatcherServlet.
so My question is if I put CoreConfig in the app level, how to make LTW pick it up? Thanks.

Loadtimeweaving will only work for classes that aren't already loaded by the class loader.
Now when using XML configuration the actual bean classes are loaded after the load time weaving is enabled so it works, more or less, flawlessly for all classes.
With Java Config classes are loaded as soon as the #Configuration annotationed class is loaded. All classes that are imported are loaded into the class loader. After this load time weaving is enabled, however only for classes that are going to be loaded after this point.
Hence the fact that it is working for classes loaded by the configuration as specified for the DispatcherServlet and hence the problem in the ContextLoaderListener.
One thing you can try is to put a #ComponentScan for #Configuration classes on the AppConfig. And let the ContextLoaderListener only load the AppConfig. That might defer the class loading a little until after the load time weaving is enabled.
Something that definitely will work is putting both configuration classes in XML, remove the #EnableLoadTimeWeaving for the AppConfig and use a <context:load-time-weaving />.

Related

Role of ContextLoaderListener

Besides loading optional root applicationContext for a web application what is the role of ContextLoaderListener?
In Spring docs API it says
Bootstrap listener to start up and shut down Spring's root WebApplicationContext.
From some other discussions I found that ServletContextListener creates a WebApplicationContext and WebApplicationContext provides access to the ServletContext via ServletContextAware beans and the getServletContext method.Otherwise it would need to be created manually.
But ContextLoaderListener is not mandatory. So if don't use ContextLoaderListener do we need to create WebApplicationContext manually?
The WebApplicationContext is bound in the ServletContext and is defined in your web.xml like:
<servlet>
<servlet-name>myservlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/my-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
If you dont configure it with a custom config file like previous (my-context.xml) and you omit this entry in your web.xml, Spring DispatcherServlet search and loads its configuration file using <servlet_name>-servlet.xml. In the my-context.xml like above (or in a <servlet_name>-servlet.xml) there could be defined Web Components as:
Controllers
ViewResolvers
LocaleResolvers
ThemeResolvers
If you want to have access to middle tier components (from Multi Web Components) like
DAO
Entity
Service
you need a parent Context. Therefore you could define in your web.xml:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/config/application-context-service.xml
/WEB-INF/config/application-context-dao.xml
</param-value>
</context-param>
ContextLoaderListener creates a root web-application-context for the web-application and puts it in the ServletContext of the root Application. DispatcherServlet creates its own WebApplicationContext and the handlers, controllers, view-resolvers etc. are managed by this WebApplicationContext.

Spring Data REST with Spring MVC: Adding RepositoryRestMvcConfiguration to existing DispatcherServlet

I have an existing Spring MVC Application with a DispatcherServlet and an XML based configuration.
Now I would like to integrate Spring Data REST but I dont know how to do this in a clean way. I added
<context:component-scan>...</context:component-scan>
so my RestControllers are found but I fail in adding a RepositoryRestMvcConfiguration config. I tried the annotation driven approach which doesnt work
#Configuration
public class RestConfiguration extends RepositoryRestMvcConfiguration {
...
}
and the
<bean class="com.mypackage.rest.RestConfiguration" />
approach is not working either.
I also tried the follwing in the web.xml
<servlet>
<servlet-name>myservlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.mypackage.rest.RestConfiguration</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
Strange thing is, a method annotated with #PostConstruct is called, but non of the configure* methods.
In the docs for Spring Data REST is a chapter where it is explained how to add a Spring Data REST to a Spring MVC application in code. It also says
The equivalent of the above in a standard web.xml will also work identically to this configuration if you are still in a servlet 2.5 environment.
How do you do this?
Fortunately, in Section 11.2 it is explained. Would have been nice to have a reference in Section 2.5 that points to Section 11.2 :-/
In Java, this would look like:
import org.springframework.context.annotation.Import;
import org.springframework.data.rest.webmvc.RepositoryRestMvcConfiguration;
#Configuration
#Import(RepositoryRestMvConfiguration.class)
public class MyApplicationConfiguration {
…
}
In XML this would look like:
<bean class="org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration"/>

Understanding spring dispatcher servlet initialization

Here is how spring documentation recomends to initialize dispatcherServlet:
<web-app>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/root-context.xml</param-value>
</context-param>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
My question is about providing an empty param-value inside the init-param tag. Despite defining that param as context-param we still provide the empty value. Therefore contextConfigLocation should be null when passes to servlet's init() method. What's wrong, correct me please.
In Spring Web Applications, there are two types of container, each of which is configured and initialized differently.
Application Context
Web Application Context
Application context is inialised by config file's that you specified in as context-params and picked up by ContextLoaderListener. This is purely i would consider as business logic related beans.
Web application context is child of application context which may or may not be present. Each DispatcherServlet will have associated WebApplicationContext and which takes spring beans from your init-params to create context.
Whatever beans are available in the ApplicationContext can be referred to from each WebApplicationContext.
Reason why we have two different bean configurations is to keep a clear separation between middle-tier services such as business logic components and data access classes (that are typically defined in the ApplicationContext) and web- related components such as controllers and view resolvers (that are defined in the WebApplicationContext per Dispatcher Servlet).

How to Setup web application context in Spring MVC test

We have a clear abstraction between Service layers & view layers context configurations and we are loading them as shown below.
Root application context:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:META-INF/spring/applicationContext*.xml</param-value>
</context-param>
Web application context:
<servlet>
<servlet-name>lovemytasks</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/mmapp-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
Now we are trying to introduce SPRING MVC TEST FRAMEWORK to test our application.
For this i would need to setup the same environment as my real web application works.
How can i do that ?
I tried below configuration on my test to load both the contexts.
#ContextConfiguration(locations = { "classpath*:META-INF/spring/applicationContext*.xml",
"file:src/main/webapp/WEB-INF/spring/mmapp-servlet.xml" })
But its erroring out saying
Caused by: org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Duplicate <global-method-security> detected.
We have defined global security in both root application context and web application context.
Note: The above said issue will not appear when i run my web application. It happens only when i run Spring MVc test
I tried removing my global security and one place and then landing into errors with conversion services on running my tests. Which warned me that I am not loading the context as teh real Spring application does.
Now, i would like to setup my Spring MVC test environment to use or work as the same way my spring web application environment works. Can any one please suggest how can i achieve it ?
Use the #ContextHierarchy annotation. Its javadoc describes it well. In your case you would use
#WebAppConfiguration
#ContextHierarchy({
#ContextConfiguration(locations = { "classpath*:/META-INF/spring/applicationContext-*.xml" }),
#ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring/mmapp-servlet.xml" })
})
don't put your appContext in meta-inf.
The "normal" way is to have one spring-servlet.xml in your web-inf
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/spring-servlet.xml</param-value>
</context-param>
Andn then import different files within the xml file :
<import resource="classpath:beans.xml"/>
I create a seprate appContent for my tests :
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations ="classpath:applicationContext-test.xml")
#Transactional
public class MyTest {
Your beans must be getting loaded twice somewhere along the line, are you importing the beans twice, defining them both in xml and also annotating ?

How to connect HttpServlet with Spring Application Context in web.xml?

I'm trying to connect my FooServlet which extends HttpServlet with the ApplicationContext which is in the same Project.
The Application Context is already used by a Wicket Servlet
It works with
servletContext = this.getServletContext();
wac = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
(IMyBean)wac().getBean("myServiceBean")
Now I try to aviod to use explicitly Spring Classes in my Code (WebApplicationContextUtils) as it's not the IoC way.
The Wicket Servlet is connected with the Application context in the web.xml
<servlet>
<servlet-name>ExampleApplication</servlet-name>
<servlet-class>org.apache.wicket.protocol.http.WicketServlet</servlet-class>
<init-param>
<param-name>applicationFactoryClassName</param-name>
<param-value>org.apache.wicket.spring.SpringWebApplicationFactory</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
I found the class Spring HttpServletBean but I don't know if it serves for my Case
I found a way to inject Beans in my HttpServlet (Note: I don't need a Presentation View, otherwise there are more advanced Spring Classes)
Add a ContextLoaderListener to web.xml so that Spring's root WebApplicationContext is loaded
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
Configure Servlet using Springs HttpRequestHandlerServlet Class
<servlet>
<servlet-name>FooServlet</servlet-name>
<display-name>Foo Servlet</display-name>
<servlet-class>
org.springframework.web.context.support.HttpRequestHandlerServlet
</servlet-class>
</servlet>
Let your Servlet implement the org.springframework.web.HttpRequestHandler Interface
Define your Servlet as a Bean in ApplicationContext (beanID must be same as "servlet-name").
Now it's possible to inject all necassary Beans in the Spring DependencyInjection way without dependency lookup.
I think you should use Spring utilities like
RequestContextUtils.getWebApplicationContext(request, application);
to hookup the Spring Context within your Servlet.
Agreed this is no DI/IoC, but the servlet is no bean as well !

Resources