I am doing a web site project with Spring/ Spring MVC I learned how to configure a spring with the java classes and annotations approach, which by far is better than the XML.
Now I want to use Spring Security with my application. However I could not understand how to configure it with already existing WebApplicationInitializer ?
The Spring documentation is not so clear.
Here is my code and what I have so far:
public class AppInitializer implements WebApplicationInitializer{
//public class AppInitializer {
private static final Class<?>[] CONFIG_CLASSES = new Class<?>[]{SiteConfigs.class, AdminConfigurations.class};
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext();
appContext.register(CONFIG_CLASSES);
DispatcherServlet servlet = new DispatcherServlet(appContext);
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcher", servlet);
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
}
If I add AbstractSecurityWebApplicationInitializer to the classpath the container throws
exception.
HTTP Status 500 - No WebApplicationContext found: no ContextLoaderListener registered?
So how to config Spring Security so that springSecurityFilterChain get initialized
The exception tells you that you don't have a ContextLoaderListener which, in your case, is true. You only have a DispatcherServlet. By default Spring Security will only lookup the filters from the root application context (the one loaded by the ContextLoaderListener).
If you want to let it use a DispatcherServlets context instead you have to tell it that. You can tell it which to use by overriding the getDispatcherWebApplicationContextSuffix() method.
You aren't limited to a single WebApplicationInitializer you can have multiple in general you want to have one for your application bootstrapping and another to add security. You can then leverage the Spring convenience classes to save a couple of lines of code. See the Spring Security Reference for a sample.
Related
I have a spring boot application, and use the embedded jetty container, with no web.xml file.
I have a custom ContextLoaderLister class, which is a sub-class of default Spring ContextLoaderListener:
public class MyContextListener extends org.springframework.web.context.ContextLoaderListener
and register it as below:
#Configuration
public class ApplicationConfiguration {
#Bean
public ServletContextListener listener(){
return new ContextDestroyListener();
}
}
But while start the container, get the following error:
java.lang.IllegalStateException: Cannot initialize context because there is already a root application context present - check whether you have multiple ContextLoader* definitions in your web.xml!
I just want to use the custom ContextLoaderListener, rather than the default ContextLoaderListener. But get this error. It seems when I use the #Bean annotation to register a ContextLoaderListener, the default Spring ContextLoaderListener is still being used, so they are 2 ContextLoader in ApplicationContext, which cause this error.
I want to know what should I do, if I want to register my own ContextLoaderListener, and let Sprint not to add the default one automatically.
You can make your configuration extend SpringBootServletInitializer and then override onStartup method:
public class MyApplication extends SpringBootServletInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
servletContext.addListener(new MyContextListener());
}
}
Please be aware that this listener will not get contextInitialized event since context is already initialized in this moment.
I am building an application using 100% code configuration approach for a spring 4 web app. Following is my web config class.
public class WebAppInitializer extends Log4jServletContainerInitializer implements WebApplicationInitializer {
public void onStartup(ServletContext container) throws ServletException {
super.onStartup(null, container);
// Create the 'root' Spring application context
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(MyAppContext.class);
// Manage the lifecycle of the root application context
container.addListener(new ContextLoaderListener(rootContext));
// Create the dispatcher servlet's Spring application context
AnnotationConfigWebApplicationContext webContext = new AnnotationConfigWebApplicationContext();
webContext.register(MyServletContext.class);
// Register and map the dispatcher servlet
ServletRegistration.Dynamic dynamic = container.addServlet("dispatcher", new DispatcherServlet(webContext));
dynamic.setLoadOnStartup(1);
dynamic.addMapping("/api/*");
}
}
Problem -
a. My spring beans are getting initialized twice
b. Whenever I add logj2.xml in my resources (using maven), then my bean creation fails.
I am new to this, kindly help me.
Log4J - 2.5, Tomcat - 8.0.32
Thanks!
I managed to fix it. It was not the problem with WebApplInitializer but with Spring Java Configurations files. I was maintaining separate configs for ApplicationContext and ServletContext. In ApplicationContext, using
#ComponentScan(value = "com.application.module",
excludeFilters = {#ComponentScan.Filter(value = {Configuration.class})})
did the trick.
In servlet context, I used -
#ComponentScan(basePackageClasses = AppContext.class)
I started writing an app using Spring MVC, then decided to go with Tapestry instead. I want to keep the XML-less configuration approach that I was using originally. I first tried this:
public class ServletConfig implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
// Spring //
AnnotationConfigWebApplicationContext rootContext = new
AnnotationConfigWebApplicationContext();
rootContext.register(PersistenceContext.class, ApplicationContext.class);
servletContext.addListener(new ContextLoaderListener(rootContext));
// Tapestry //
servletContext.setInitParameter("tapestry.app-package", "...");
FilterRegistration.Dynamic filter = servletContext.addFilter("app", TapestrySpringFilter.class);
filter.addMappingForUrlPatterns(null, false, "/*");
}
}
The problem here is that tapestry creates another ContextLoaderListener, using the empty constructor. Instead of taking in a WebApplicationContext, it looks at the contextClass and contextConfigLocation init parameters. So I tried this:
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
// Spring //
servletContext.setInitParameter("contextClass",
"org.springframework.web.context.support.AnnotationConfigWebApplicationContext");
servletContext
.setInitParameter(
"contextConfigLocation",
"...config.PersistenceContext ...config.ApplicationContext");
// Tapestry //
servletContext.setInitParameter("tapestry.app-package", "...");
FilterRegistration.Dynamic filter = servletContext.addFilter("app", TapestrySpringFilter.class);
filter.addMappingForUrlPatterns(null, false, "/*");
}
Which caused this:
java.lang.IllegalArgumentException: When using the Tapestry/Spring integration library, you must specifiy a context class that extends from org.apache.tapestry5.spring.TapestryApplicationContext. Class org.springframework.web.context.support.AnnotationConfigWebApplicationContext does not. Update the 'contextClass' servlet context init parameter.
TapestryApplicationContext inherits from org.springframework.web.context.support.XmlWebApplicationContext. So my question(s): is there a way to make annotation-based configuration work with this approach? If not, is there another approach that will allow me to use it? Or is there no way to avoid the XML?
I tried reverting to the first version of ServletConfig I put up on here, but I added
servletContext.setInitParameter("tapestry.use-external-spring-context", "true");
I'm no longer getting the error message, but the page isn't loading either. When I try to load /app/, instead of loading the index page I get this:
<html>
<head>
<title>Error</title>
</head>
<body>/app/index.jsp</body>
</html>
I'm not sure what's causing this, I can't find anything in the logs to indicate any problems. I'm thinking that there's some sort of issue with the dispatcher service. Has anybody seen this type of error before? I'm not sure if this is unrelated to my original problem or if this is a symptom of my approach not being valid. If somebody can tell me that this is a separate issue I'll take the appropriate action.
The out-of-the-box spring integration expects an XML file.
It wouldn't be too hard to extend SpringModuleDef and override locateApplicationContext to return an AnnotationConfigApplicationContext
You would then write your own TapestrySpringFilter implementation which loads your new SpringModuleDef subclass.
--edit---
I was wrong, the tapestry spring integration uses WebApplicationContextUtils.getWebApplicationContext(servletContext) to lookup the ApplicationContext. So you could initialize the servlet context with an ApplicationContext prior to loading the TapestryStringFilter and it should just work.
eg
ApplicationContext myContext = createAnnotationBasedAppContext();
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, myContext);
I have a custom ServletContextListener that I am using to initialize and start up a Cron4J scheduler.
public class MainListener implements ServletContextListener {
#Value("${cron.pattern}")
private String dealHandlerPattern;
#Autowired
private DealMoqHandler dealMoqHandler;
}
I am autowiring some objects in the Listener as shown, and would like for Spring to manage the listener's instantiation. I am using programmatic web.xml configuration through WebApplicationInitializer, but so far the Listener isn't being autowired (NullPointerExceptions whenever I try to access the supposedly autowired objects).
I've already tried to add my customer listener after adding the ContextLoaderListener, shown below:
public class CouponsWebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext container) throws ServletException {
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(SpringAppConfig.class);
// Manage the lifecycle of the root application context
container.addListener(new ContextLoaderListener(rootContext));
container.addListener(new MainListener()); //TODO Not working
}
I checked these past questions Spring - Injecting a dependency into a ServletContextListener and dependency inject servlet listener and tried to implement the following code inside the contextInitialized method of my listener:
WebApplicationContextUtils
.getRequiredWebApplicationContext(sce.getServletContext())
.getAutowireCapableBeanFactory()
.autowireBean(this);
However, I just get the following exception:
Exception sending context initialized event to listener instance of class com.enovax.coupons.spring.CouponsMainListener: java.lang.IllegalStateException: No WebApplicationContext found: no ContextLoaderListener registered?
at org.springframework.web.context.support.WebApplicationContextUtils.getRequiredWebApplicationContext(WebApplicationContextUtils.java:90) [spring-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
How can I make sure that Spring has already finished instantiation before adding my listener?
My mistake. It turns out the code in the original post is correct, and the listeners are being added in the correct order.
The issue was that I had annotated my custom listener with a #WebListener annotation (Servlet 3.0). This was causing the web app to disregard my addListener() code in WebApplicationInitializer, and instantiate the custom listener AHEAD of Spring's ContextLoaderListener.
The following code block illustrates the ERRONEOUS code:
#WebListener /* should not have been added */
public class CouponsMainListener implements ServletContextListener {
#Autowired
private Prop someProp;
}
You cannot use new with Spring beans - Java doesn't care about Spring and Spring has no way to modify the behavior of the new operator. If you create your objects yourself, you need to wire them yourself.
You also need to be careful what you do during initialization. When using Spring, use this (simplified) model: First, Spring creates all the beans (calls new for all of them). Then it starts to wire them.
So you must be extra careful when you start to use the autowired fields. You can't always use them right away, you need to make sure that Spring is finished initializing everything.
In some cases, you can't even use autowired fields in #PostProcess methods because Spring couldn't create the bean because of cyclic dependencies.
So my guess is that "whenever I try to access" is too early.
For the same reason, you can't use WebApplicationContextUtils in the WebApplicationInitializer: It hasn't finished setting up Spring, yet.
I am using Spring framework (2.5.4) in my app with Load time weaving and everything works fine everywhere (in Spring beans, in non-Spring entities), except when I try to autowire field in a servlet annotated as #Configurable, then I get a nice NullPointerException...
#Configurable(dependencyCheck=true)
public class CaptchaServlet extends HttpServlet{
#Autowired
private CaptchaServiceIface captchaService;
#Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
// ApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(config.getServletContext());
// captchaService = (CaptchaServiceIface) ctx.getBean("captchaService");
}
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Captcha c = captchaService.getCatpcha();
req.getSession().setAttribute("captchaAnswer", c.getAnswer());
resp.setContentType("image/png");
ImageIO.write(c.getImage(), "png", resp.getOutputStream());
}
}
<context:load-time-weaver/>
<context:spring-configured/>
<context:component-scan base-package="cz.flexibla2" />
Any suggestions about what am I doing incorrectly?
Thanks.
This is likely because the Servlet is being instantiated and initialized by the servlet container, before the Spring context is being initialized, and it's the Spring context which handles the load-time weaving.
Is your <context:load-time-weaver/> stuff being handle inside the servlet Spring context/ or at the webapp-level? The former almost certainly won't work (for the reasons specified above), but a webapp-level config might work (using ContextLoaderListener).
See also mailing list discussion and bug report at https:// bugs.eclipse.org/bugs/show_bug.cgi?id=317874 . I agree that intuitively the #Configurable annotation on the servlet should be enough to indicate to the spring framework that the servlet when instantiated will be configured by spring, when using <context:spring-configured/>. I have also observed that the desired behavior is achievable when using the -javaagent:/path/to/aspectjweaver.jar instead of spring-instrument*.jar or spring-agent.jar. Please raise an issue with Spring Jira at https:// jira.springframework.org/browse/SPR. I believe that the problem may be that the servlet class - not an instance of the servlet, but the class itself - is loaded before the spring ContextLoaderListener is called, thus the spring framework does not have a chance to instrument the servlet class before it is loaded.
The spring instrumentation for load-time-weaving appears to be based on being able to transform class bytecode before it is loaded. If the servlet container is holding onto an instance of the Class object that was obtained before it was transformed by spring, then it (the servlet container) would not be able to produce instances of the transformed class, nor would spring be able to instrument instances created using the factory methods on that Class object.