Testing multiple Spring webApplicationContext in same test - spring

I have 1 application that was a single module with /intranet/** and /internet/** endpoints. Now I am splitting the application in two modules: intranet and internet. Each module will create a war so now I have two applications.
My problem is that I have multiple junit tests that invoke /intranet/** and /internet/** endpoints.
I can't figure how to configure the testing context in a way that I can invoke both application contexts.
In the intranet module I have the following configurations:
#Configuration
#ComponentScan(...)
#EnableWebMvc
#EnableTransactionManagement
public class MvcConfigIntra extends WebMvcConfigurerAdapter {
...
)
public class IntraWebInitializer implements WebApplicationInitializer{
#Override
public final void onStartup(ServletContext servletContext) {
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(RootConfigIntra.class);
servletContext.addListener(new ContextLoaderListener(rootContext));
AnnotationConfigWebApplicationContext webAppContext = new AnnotationConfigWebApplicationContext();
webAppContext.setParent(rootContext);
webAppContext.register(MvcConfigIntra.class);
ServletRegistration.Dynamic dispatcherWebApp = servletContext.addServlet("dispatcherIntranet", new DispatcherServlet(webAppContext));
dispatcherWebApp.addMapping("/*");
}
}
In the internet module it is the same thing:
#Configuration
#ComponentScan(...)
#EnableWebMvc
#EnableTransactionManagement
public class MvcConfigInter extends WebMvcConfigurerAdapter {
...
)
public class WebInitializer implements WebApplicationInitializer {
#Override
public final void onStartup(ServletContext servletContext) {
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(RootConfig.class);
servletContext.addListener(new ContextLoaderListener(rootContext));
AnnotationConfigWebApplicationContext webAppContext = new AnnotationConfigWebApplicationContext();
webAppContext.setParent(rootContext);
webAppContext.register(MvcConfigInter.class);
ServletRegistration.Dynamic dispatcherWebApp = servletContext.addServlet("dispatcherInternet", new DispatcherServlet(webAppContext));
dispatcherWebApp.addMapping("/*");
}
}
Now for tests:
#Configuration
#Import({
...
})
#Profile("test")
public class IntraTestConfig extends MvcConfigIntra {
}
#Configuration
#Import({
...
})
#Profile("test")
public class InterTestConfig extends MvcConfigInter {
}
A test inside intranet module:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(
classes = {
IntraTestConfig.class,
InterTestConfig.class
},
loader = AnnotationConfigWebContextLoader.class
)
#ActiveProfiles({"test"})
public class TestInternetAndIntranet {
private MockMvc mockMvcIntranet;
private MockMvc mockMvcInternet;
#Before
public void init() throws Exception {
this.mockMvcIntranet = MockMvcBuilders.webAppContextSetup(this.ctx).build();
/*HOW TO GET the right AppContext ?!*/
//this.mockMvcInternet = MockMvcBuilders.webAppContextSetup(this.ctx).build();
}
#Test
public final void test1() {
//This works
mockMvc.perform(post("/intranet/submeter")
//This DOES NOT work
mockMvc.perform(post("/internet/submeter")
}
}
The error is:
javax.servlet.ServletException: Could not resolve view with name 'internet/500' in servlet with name ''
at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1262)
at org.springframework.test.web.servlet.TestDispatcherServlet.render(TestDispatcherServlet.java:105)
at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1037)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:980)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:668)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
at org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:65)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:770)
at org.springframework.mock.web.MockFilterChain$ServletFilterProxy.doFilter(MockFilterChain.java:167)
at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134)
at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:155)
One thing I don't know is how to get the AppContext for mockMvcInternet.
Is this possible? What's missing in this test configuration?

I could not use two application contexts.
So, I changed the solution.
I made a new module old-tests to put all the tests that invoke /intranet/** and /internet/** endpoints.
In this module I have a Spring Application Context with all endpoints.

Related

How to test custom WebApplicationInitializer in Spring

I moved web.xml to Java annotation configuration
My custom code is
public class WebInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext container) {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.setDisplayName("app-name");
context.register(Config.class);
container.addListener(new RequestContextListener());
container.addListener(new ContextLoaderListener(context));
Dynamic dispatcher = container.addServlet("app-name", new DispatcherServlet(context));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/app-name-rest/*");
}
I tried the solution https://stackoverflow.com/a/25210356/6700081 but the line coverage is still 0%
I had the same problem and solved in this way:
public class WebInitializerTest {
#Mock
private MockServletContext mockServletContext;
#Mock
private ServletRegistration.Dynamic mockServletRegistration;
#BeforeEach
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testOnStartup() {
WebInitializer webInitializer = new WebInitializer();
when(mockServletContext.addServlet(ArgumentMatchers.eq("app-name"), any(Servlet.class))).thenReturn(mockServletRegistration);
webInitializer.onStartup(mockServletContext);
verify(mockServletContext, times(1)).addListener(any(RequestContextListener.class));
// other asserts..
}}
Note: I use Junit5 (#BeforeEach)

Integrating spring-boot with RESTEasy

I am trying to prototype a Spring Boot application. I'm coming from a Guice JAX-RS application, so I prefer the standard JAX-RS annotations to Spring MVC. I've gotten Jetty up and serving:
#Configuration
#Import({ResteasyBootstrap.class, SpringBeanProcessorServletAware.class, HttpServletDispatcher.class})
public class EmbeddedJetty {
#Bean
#Singleton
public EmbeddedServletContainerFactory servletContainer() {
JettyEmbeddedServletContainerFactory factory = new JettyEmbeddedServletContainerFactory();
factory.setPort(9000);
factory.setSessionTimeout(10, TimeUnit.MINUTES);
return factory;
}
}
However, I just can't figure out how to get RESTEasy hooked up correctly. With the above SpringBeanProcessorServletAware it bails, seemingly the ServletContext is not injected through ServletContextAware before it ends up being used:
java.lang.NullPointerException: null
at org.jboss.resteasy.plugins.spring.SpringBeanProcessorServletAware.getRegistry(SpringBeanProcessorServletAware.java:30)
at org.jboss.resteasy.plugins.spring.SpringBeanProcessor.postProcessBeanFactory(SpringBeanProcessor.java:247)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:284)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:174)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:680)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:522)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:766)
I also tried using the SpringContextLoaderListener, but that seems to conflict with the spring-boot AnnotationConfigEmbeddedWebApplicationContext class.
I'm using spring-boot 1.3.3 and spring-framework 4.3.0.rc1
The other answer won't have your resources as spring beans, this autoconfiguration will integrate them properly:
The Configuration class:
#Configuration
#ConditionalOnWebApplication
public class RestEasyAutoConfigurer {
private Environment environment;
#Bean(name = "resteasyDispatcher")
public ServletRegistrationBean resteasyServletRegistration() {
ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HttpServletDispatcher(), getPrefix()
+ "/*");
registrationBean.setInitParameters(ImmutableMap.of("resteasy.servlet.mapping.prefix", "/rs/")); // set prefix here
registrationBean.setLoadOnStartup(1);
return registrationBean;
}
#Bean(destroyMethod = "cleanup")
public static RestEasySpringInitializer restEasySpringInitializer() {
return new RestEasySpringInitializer();
}
#Bean
// use Spring Boot configured Jackson
public CustomResteasyJackson2Provider jackson2Provider(ObjectMapper mapper) {
return new CustomResteasyJackson2Provider(mapper);
}
public static class RestEasySpringInitializer
implements
ServletContextInitializer,
ApplicationContextAware,
BeanFactoryPostProcessor {
private ResteasyDeployment deployment;
private ConfigurableApplicationContext applicationContext;
private ConfigurableListableBeanFactory beanFactory;
public void cleanup() {
deployment.stop();
}
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
ListenerBootstrap config = new ListenerBootstrap(servletContext);
deployment = config.createDeployment();
deployment.start();
servletContext.setAttribute(ResteasyProviderFactory.class.getName(), deployment.getProviderFactory());
servletContext.setAttribute(Dispatcher.class.getName(), deployment.getDispatcher());
servletContext.setAttribute(Registry.class.getName(), deployment.getRegistry());
SpringBeanProcessor processor = new SpringBeanProcessor(deployment.getDispatcher(),
deployment.getRegistry(), deployment.getProviderFactory());
processor.postProcessBeanFactory(beanFactory);
applicationContext.addApplicationListener(processor);
}
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = (ConfigurableApplicationContext) applicationContext;
}
}
}
And the Jackson provider:
#Provider
#Consumes({"application/*+json", "text/json"})
#Produces({"application/*+json", "text/json"})
public class CustomResteasyJackson2Provider extends ResteasyJackson2Provider {
private ObjectMapper mapper;
public CustomResteasyJackson2Provider(ObjectMapper mapper) {
this.mapper = mapper;
}
#Override
public ObjectMapper locateMapper(Class<?> type, MediaType mediaType) {
return Optional.ofNullable(_mapperConfig.getConfiguredMapper()).orElse(mapper);
}
}
NOTE: this is a working configuration for Spring Boot 1.3.3 / RESTEasy 3.0.16
You can use RESTEasy Spring Boot starter. Here is how you do it:
Adding POM dependency
Add the Maven dependency below to your Spring Boot application pom file.
<dependency>
<groupId>com.paypal.springboot</groupId>
<artifactId>resteasy-spring-boot-starter</artifactId>
<version>2.1.1-RELEASE</version>
<scope>runtime</scope>
</dependency>
Registering JAX-RS application classes
Just define your JAX-RS application class (a subclass of Application) as a Spring bean, and it will be automatically registered. See the example below. See section JAX-RS application registration methods in How to use RESTEasy Spring Boot Starter for further information.
package com.test;
import org.springframework.stereotype.Component;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
#Component
#ApplicationPath("/sample-app/")
public class JaxrsApplication extends Application {
}
Registering JAX-RS resources and providers
Just define them as Spring beans, and they will be automatically registered. Notice that JAX-RS resources can be singleton or request scoped, while JAX-RS providers must be singletons.
Further information at the project GitHub page.
Here is fully working example.
First, a sample JAX-RS endpoint:
#Path("/api")
public class SampleResource {
#GET
#Path("/sample")
#Produces(MediaType.APPLICATION_JSON)
public String getSample() {
return "Some JSON";
}
}
Next, a JAX-RS configuration class that loads all endpoints.
import javax.ws.rs.core.Application;
public class RestEasyConfig extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<>();
classes.add(SampleRest.class);
return classes;
}
}
Finally, in your Spring configuration, initialize RESTEast filter and inform the framework about its existence.
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.jboss.resteasy.plugins.server.servlet.FilterDispatcher;
...
#Bean
public FilterRegistrationBean filterRegistrationBean() {
Map<String, String> initParams = new HashMap<>();
initParams.put("javax.ws.rs.Application", RestEasyConfig.class.getCanonicalName());
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new FilterDispatcher());
registrationBean.setInitParameters(initParams);
return registrationBean;
}
Your endpoint should be up and running. If you are missing the FilterDispatcher class on your class path, add the resteasy-jaxrs library to your build descriptor.

How to load ApplicationContext java-config in Junit

I am trying to move spring xml config to java config. Right now, I am able to make everything work, except one thing, that is to run junit on my services. The problem is that my junit test couldn't detect application context. Hence, how I can load application context java-based config in Junit? Since I can run my service on a browser, I assume that my integration is correct, meaning that my test configuration is not right, but I can't find where is my be wrong.
This is my junit config:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(
classes={Bootstrap.class, RootContextConfig.class
, WebServletContextConfig.class, PersistenceConfig.class })
#WebAppConfiguration
//#TransactionConfiguration(transactionManager="transactionManager",defaultRollback=true)
public class BaseTest { ... }
Just in case, I include my application context configuration here also.
This is my bootstrap
public class Bootstrap implements WebApplicationInitializer
{
#Override
public void onStartup(ServletContext container)
throws ServletException {
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(RootContextConfig.class);
container.addListener(new ContextLoaderListener(rootContext));
AnnotationConfigWebApplicationContext servletContext = new AnnotationConfigWebApplicationContext();
servletContext.register(WebServletContextConfig.class);
ServletRegistration.Dynamic dispatcher = container.addServlet(
"SpringDispatcher", new DispatcherServlet(servletContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
}
My RootContext
#Configuration
#EnableScheduling
#EnableAsync(
mode=AdviceMode.PROXY, proxyTargetClass=false,
order=Ordered.HIGHEST_PRECEDENCE
)
#ComponentScan(
basePackages={"com.core", "com.common"},
excludeFilters=#ComponentScan.Filter(Controller.class)
)
#Import({PersistenceConfig.class})
public class RootContextConfig {...}
My WebServletContext
#Configuration
#EnableWebMvc
#ComponentScan(
basePackages = { "com.web" },
useDefaultFilters = false,
includeFilters = #ComponentScan.Filter({Controller.class, ControllerAdvice.class})
)
public class WebServletContextConfig extends WebMvcConfigurerAdapter {..}
My PersistentContext
#Configuration
#EnableTransactionManagement(
mode=AdviceMode.PROXY, proxyTargetClass=false
, order=Ordered.LOWEST_PRECEDENCE)
public class PersistenceConfig {
#Bean
public DataSource customerSupportDataSource() {
//JndiDataSourceLookup lookup = new JndiDataSourceLookup();\
BasicDataSource basic = new BasicDataSource();
basic.setDriverClassName(KEY_DRIVER_CLASS);
basic.setUrl(KEY_DB_URL);
basic.setUsername(KEY_DB_USERNAME);
basic.setPassword(KEY_DB_PASSWORD);
return basic;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean()
{
Map<String, Object> properties = new Hashtable<>();
properties.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setDatabasePlatform("org.hibernate.dialect.PostgreSQLDialect");
adapter.setGenerateDdl(true);
adapter.setShowSql(true);
LocalContainerEntityManagerFactoryBean factory =
new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(adapter);
factory.setDataSource(this.customerSupportDataSource());
factory.setPackagesToScan("com.common.model");
factory.setSharedCacheMode(SharedCacheMode.ENABLE_SELECTIVE);
factory.setValidationMode(ValidationMode.NONE);
factory.setJpaPropertyMap(properties);
return factory;
}
#Bean
public PlatformTransactionManager jpaTransactionManager()
{
return new JpaTransactionManager(
this.entityManagerFactoryBean().getObject()
);
}
}

how to add filters to servlet when using #enablewebmvc annotation in spring 4?

Here is the current configuration
public class WebAppConfig implements WebApplicationInitializer {
private static final String CHARACTER_ENCODING_FILTER_ENCODING = "UTF-8";
private static final String CHARACTER_ENCODING_FILTER_NAME = "characterEncoding";
private static final String CHARACTER_ENCODING_FILTER_URL_PATTERN = "/*";
private static final String DISPATCHER_SERVLET_NAME = "dispatcher";
private static final String DISPATCHER_SERVLET_MAPPING = "/";
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(ExampleApplicationContext.class);
configureDispatcherServlet(servletContext, rootContext);
EnumSet<DispatcherType> dispatcherTypes = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD);
configureCharacterEncodingFilter(servletContext, dispatcherTypes);
servletContext.addListener(new ContextLoaderListener(rootContext));
}
private void configureDispatcherServlet(ServletContext servletContext, WebApplicationContext rootContext) {
ServletRegistration.Dynamic dispatcher = servletContext.addServlet(
DISPATCHER_SERVLET_NAME,
new DispatcherServlet(rootContext)
);
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping(DISPATCHER_SERVLET_MAPPING);
}
private void configureCharacterEncodingFilter(ServletContext servletContext, EnumSet<DispatcherType> dispatcherTypes) {
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding(CHARACTER_ENCODING_FILTER_ENCODING);
characterEncodingFilter.setForceEncoding(true);
FilterRegistration.Dynamic characterEncoding = servletContext.addFilter(CHARACTER_ENCODING_FILTER_NAME, characterEncodingFilter);
characterEncoding.addMappingForUrlPatterns(dispatcherTypes, true, CHARACTER_ENCODING_FILTER_URL_PATTERN);
}
}
Now I want to use #EnableWebMvc
#EnableWebMvc
#ComponentScan(basePackages = { "com.example.mvc.base.controller" })
public class WebAppConfig extends WebMvcConfigurerAdapter {
}
how would I add filters and Listeners to servletContext which I did using WebApplicationInitializer?
You still continue adding filters and listeners thorough your WebApplicationInitializer. This class methods starts up the web application with a Servlet registered with required contexts and beans.
In This class a DispatcherServlet is created containing programmatically configured AnnotationConfigWebApplicationContext.The dispatcher is also mapped to .html. 
WebAppConfig class enables WebMVC based on annotation and scans the base packages for the annotated resources. These are further registered by the web application context of the default servlet which was initialized by WebApplicationInitializer.

can not start spring mvc 4 application without web.xml

I am trying to deploy spring mvc 4 web application without web.xml file using #Configuration annotation only.
I have
public class WebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext)
throws ServletException {
WebApplicationContext context = getContext();
servletContext.addListener(new ContextLoaderListener(context));
ServletRegistration.Dynamic dispatcher = servletContext.addServlet(
"DispatcherServlet", new DispatcherServlet(context));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("*.html");
}
private AnnotationConfigWebApplicationContext getContext() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.setConfigLocation("ge.dm.icmc.config.WebConfig");
return context;
}
}
and my WebConfig.java class looks like :
#Configuration
#EnableWebMvc
#ComponentScan(basePackages="ge.dm.icmc")
public class WebConfig{
}
But when I try to start the application, I see in log :
14:49:12.275 [localhost-startStop-1] DEBUG o.s.w.c.s.AnnotationConfigWebApplicationContext - Could not load class for config location [] - trying package scan. java.lang.ClassNotFoundException:
If I try to add web.xml file, then it is started normally.
You are using the method setConfigLocation which, in this case, is wrong. You should use the register method instead.
private AnnotationConfigWebApplicationContext getContext() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(ge.dm.icmc.config.WebConfig.class);
return context;
}
However instead of implementing the WebApplicationInitializer I strongly suggest using one of the convenience classes of Spring for this. In your case the AbstractAnnotationConfigDispatcherServletInitializer would come in handy.
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
protected Class<?>[] getRootConfigClasses() { return null;}
protected Class<?>[] getServletConfigClasses() {
return new Class[] { WebConfig.class};
}
protected String[] getServletMappings() {
return new String[] {"*.html"};
}
}

Resources