Loading spring dm in OSGi container - spring-mvc

I am trying to load spring dm in my OSGi bundle. I have followed a tutorial. My aim is, for a particular URL, the URL should be processed by spring instead of default sling servlet. I am partly successful in achieving the same.
Sometimes, It doesn't work. My globaldispatcher servlet is not being initialized. The issue is intermittent. Some times my spring servlet gets initialized just fine. But some times I get this error :
[SpringOsgiExtenderThread-2] net.jasonday.examples.sling.spring.mvc.sling.SlingDispatcherServlet FrameworkServlet 'globaldispatcher': initialization started
[SpringOsgiExtenderThread-2] net.jasonday.examples.sling.spring.mvc.sling.SlingDispatcherServlet Context initialization failed
java.lang.IllegalArgumentException: bundle context should be set before refreshing the application context
at org.springframework.util.Assert.notNull(Assert.java:112)
at org.springframework.osgi.context.support.AbstractDelegatedExecutionApplicationContext.normalRefresh(AbstractDelegatedExecutionApplicationContext.java:179)
at org.springframework.osgi.context.support.AbstractDelegatedExecutionApplicationContext$NoDependenciesWaitRefreshExecutor.refresh(AbstractDelegatedExecutionApplicationContext.java:89)
at org.springframework.osgi.context.support.AbstractDelegatedExecutionApplicationContext.refresh(AbstractDelegatedExecutionApplicationContext.java:175)
at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:467)
at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:483)
at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:358)
at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:325)
at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:127)
at javax.servlet.GenericServlet.init(GenericServlet.java:158)
at org.apache.sling.servlets.resolver.internal.SlingServletResolver.createServlet(SlingServletResolver.java:988)
at org.apache.sling.servlets.resolver.internal.SlingServletResolver.bindServlet(SlingServletResolver.java:936)
at sun.reflect.GeneratedMethodAccessor36.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
Java version : 1.7.0_45
I am using following dependencies as mentioned in tutorial: (All are OSGi bundles)
Spring AOP v3.0.6.RELEASE
Spring ASM v3.0.6.RELEASE
Spring Aspects v3.0.6.RELEASE
Spring Beans v3.0.6.RELEASE
Spring Context v3.0.6.RELEASE
Spring Context Support v3.0.6.RELEASE
Spring Core v3.0.6.RELEASE
Spring Expression v3.0.6.RELEASE
Spring Web v3.0.6.RELEASE
Spring Web Servlet v3.0.6.RELEASE
Spring OSGi IO v1.2.1
Spring OSGi Core v1.2.1
Spring OSGi Extender v1.2.1
Spring OSGi Annotation v1.2.1
Spring OSGi Web v1.2.1
AOP Alliance v1.0.0
CGLib 2.2.0
Commons Lang v2.6
Commons Codec v1.5
Commons Logging v1.1.1
ASM v3.2.0
JSR 330 (javax.inject) v1.0.0
JSR 250 (javax.annotation) v1.0.0
Following is the code from my spring-osgi.xml which is present in META-INF/spring/ folder
<osgi:service ref="globalSlingDispatcherServlet">
<osgi:interfaces>
<value>javax.servlet.Servlet</value>
</osgi:interfaces>
<osgi:service-properties>
<entry key="sling.servlet.resourceTypes" value="examples/sling/spring/mvc/dispatcher/global" />
<entry key="sling.servlet.extensions">
<array>
<value>json</value>
<value>html</value>
</array>
</entry>
<entry key="sling.servlet.methods">
<array>
<value>GET</value>
<value>POST</value>
</array>
</entry>
<entry key="contextClass" value="org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext"/>
<entry key="sling.core.servletName" value="globaldispatcher" />
</osgi:service-properties>
Below is the code from META-INF\spring\spring.xml
<context:annotation-config />
<context:component-scan base-package="net.jasonday.examples.sling.spring.mvc.sling" />
<aop:aspectj-autoproxy />
<bean class="org.springframework.osgi.extensions.annotation.ServiceReferenceInjectionBeanPostProcessor" />
Following is the code from WEB-INF/globaldispatcher
<context:annotation-config />
<context:component-scan base-package="net.jasonday.examples.sling.spring.mvc">
<context:exclude-filter type="regex"
expression="net\.jasonday\.examples\.sling\.spring\.mvc\.sling\..*" />
</context:component-scan>
<aop:aspectj-autoproxy />
<bean class="org.springframework.osgi.extensions.annotation.ServiceReferenceInjectionBeanPostProcessor" />
SlingConfiguration.java :
#Configuration
public class SlingConfiguration {
#Bean
#DependsOn("slingContextLoader")
public SlingDispatcherServlet globalSlingDispatcherServlet() {
return new SlingDispatcherServlet();
}
}
SlingContextLoader.java :
#Component
public class SlingContextLoader extends ContextLoader {
private ServletContext servletContext;
private ApplicationContext applicationContext;
public ApplicationContext getApplicationContext() {
return applicationContext;
}
#Inject
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public ServletContext getServletContext() {
return servletContext;
}
#ServiceReference
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
#Override
protected Class<?> determineContextClass(
ServletContext currentServletContext) {
return OsgiBundleXmlWebApplicationContext.class;
}
#Override
protected ApplicationContext loadParentContext(
ServletContext currentServletContext) {
return applicationContext;
}
#PostConstruct
public void init() {
initWebApplicationContext(servletContext);
}
#PreDestroy
public void destroy() {
closeWebApplicationContext(servletContext);
}
}
My SlingDispatcherServlet.java
public class SlingDispatcherServlet extends DispatcherServlet implements Servlet {
#PreDestroy
#Override
public void destroy() {
super.destroy();
String attrName = getServletContextAttributeName();
getServletContext().removeAttribute(attrName);
}
}

This issue is being caused because the Spring annotations like #PostConstruct are not working as expected some times because of OSGi class loading issues.
Hence a workaround would be to not to rely on annotations. The below code solved the issue for me.
I am calling init() method manually in setServletContext() in SlingContextLoader
#ServiceReference
public void setServletContext(ServletContext servletContext) {
log.info("in setServletContext");
this.servletContext = servletContext;
init();
}
in init() method I am adding following code :
//#PostConstruct
public void init() {
log.info("in init");
//Remove existing application context if any
if (servletContext
.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
log.info("removing existing application context to create new one");
closeWebApplicationContext(servletContext);
}
initWebApplicationContext(servletContext);
}

Related

SPRING MVC - Error with autowired beans

I have this maven project with its modules
Parent
|_____Model
|_____Persistence
|_____Service
|_ service-context.xml
|_____View
|_ spring/app-config.xml
I have this controller
controllers.HomeController in the View module
#Controller
public class HomeController {
protected final Log logger = LogFactory.getLog(getClass());
private IServicioService servicioService;
#RequestMapping(value="/home.htm")
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
logger.info("Returning hello view");
return new ModelAndView("home.jsp");
}
#Autowired
public void setServicioService(IServicioService servicioService) {
this.servicioService = servicioService;
}
And my service servicios.ServicioService in the services module
#Service
public class ServicioService implements IServicioService{
private ServicioDao servicioDao;
public ServicioService(){}
/*************************** Gett&Sett ****************************/
public ServicioDao getServicioDao() {
return servicioDao;
}
public void setServicioDao(ServicioDao servicioDao) {
this.servicioDao = servicioDao;
}
}
The IServicioService hasnt a #Service
The ServicioService bean is defined in service-context.xml like this
<bean id="servicioService" class="servicios.ServicioService">
<property name="servicioDao" ref="servicioDao" />
</bean>
**And my app-config.xml is importing the service-context.xml **
<import resource="classpath*:service-context.xml" />
<context:component-scan base-package="controllers" />
Idk why is giving me this
No matching bean of type [servicios.ServicioService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
Help!!
Your problem probably lies inside of xml configuration.
Try adding in your app-config.xml line:
<context:component-scan base-package="servicios" />
After that you should do the same with your daos

Spring data redis error in Spring mvc framework

I'm using to redis in spring mvc based web application.
so I used spring-data-redis & jedis.
The library version is the following.
Spring MVC 4.1.6
jedis 3.0
Spring data redis 1.6 SNAPSHOT
Redis Server 3.0.2 (installed on Cent OS 7)
Application configuration is following:
-spring configuration xml(applicationContext.xml)
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxWaitMillis" value="3000"/>
<property name="maxTotal" value="50"/>
<property name="testOnBorrow" value="true"/>
</bean>
<bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
p:database="16" p:usePool="true"
p:poolConfig-ref="jedisPoolConfig" p:host-name="${redis.host}" p:port="${redis.port}" p:password="${redis.pass}"/>
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" p:connectionFactory-ref="connectionFactory" />
-RedisServices.java
public interface RedisServices {
List getAppInfos();
void add_appinfo(List data);
}
-RedisServicesImpl.java
#Service
public class RedisServicesImpl implements RedisServices{
#Autowired
private RedisTemplate<String, Object> redisTemplate;
#Resource(name="redisTemplate")
private ValueOperations<String, Object> valueOps;
#Override
public List getAppInfos() {
return (List)valueOps.get(Constants.APP_INFO_KEY);
}
#Override
public void add_appinfo(List data) {
valueOps.set(Constants.APP_INFO_KEY, data);
}
}
- AppController.java
#RestController
#RequestMapping(value = "/appinfo")
public class AppsController {
#Autowired
private RedisServices redis;
#RequestMapping(value = "/mobiles",method = RequestMethod.GET)
public List view_apps_register(){
List data=new ArrayList();
List lst=services.view_app_register(apps);
for (int i = 0; i < lst.size(); i++) {
Map ap = (Map) lst.get(i);
String server=(String) ap.get("server");
if(server.equals(apps)){
data.add(ap);
}
}
redis.add_appinfo(data);
return null;
}
}
The following errors raised
java.lang.IllegalAccessError: tried to access method redis.clients.util.Pool.returnResource(Ljava/lang/Object;)V from class org.springframework.data.redis.connection.jedis.JedisConnection
at org.springframework.data.redis.connection.jedis.JedisConnection.close(JedisConnection.java:251)
at org.springframework.data.redis.connection.jedis.JedisConnection.<init>(JedisConnection.java:184)
at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.getConnection(JedisConnectionFactory.java:252)
at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.getConnection(JedisConnectionFactory.java:58)
at org.springframework.data.redis.core.RedisConnectionUtils.doGetConnection(RedisConnectionUtils.java:128)
at org.springframework.data.redis.core.RedisConnectionUtils.getConnection(RedisConnectionUtils.java:91)
at org.springframework.data.redis.core.RedisConnectionUtils.getConnection(RedisConnectionUtils.java:78)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:178)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:153)
at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:86)
at org.springframework.data.redis.core.DefaultValueOperations.set(DefaultValueOperations.java:169)
at com.wwzz.services.redis.RedisServicesImpl.add_appinfo(RedisServicesImpl.java:63)
Help me!!!!

Spring MVC and WebSockets

I try to add a WebSocket end point to my Spring 4 web app.
I follow this tutorial.
I created and Handler and an Interceptor and I registered them adding this configuration in the file mvc-dispatcher-servlet.xml that is loaded by the
DispatcherServlet .
<bean id="websocket" class="co.syntx.example.websocket.handler.WebsocketEndPoint"/>
<websocket:handlers>
<websocket:mapping path="/websocket" handler="websocket"/>
<websocket:handshake-interceptors>
<bean class="co.syntx.example.websocket.HandshakeInterceptor"/>
</websocket:handshake-interceptors>
</websocket:handlers>
My poblem is that the handler is not registered and I get a 404 error when I try to connect to
ws:localhost:8080/<app-context>/websocket
I also try to user the 'xml-less' configuration (below), but I tried to add a breakpoint in the registerWebSocketHandlers but the method is never invoked.
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
#Configuration
#EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myHandler(), "/myHandler");
}
#Bean
public WebSocketHandler myHandler() {
return new MyHandler();
}
}

Spring Java Config and Thymeleaf - Dandelion Datatables config

Attempting to get pagination on datatables using Thymeleaf and Dandelion. According to the docs I need to update a few things:
web.xml (javaconfig attempt further down)
<!-- Dandelion filter definition and mapping -->
<filter>
<filter-name>dandelionFilter</filter-name>
<filter-class>com.github.dandelion.core.web.DandelionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>dandelionFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Dandelion servlet definition and mapping -->
<servlet>
<servlet-name>dandelionServlet</servlet-name>
<servlet-class>com.github.dandelion.core.web.DandelionServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>dandelionServlet</servlet-name>
<url-pattern>/dandelion-assets/*</url-pattern>
</servlet-mapping>
SpringTemplateEngine #Bean (skipped as I already have the Thymeleaf template Engine)
<bean id="templateEngine" class="org.thymeleaf.spring3.SpringTemplateEngine">
<property name="templateResolver" ref="templateResolver" />
<property name="additionalDialects">
<set>
<bean class="com.github.dandelion.datatables.thymeleaf.dialect.DataTablesDialect" />
</set>
</property>
</bean>
My knowledge of Spring is still extremely shaky but I have to replace the web.xml components (least I think I can do it this way):
public class Initializer extends
AbstractAnnotationConfigDispatcherServletInitializer...
#Override
protected Class<?>[] getServletConfigClasses() {
logger.debug("Entering getServletConfigClasses()");
return new Class<?>[] { ThymeleafConfig.class, WebAppConfig.class, DandelionServlet.class };
}
#Override
protected Filter[] getServletFilters() {
return new Filter[] { new DandelionFilter(),
new DelegatingFilterProxy("springSecurityFilterChain") };
}
My ThymeleafConfig:
#Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver());
templateEngine.addDialect(dataTablesDialect());
return templateEngine;
}
#Bean
public ThymeleafViewResolver thymeleafViewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine());
return resolver;
}
#Bean
public DataTablesDialect dataTablesDialect() {
return new DataTablesDialect();
}
My security settings:
.antMatchers("/dandelion-assets/**").permitAll()
.antMatchers("/datatablesController/**").permitAll()
I get the following in my logs after both dialects load:
[THYMELEAF] TEMPLATE ENGINE CONFIGURED OK
INFO org.thymeleaf.TemplateEngine - [THYMELEAF] TEMPLATE ENGINE INITIALIZED
When the page loads:
DEBUG com.github.dandelion.datatables.core.configuration.StandardConfigurationLoader - No custom configuration. Using default one.
DEBUG com.github.dandelion.datatables.core.configuration.StandardConfigurationLoader - Resolving groups for the locale en_US...
DEBUG com.github.dandelion.datatables.core.configuration.StandardConfigurationLoader - 1 groups declared [global].
DEBUG com.github.dandelion.datatables.core.configuration.StandardConfigurationLoader - Resolving configurations for the locale en_US...
DEBUG com.github.dandelion.datatables.core.configuration.StandardConfigurationLoader - Group 'global' initialized with 0 properties
DEBUG com.github.dandelion.datatables.core.configuration.StandardConfigurationLoader - 1 group(s) resolved [global] for the locale en_US
DEBUG com.github.dandelion.datatables.thymeleaf.processor.el.TableFinalizerElProcessor - No configuration to apply, i.e. no 'dt:conf' has been found in the current template.
DEBUG com.github.dandelion.datatables.core.generator.configuration.DatatablesGenerator - Generating DataTables configuration ..
DEBUG com.github.dandelion.datatables.core.generator.configuration.DatatablesGenerator - DataTables configuration generated
DEBUG com.github.dandelion.datatables.core.generator.WebResourceGenerator - Loading extensions...
DEBUG com.github.dandelion.datatables.core.extension.ExtensionLoader - Scanning built-in extensions...
DEBUG com.github.dandelion.datatables.core.generator.WebResourceGenerator - Transforming configuration to JSON...
DEBUG com.github.dandelion.datatables.thymeleaf.processor.el.TableFinalizerElProcessor - Web content generated successfully
DEBUG com.github.dandelion.datatables.core.configuration.DatatablesConfigurator - Initializing the Javascript generator...
Only warning I get:
WARN com.github.dandelion.core.asset.AssetMapper - No location found for delegate on AssetStorageUnit [name=dandelion-datatables, version=0.10.0, type=js, dom=null, locations={delegate=dandelion-datatables.js}, attributes=null, attributesOnlyName=[]]
DEBUG com.github.dandelion.core.asset.cache.AssetCacheManager - Retrieving asset with the key 6c075191955bbb1ecbd703380e648817806cf15b/dandelion-datatables-0.10.0.js
DEBUG com.github.dandelion.core.asset.cache.AssetCacheManager - Storing asset under the key 6c075191955bbb1ecbd703380e648817806cf15b/dandelion-datatables-0.10.0.js
WARN com.github.dandelion.core.asset.AssetMapper - No location found for delegate on AssetStorageUnit [name=dandelion-datatables, version=0.10.0, type=js, dom=null, locations={delegate=dandelion-datatables.js}, attributes=null, attributesOnlyName=[]]
DEBUG com.github.dandelion.core.asset.cache.AssetCacheManager - Retrieving asset with the key 6c075191955bbb1ecbd703380e648817806cf15b/dandelion-datatables-0.10.0.js
DEBUG com.github.dandelion.core.asset.cache.AssetCacheManager - Storing asset under the key 6c075191955bbb1ecbd703380e648817806cf15b/dandelion-datatables-0.10.0.js
And finally:
<table class="table table-striped table-bordered table-hover"
dt:table="true" id="myTable" dt:pagination="true">
When the page loads, the data loads but it isn't paginated; where'd I go wrong?
Just tried to set this up.
Here follows my working configuration.
WebConfig.java
#Configuration
#ComponentScan(basePackages = { "com.github.dandelion.datatables.web" })
#EnableWebMvc
#Import({ ThymeleafConfig.class })
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
Here is the imported ThymeleafConfig.java class:
#Configuration
public class ThymeleafConfig {
#Bean
public ServletContextTemplateResolver templateResolver() {
ServletContextTemplateResolver resolver = new ServletContextTemplateResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".html");
resolver.setTemplateMode("HTML5");
resolver.setCacheable(false);
return resolver;
}
#Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.setTemplateResolver(templateResolver());
engine.addDialect(new DandelionDialect());
engine.addDialect(new DataTablesDialect());
return engine;
}
#Bean
public ThymeleafViewResolver thymeleafViewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine());
return resolver;
}
}
Then the root configuration. Quite hairy as you can see.
RootConfig.java
#Configuration
#ComponentScan(basePackages = { "com.github.dandelion.datatables.service", "com.github.dandelion.datatables.repository" })
public class RootConfig {
}
And finally, the app initializer. Make sure to have well configured the Dandelion components (DandelionFilter and DandelionServlet).
ApplicationInitializer.java
public class ApplicationInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
// Register the Root application context
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(RootConfig.class);
// Register the Web application context
AnnotationConfigWebApplicationContext mvcContext = new AnnotationConfigWebApplicationContext();
mvcContext.register(WebConfig.class);
// Context loader listener
servletContext.addListener(new ContextLoaderListener(rootContext));
// Register the Dandelion filter
FilterRegistration.Dynamic dandelionFilter = servletContext.addFilter("dandelionFilter", new DandelionFilter());
dandelionFilter.addMappingForUrlPatterns(null, false, "/*");
// Register the Spring dispatcher servlet
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("springServlet", new DispatcherServlet(mvcContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
// Register the Dandelion servlet
ServletRegistration.Dynamic dandelionServlet = servletContext.addServlet("dandelionServlet", new DandelionServlet());
dandelionServlet.setLoadOnStartup(2);
dandelionServlet.addMapping("/dandelion-assets/*");
}
}
You can see the full sample application here.
Hope this helps!
(Disclaimer required by StackOverflow: I'm the author of Dandelion)

Spring Security without web.xml

What is the recommended way to add Spring Security to a web application that is using Spring's new WebApplicationInitializer interface instead of the web.xml file? I'm looking for the equivalent of:
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
UPDATE
The provided answers are reasonable but they both assume that I've got a servletContext instance. I've looked through the hierarchy of WebApplicationInitializers and I don't see any access to the servlet context unless I choose to override one of Spring's initializer methods. AbstractDispatcherServletInitializer.registerServletFilter seems like the sensible choice but it doesn't default to URL pattern mapping and I'd hate to change filter registration for everything if there is a better way.
This is the way that I have done it:
container.addFilter("springSecurityFilterChain", new DelegatingFilterProxy("springSecurityFilterChain"))
.addMappingForUrlPatterns(null, false, "/*");
container is an instance of ServletContext
The Spring Security Reference answers this question and the solution depends on whether or not you are using Spring Security in conjunction with Spring or Spring MVC.
Using Spring Security without Spring or Spring MVC
If you are not using Spring Security with Spring or Spring MVC (i.e. you do not have an existing WebApplicationInitializer) then you need to provide the following additional class:
import org.springframework.security.web.context.*;
public class SecurityWebApplicationInitializer
extends AbstractSecurityWebApplicationInitializer {
public SecurityWebApplicationInitializer() {
super(SecurityConfig.class);
}
}
Where SecurityConfig is your Spring Security Java configuration class.
Using Spring Security with Spring or Spring MVC
If you are using Spring Security with Spring or Spring MVC (i.e. you have an existing WebApplicationInitializer) then firstly you need to provide the following additional class:
import org.springframework.security.web.context.*;
public class SecurityWebApplicationInitializer
extends AbstractSecurityWebApplicationInitializer {
}
Then you need to ensure that your Spring Security Java configuration class, SecurityConfig in this example, is declared in your existing Spring or Spring MVC WebApplicationInitializer. For example:
import org.springframework.web.servlet.support.*;
public class MvcWebApplicationInitializer
extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] {SecurityConfig.class};
}
// ... other overrides ...
}
Dynamic securityFilter = servletContext.addFilter(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME, DelegatingFilterProxy.class);
securityFilter.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), false, "/*");
EnumSet.allOf(DispatcherType.class) to be sure that you add a mapping not only for default DispatcherType.REQUEST, but for DispatcherType.FORWARD, etc...
After a bit of work, I've discovered that it's actually quite simple:
public class Initialiser extends AbstractAnnotationConfigDispatcherServletInitializer implements WebApplicationInitializer {
#Override
protected Class< ? >[] getRootConfigClasses() {
return new Class[] { RootConfig.class };
}
#Override
protected Class< ? >[] getServletConfigClasses() {
return new Class[] { WebAppConfig.class };
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
#Override
protected Filter[] getServletFilters() {
return new Filter[] { new DelegatingFilterProxy("springSecurityFilterChain") };
}
}
The most important thing, though, is that you must have a root context (e.g. RootConfig in this case), and that must contain a reference to all the spring security information.
Thus, my RootConfig class:
#ImportResource("classpath:spring/securityContext.xml")
#ComponentScan({ "com.example.authentication", "com.example.config" })
#Configuration
public class RootConfig {
#Bean
public DatabaseService databaseService() {
return new DefaultDatabaseService();
}
#Bean
public ExceptionMappingAuthenticationFailureHandler authExceptionMapping() {
final ExceptionMappingAuthenticationFailureHandler emafh = new ExceptionMappingAuthenticationFailureHandler();
emafh.setDefaultFailureUrl("/loginFailed");
final Map<String, String> mappings = new HashMap<>();
mappings.put(CredentialsExpiredException.class.getCanonicalName(), "/change_password");
emafh.setExceptionMappings(mappings);
return emafh;
}
}
And spring/securityContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:noNamespaceSchemaLocation="http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<security:http security="none" pattern="/favicon.ico"/>
<!-- Secured pages -->
<security:http use-expressions="true">
<security:intercept-url pattern="/login" access="permitAll" />
<security:intercept-url pattern="/**" access="isAuthenticated()" />
<security:form-login default-target-url="/index" login-processing-url="/login_form" login-page="/login" authentication-failure-handler-ref="authExceptionMapping" />
</security:http>
<security:authentication-manager>
<security:authentication-provider ref="customAuthProvider" />
</security:authentication-manager>
</beans>
I could not get it to work if I merged the RootConfig and WebAppConfig classes into just WebAppConfig and had the following:
#Override
protected Class< ? >[] getRootConfigClasses() {
return null;
}
#Override
protected Class< ? >[] getServletConfigClasses() {
return new Class[] { WebAppConfig.class };
}
public class SIServerSecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
#Override
protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
Dynamic registration = servletContext.addFilter("TenantServletFilter", TenantServletFilter.class);
EnumSet<DispatcherType> dispatcherTypes = getSecurityDispatcherTypes();
registration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*");
}
}
This scenario is for executing a filter before executing other filters.
If you want to execute a filter after other filers pass true in registration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");. Also check the DispatcherType ASYNC, FORWARD etc.
Faced with the same problem. Merge RootConfig and WebAppConfig - not best way - because this i did not try this solution. Tried all other solutions - everty time got "org.apache.catalina.core.StandardContext.startInternal Error filterStart". After some work, got something like this:
FilterRegistration.Dynamic enc= servletContext.addFilter("encodingFilter",
new CharacterEncodingFilter());
encodingFilter .setInitParameter("encoding", "UTF-8");
encodingFilter .setInitParameter("forceEncoding", "true");
encodingFilter .addMappingForUrlPatterns(null, true, "/*");
But is not working with DelegatingFilterProxy(). Continue finding for best common solution for all filters.
UPDATE:
I did it.
So, the main issue is: if you want add filters using java config, especially if you want to add security filter, such as DelegatingFilterProxy, then you have to create WebAppSecurityConfig:
#Configuration
#EnableWebSecurity
#ImportResource("classpath:security.xml")
public class WebAppSecurityConfig extends WebSecurityConfigurerAdapter {
}
In this case i create WebAppSecurityConfig and make import resource ("security.xml").
This let me to do that in Initializer class:
servletContext.addFilter("securityFilter", new DelegatingFilterProxy("springSecurityFilterChain"))
.addMappingForUrlPatterns(null, false, "/*");
This is related to those interested in Spring Boot with security: You don't need anything, Spring Boot picks up the #components and solves the other issues

Resources