/**
* Licensed to Apereo under one or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information regarding copyright ownership. Apereo
* licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use
* this file except in compliance with the License. You may obtain a copy of the License at the
* following location:
*
* <p>http://www.apache.org/licenses/LICENSE-2.0
*
* <p>Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jasig.portlet.calendar.util;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.SourceFilteringListener;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.FileSystemResourceLoader;
import org.springframework.mock.web.MockServletContext;
import org.springframework.mock.web.portlet.MockPortletConfig;
import org.springframework.mock.web.portlet.ServletWrappingPortletContext;
import org.springframework.test.context.ContextLoader;
import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.test.context.support.AbstractContextLoader;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.portlet.DispatcherPortlet;
import org.springframework.web.portlet.context.XmlPortletApplicationContext;
import org.springframework.web.servlet.ViewResolver;
/**
* A Spring {@link ContextLoader} that establishes a mock Portlet environment and {@link
* WebApplicationContext} so that Spring Portlet MVC stacks can be tested from within JUnit.
*/
public class MockWebApplicationContextLoader extends AbstractContextLoader {
/** The configuration defined in the {@link MockWebApplication} annotation. */
private MockWebApplication configuration;
public ApplicationContext loadContext(String... locations) throws Exception {
// Establish the portlet context and config based on the test class's MockWebApplication annotation.
MockServletContext mockServletContext =
new MockServletContext(configuration.webapp(), new FileSystemResourceLoader());
final ServletWrappingPortletContext portletContext =
new ServletWrappingPortletContext(mockServletContext);
final MockPortletConfig portletConfig =
new MockPortletConfig(portletContext, configuration.name());
// Create a WebApplicationContext and initialize it with the xml and portlet configuration.
final XmlPortletApplicationContext portletApplicationContext =
new XmlPortletApplicationContext();
portletContext.setAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, portletApplicationContext);
portletApplicationContext.setPortletConfig(portletConfig);
portletApplicationContext.setConfigLocations(locations);
// Create a DispatcherPortlet that uses the previously established WebApplicationContext.
final DispatcherPortlet dispatcherPortlet =
new DispatcherPortlet() {
@Override
protected WebApplicationContext createPortletApplicationContext(
ApplicationContext parent) {
return portletApplicationContext;
}
};
final ViewResolver viewResolver = new MockViewResolver();
// Add the DispatcherPortlet (and anything else you want) to the context.
// Note: this doesn't happen until refresh is called below.
portletApplicationContext.addBeanFactoryPostProcessor(
new BeanFactoryPostProcessor() {
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
beanFactory.registerResolvableDependency(DispatcherPortlet.class, dispatcherPortlet);
// Register any other beans here, including a ViewResolver if you are using JSPs.
beanFactory.registerResolvableDependency(ViewResolver.class, viewResolver);
}
});
// Have the context notify the portlet every time it is refreshed.
portletApplicationContext.addApplicationListener(
new SourceFilteringListener(
portletApplicationContext,
new ApplicationListener<ContextRefreshedEvent>() {
public void onApplicationEvent(ContextRefreshedEvent event) {
dispatcherPortlet.onApplicationEvent(event);
}
}));
// Prepare the context.
portletApplicationContext.refresh();
portletApplicationContext.registerShutdownHook();
// Initialize the portlet.
dispatcherPortlet.setContextConfigLocation("");
dispatcherPortlet.init(portletConfig);
return portletApplicationContext;
}
/**
* One of these two methods will get called before {@link #loadContext(String...)}. We just use
* this chance to extract the configuration.
*/
@Override
protected String[] generateDefaultLocations(Class<?> clazz) {
extractConfiguration(clazz);
return super.generateDefaultLocations(clazz);
}
/**
* One of these two methods will get called before {@link #loadContext(String...)}. We just use
* this chance to extract the configuration.
*/
@Override
protected String[] modifyLocations(Class<?> clazz, String... locations) {
extractConfiguration(clazz);
return super.modifyLocations(clazz, locations);
}
private void extractConfiguration(Class<?> clazz) {
configuration = AnnotationUtils.findAnnotation(clazz, MockWebApplication.class);
if (configuration == null)
throw new IllegalArgumentException(
"Test class " + clazz.getName() + " must be annotated @MockWebApplication.");
}
@Override
protected String getResourceSuffix() {
return "-context.xml";
}
@Override
public ApplicationContext loadContext(MergedContextConfiguration mergedConfig) throws Exception {
return loadContext(mergedConfig.getLocations());
}
}