Two separate Spring contexts for one webapp - java

I want to use two different Spring web contexts, each have own contextConfig, spring servlet and filter, that should be mapped to different urls. I have a
Standard Grails project, mapped to '/'
And an existing Spring webapp, that I want to map to /extra/
I know that I can deploy both into one Tomcat, but I'm looking for a way of making one app (one war, etc), because It can simplify our deployment and development process.
This applications don't need to share beans or anything, should be completely separate. Both have DispatcherServlet and DispatcherFilter (and both are using Spring Security, but different configuration)
How I can configure web.xml for such webapp?
I've tried to add new filter:
<filter>
<filter-name>extraSpringSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>contextAttribute</param-name>
<param-value>org.springframework.web.servlet.FrameworkServlet.CONTEXT.extraSpring</param-value>
</init-param>
<init-param>
<param-name>targetBeanName</param-name>
<param-value>extraSecurityFilterBean</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>extraSpringSecurityFilterChain</filter-name>
<url-pattern>/extra/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
and spring dispatcher servlet:
<servlet>
<servlet-name>extraSpring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<init-param>
<param-name>springConfigLocation</param-name>
<param-value>classpath:extra-spring-web.xml</param-value>
</init-param>
</servlet>
Where:
two context xml in classpath (inside exra library jar):
extra-spring-web.xml
extra-spring-security.xml (!!! how I should configure it?)
extra-spring-security.xml
is pretty standard Spring Security config
have configured bean extraSecurityFilterBean
have dependecy to beans from -web context (but it's not required to be)
It's semi-working now:
as I see from logs, extraSpring servlet successfully load beans from extra-spring-web.xml
but after accessing url /extra/ I got NoSuchBeanDefinitionException: No bean named 'extraSecurityFilterBean' is defined.
So, the question, how I can define context for DelegatingFilterProxy? I even tried to add this files into main context (contextConfigLocation param), it's not what i'm looking for, but it didn't work.
I've taken a look into DelegatingFilterProxy sources, but it's not clear for me how it loads the context.

As per my comment on the question, if the security filter chain is defined in extra-spring-security.xml then you need to ensure that that file is loaded by your extra DispatcherServlet in addition to extra-spring-web.xml either by <import>ing the -security file from the -web one or configuring it as:
<servlet>
<servlet-name>extraSpring</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:extra-spring-web.xml
classpath:extra-spring-security.xml
</param-value>
</init-param>
</servlet>
You will also need to ensure that the security filter in the Grails application doesn't apply to /extra URIs, exactly how you do this depends on whether you're using annotations, database RequestMap entries etc.

If the modules are completely separate: the easiest way is to package them as two different webapp. Tens of different spring-based apps can run in one appserver -even on a modest developer machine- without issues.

A few questions
What does your Spring Security configuration look like?
I'm confused why the error states "No bean named 'apiservSecurityFilterChain' is defined" but the web.xml you have posted only references extraSpringSecurityFilterChain (the bean names should match or some important configuration is being left out).
Possible Answer
I'm guessing the problem is that the filter-name needs to match Spring Security's bean name (cannot know for sure without seeing the Spring Security configuration you are using). The default value used by the Spring Security namespace is springSecurityFilterChain, so try the following in the web.xml instead (notice extraSpringSecurityFilterChain changed to springSecurityFilterChain):
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>contextAttribute</param-name>
<param-value>org.springframework.web.servlet.FrameworkServlet.CONTEXT.extraSpring</param-value>
</init-param>
<init-param>
<param-name>targetBeanName</param-name>
<param-value>extraSecurityFilterBean</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/extra/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>

Related

Configuring provider packages in web.xml with Jersey

I am working on a web service and I get very strange error.
This is the line from my web.xml:
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>service</param-value>
</init-param>
As I know, <param-value> has to be referred in to the package my main application is. However, my application is in rest.main package, but the web service works only with the service value as defined above.
What is the problem, can somebody explain me these lines?
Have a look at the documentation regarding the jersey.config.server.provider.packages configuration property:
Defines one or more packages that contain application-specific resources and providers. If the property is set, the specified packages will be scanned for JAX-RS root resources and providers.
Servlet 2.x containers
This setting is frequently used in the web.xml deployment descriptor to instruct Jersey to scan these packages and register any found resources and providers automatically:
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>
org.foo.myresources,org.bar.otherresources
</param-value>
</init-param>
With this setting, Jersey will automatically discover the resources and providers in the selected packages. By default, Jersey will recursively scan the sub-packages as well.
Servlet 3.x containers
For Servlet 3.x containers, no web.xml is necessary at all. Instead, an #ApplicationPath annotation can be used to annotate a custom Application or ResourceConfig subclass and define the base application URI for all JAX-RS resources configured in the application.
Use the following to defined the packages that will be scanned:
#ApplicationPath("resources")
public class MyApplication extends ResourceConfig {
public MyApplication() {
packages("org.foo.myresources,org.bar.otherresources");
}
}
For more details, check the deployment section of the Jersey documentation.
Important
Always use the qualified name of the package;
Use , or ; as delimiter when declaring multiple packages.
You just need to add the package name to the
Form an Example if I have placed my resource class in com.ft.resources package
then I have to add the package name in
<init-param>
<!-- For Jersey 2.x -->
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>com.ft.resources</param-value>
</init-param>
Hope this might resolve your issue
If the application consists only resources and providers stored in particular packages, Jersey can scan them and register automatically.
<web-app>
<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>org.foo.rest;org.bar.rest</param-value>
</init-param>
...
</servlet>
...
</web-app>
The param-value refers to the packages which will be scanned automatically.

Spring cloud Zuul with servlet-api 2.5

#EnableZuulProxy doesn't work under a servlet 2.5 container. Is there any workaround to get spring-cloud zuul work under a servet 2.5 container?
Also I could not find the annotation processor of #EnableZuulProxy. Please provide the class which propesses #EnableZuulProxy so that I can better understand what this annotation really does.
Spring Cloud is meant to be run on servlet 3.0. That being said, it is possible to get #EnableZuulProxy running on servlet 2.5. I had to figure out a hack for this as I had to get this working in Tomcat 6.
The main issue is due to the ZuulConfiguration.class, which has the method:
#Bean
#ConditionalOnMissingBean(name = "zuulServlet")
public ServletRegistrationBean zuulServlet() { ... }
The issue here is that ServletRegistrationBean uses javax.servlet.Registration$Dynamic, which is not available until Servlet 3.0. This results in a NoClassDefFoundError.
To work around this, use the spring-boot-legacy project to first register a DispatcherServlet. Secondly, you'll have to manually create a zuul servlet.
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.dm.gateway.microservicegateway.Application</param-value>
</context-param>
<listener>
<listener-class>org.springframework.boot.legacy.context.web.SpringBootContextLoaderListener</listener-class>
</listener>
<filter>
<filter-name>ContextLifecycleFilter</filter-name>
<filter-class>com.netflix.zuul.context.ContextLifecycleFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ContextLifecycleFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextAttribute</param-name>
<param-value>org.springframework.web.context.WebApplicationContext.ROOT</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>zuul</servlet-name>
<servlet-class>com.netflix.zuul.http.ZuulServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
I found the best way to remove the auto servlet registration, is to just make a verbatim copy of ZuulConfig called ZuulOverrideConfig, and remove the zuulServlet() method. This is because the ZuulProxyConfiguration extends ZuulConfiguration, and it seemed to create the bean even when i tried to override it. I'm not 100% on the mechanics behind this, so there may be a better way.
The second change I made in ZuulOverrideConfig was to call an extended implementation of ZuulFilterInitializer, called 'LegacyZuulFilterInitializer`. This is because for some reason, the Zuul servlet was being crated, and able to be invoked, but no filters were bootstrapped. This extension is a hacky way to get the filters to bootstrap.
Next, I created a copy of ZuulProxyConfiguration called ZuulLegacyProxyConfiguraiton, and had it extend ZuulOverrideConfig.class.
Finally, I annotated the Application class as follows.
#EnableCircuitBreaker
#EnableDiscoveryClient
#Import(ZuulLegacyProxyConfiguration.class)
#SpringBootApplication
public class Application {....}
After all these hacks, the implementation finally worked as expected. I wouldn't suggest using this for long, as it's pretty hacky. You won't get configuration class updates automatically when moving to new versions, and I can't guarantee that something won't break randomly!
This is using Spring Cloud 1.1.4.RELEASE
Gist of all the code.
#EnableZuulProxy is from Spring Cloud which is based on Spring Boot which is Servlet 3.0 and above. If you need to use Servlet 2.5 you can use the Netflix APIs directly.
#EnableZuulProxy is meta-annotated with #Import(ZuulProxyConfiguration.class) so I guess that's what you mean when you say "propesses"? If you don't know what an #Import is, go and read up on Spring.

Spring MVC: Using #Autowire is getting references to different spring bean instances

I have a UserCredetnialsDatSourceAdapter defined in my Spring app context file. I also have a custom filter added to the Spring via the DelegatingFilterProxy.
This filter is using the #Autowire to get a reference to the DataSource Bean. I also #Autowire the DataSource in my DAO. When I debug I see different instance id's to the datasource in the Filter and DAO instances. Why are there 2 instances whenthese are singletons by default?
I also fired up jvisualvm and I looked at the heap and all my beans in my app context have 2 instances? Thanks for any insight maybe the bean pre/post processing has something do with it or maybe I should not be using #Autowire in a Filter. Any help is apprciated. Thanks!
EDIT
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/config-context.xml</param-value>
</context-param>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/web-context.xml,/WEB-INF/config-context.xml</param-value>
</init-param>
Interesting I think I see what it going on. There are 2 instances because there are 2 contexts. One for the app and 1 for each request (Thread) I assume? Is there a way to specfiy which context to use? Maybe filter is not the answer and I will need to use AOP. Need to research how to inject a bean in #Aspect, if that is even possible.
Thanks!!
-Joe
You are importing your /WEB-INF/config-context.xml as part of your Root Application Context(one loaded up by ContextLoaderListener) as well as your Web Context(loaded by DispatcherServlet). You can probably remove it from the one for DispatcherServlet.

Why is my Spring ContextRefreshed event called twice?

I have a Spring ApplicationListener bean registered to listen for ContextRefreshed events. For some odd reason though, I get two calls to the onApplicationEvent(ContextRefreshedEvent) method at the completion of the context initialization. Is this normal behavior or is it indicative of a problem with my configuration? I'm using Jetty 8 for my Servlet container.
My relevant web.xml configuration is as follows
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/config/spring/spring-config.xml</param-value>
</context-param>
<servlet>
<servlet-name>Spring</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>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet-mapping>
<servlet-name>Spring</servlet-name>
<url-pattern>/service/*</url-pattern>
</servlet-mapping>
Thanks!
Even though you did not specify a contextConfigLocation for your DispatcherServlet it still creates a child context and the second refreshed event is for that context. Use event.getApplicationContext() to find out which context the event is for.
it happened to me as well, on a different event-listener. (ApplicationListener<AuthenticationFailureBadCredentialsEvent>)
I suspected the ContextLoaderListener, and when I removed the declaration from the web.xml, the app was working properly. Then I had to figure out what is its purpose, of the ContextLoaderListener...
Role/Purpose of ContextLoaderListener in Spring?
the interesting answer there is:
ContextLoaderListener is optional. Just to make a point here: you can
boot up a Spring application without ever configuring
ContextLoaderListener ...just the basic minimum web.xml with
DispatcherServlet
It looks like bug.
https://jira.springsource.org/browse/SPR-6589
If you are using 3.0 try it on the latest available release which is 3.05.
I had this problem too but fixed it. I was injecting the dataSource into my DAO (and instantiating a JdbcTemplate with it)....but I also had a Spring bean configured for JDBCTemplate.
I should have been injecting my DAO with the jdbcTemplate...that avoids the duplicate.

doubt on struts and filter

Assume it is a struts project..
the filter configuration is as follows,
<filter>
<filter-name>samplefilter</filter-name>
<filter-class>org.samplepack.SampleFilterXXX</filter-class>
</filter>
<filter-mapping>
<filter-name>samplefilter</filter-name>
<servlet-name>action</servlet-name>
</filter-mapping>
and the servlet mapping is as follows,
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
I want to know whether the samplefilter will be executed for every action class in the struts project?
can you post the situations whereever i can apply this effectively ?
The simple answer is Yes, provided that all your struts actions are accessed via the pattern *.do. This is the default configuration for Struts, so I expect that this is the case.
However as the Struts mapping us configurable, it is possible for you to define a different mapping to access a Struts action, and therefore your filter will not be picked up. But, as mentioned above, this is uncommon practice, so I expect you will be okay.

Categories