0

I'm very new to Spring, and I already have a very strange problem with a basic configuration.

Caused by: 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! at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:297) at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:107) at io.undertow.servlet.core.ApplicationListeners.contextInitialized(ApplicationListeners.java:173) at io.undertow.servlet.core.DeploymentManagerImpl.deploy(DeploymentManagerImpl.java:194) ... 7 more

I need two different REST servlets : one for internal calls (view to server), and one for external apis. I also want some services to be shared by both servlets, so I used a SpringRootConfiguration to scan the packages that contains two interceptors and one service.

All configuration is done in Java, with just the context and listener in the web.xml

web.xml

<context-param>
    <param-name>contextClass</param-name>
    <param-value>
        org.springframework.web.context.support.AnnotationConfigWebApplicationContext
    </param-value>
</context-param>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

SpringRestExternalAppInitializer

@Override
protected Class<?>[] getRootConfigClasses() {
    return new Class[]{SpringRootConfiguration.class};
}

@Override
protected Class<?>[] getServletConfigClasses() {
    return new Class[]{SpringRestExternalConfiguration.class};
}

@Override
protected String[] getServletMappings() {
    return new String[]{"/api/*"};
}

@Override
protected String getServletName() {
    return "restexternal";
}

SpringRootConfiguration

@ComponentScan({"be.xperthis.common"})
@Configuration
public class SpringRootConfiguration {

}

SpringRestExternalConfiguration

@ComponentScan("com.polymedis.result.web.api")
@Configuration
public class SpringRestExternalConfiguration extends WebMvcConfigurationSupport {

@Autowired
private NoCacheHandler noCacheHandler;

@Autowired
private RestMetricHandler restMetrics;

@Override
public void addInterceptors(final InterceptorRegistry registry) {
    registry.addInterceptor(noCacheHandler);
    registry.addInterceptor(restMetrics);
}

@Override
public void configureDefaultServletHandling(final DefaultServletHandlerConfigurer configurer) {
    configurer.enable();
}

@Override
public void configureMessageConverters(final List<HttpMessageConverter<?>> converters) {
    ....
}

@Override
public RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
    return new ApiVersionRequestMappingHandlerMapping(); //used to check a custom annotation on every RestController methods
}
}

In theory, there should be a second Rest configuration (internal), but I removed it for now while trying to understand the exception...

Note : Following the Spring security tutorial, I had the same error while putting the SpringSecurityConfiguration in getRootConfigClasses() { ... }

Should I remove/add something from the web.xml? I'm using Wildfly 8 and i already had to find a workaround for a bug in Wildfly : Spring confgiurations are not picked up if the spring libraries (jars) are not added in the WEB-Inf/lib folder...

ALansmanne
  • 271
  • 1
  • 6
  • 17
  • 1
    The problem is because you have both web.xml and `SpringRestExternalAppInitializer` (which likely extends `AbstractAnnotationConfigDispatcherServletInitializer`). That's actually almost the same - web.xml is the xml variant, and extending `AbstractAnnotationConfigDispatcherServletInitializer` is the way to do the same in Java. So you have to choose either leave web.xml or Java class. Check the answer to this question - it might help http://stackoverflow.com/questions/26676782/when-use-abstractannotationconfigdispatcherservletinitializer-and-webapplication – lenach87 Aug 11 '16 at 19:40
  • Removing the web.xml part did fix my first problem. But Now, I want to add a second servlet for internal REST calls. If I create two AbstractAnnotationConfigDispatcherServletInitializer, I do get the same error... – ALansmanne Aug 12 '16 at 09:02
  • Well, if just add two classes it shouldn't work straight away. Check this answer, it might help you to get the general understanding and the way where to search further http://stackoverflow.com/questions/12059307/multiple-application-context-multiple-dispatcher-servlets – lenach87 Aug 12 '16 at 11:40

1 Answers1

1

Ok, I've found a solution. Instead of overriding twice AbstractAnnotationConfigDispatcherServletInitializer, I'm now using a single class implementing WebApplicationInitializer, and I build my two servlets in this class.

public class SpringAppInitializer implements WebApplicationInitializer {

@Override
public void onStartup(final ServletContext servletContext) throws ServletException {
    final AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
    rootContext.register(SpringRootConfiguration.class, SpringSecurityConfig.class);
    servletContext.addListener(new ContextLoaderListener(rootContext));

    // Internal REST
    buildInternalRestContext(servletContext, rootContext);

    // External REST
    buildExternalRestContext(servletContext, rootContext);

    ...
}

public void buildExternalRestContext(final ServletContext servletContext, final AnnotationConfigWebApplicationContext rootContext) {
    final AnnotationConfigWebApplicationContext externalRestContext = new AnnotationConfigWebApplicationContext();
    externalRestContext.setParent(rootContext);
    externalRestContext.register(SpringRestExternalConfiguration.class);

    final ServletRegistration.Dynamic externalRestServlet = servletContext.addServlet("externalrest", new DispatcherServlet(externalRestContext));
    externalRestServlet.addMapping("/api/*");
}

public void buildInternalRestContext(final ServletContext servletContext, final AnnotationConfigWebApplicationContext rootContext) {
    final AnnotationConfigWebApplicationContext internalRestContext = new AnnotationConfigWebApplicationContext();
    internalRestContext.setParent(rootContext);
    internalRestContext.register(SpringRestConfiguration.class);

    final ServletRegistration.Dynamic restDispatcherServlet = servletContext.addServlet("rest", new DispatcherServlet(internalRestContext));
    restDispatcherServlet.addMapping("/rest/*");
}

}

I will now continue setting up SpringSecurity, but that's another topic :)

ALansmanne
  • 271
  • 1
  • 6
  • 17