blob: 43d7f69b1c20784e757ac3af89e0a046cbb6bd56 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2010 VMware Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* VMware Inc. - initial contribution
*******************************************************************************/
package org.eclipse.virgo.web.dm;
import java.io.IOException;
import java.util.Map;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextException;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.io.Resource;
import org.eclipse.gemini.blueprint.context.ConfigurableOsgiBundleApplicationContext;
import org.eclipse.gemini.blueprint.context.support.OsgiBundleXmlApplicationContext;
import org.eclipse.gemini.blueprint.extender.OsgiBeanFactoryPostProcessor;
import org.springframework.ui.context.Theme;
import org.springframework.ui.context.ThemeSource;
import org.springframework.ui.context.support.UiApplicationContextUtils;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.ConfigurableWebApplicationContext;
import org.springframework.web.context.ConfigurableWebEnvironment;
import org.springframework.web.context.ServletConfigAware;
import org.springframework.web.context.ServletContextAware;
import org.springframework.web.context.support.AbstractRefreshableWebApplicationContext;
import org.springframework.web.context.support.ServletContextAwareProcessor;
import org.springframework.web.context.support.ServletContextResourcePatternResolver;
import org.springframework.web.context.support.StandardServletEnvironment;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.context.support.XmlWebApplicationContext;
/**
* <code>ServerOsgiBundleXmlWebApplicationContext</code> is a custom extension of
* {@link OsgiBundleXmlApplicationContext} which provides support for application contexts backed by an OSGi
* {@link Bundle bundle} in Spring MVC based web applications by implementing {@link ConfigurableWebApplicationContext}.
* <p />
*
* Since Java does not support multiple inheritance, the implementation details specific to
* <code>ConfigurableWebApplicationContext</code> have been copied directly from {@link XmlWebApplicationContext} and
* {@link AbstractRefreshableWebApplicationContext}.
* <p />
*
* <strong>Concurrent Semantics</strong><br />
*
* This class is not thread-safe.
*
*/
public class ServerOsgiBundleXmlWebApplicationContext extends OsgiBundleXmlApplicationContext implements ConfigurableWebApplicationContext,
ThemeSource {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* {@link ServletContext} attribute name for the {@link BundleContext} to be used to back this
* {@link org.eclipse.gemini.blueprint.context.ConfigurableOsgiBundleApplicationContext
* ConfigurableOsgiBundleApplicationContext}.
*/
public static final String BUNDLE_CONTEXT_ATTRIBUTE = "osgi-bundlecontext";
/** service entry used for storing the namespace associated with this context */
private static final String APPLICATION_CONTEXT_SERVICE_NAMESPACE_PROPERTY = "org.springframework.web.context.namespace";
/** Suffix for WebApplicationContext namespaces. */
private static final String DEFAULT_NAMESPACE_SUFFIX = "-servlet";
private static final String PREFIX_DELIMITER = ":";
/** Servlet context that this context runs in */
private ServletContext servletContext;
/** ResourcePatternResolver for the associated ServletContext. */
private ServletContextResourcePatternResolver servletContextResourcePatternResolver;
/** Servlet config that this context runs in, if any */
private ServletConfig servletConfig;
/** Namespace of this context, or <code>null</code> if root */
private String namespace;
/** the ThemeSource for this ApplicationContext */
private ThemeSource themeSource;
/**
* Creates a <code>ServerOsgiBundleXmlWebApplicationContext</code> with no parent.
*/
public ServerOsgiBundleXmlWebApplicationContext() {
super();
setDisplayName("Root ServerOsgiBundleXmlWebApplicationContext");
}
/**
* Creates a <code>ServerOsgiBundleXmlWebApplicationContext</code> with the supplied config locations.
*
* @param configLocations the config locations.
*/
public ServerOsgiBundleXmlWebApplicationContext(String[] configLocations) {
super(configLocations);
setDisplayName("Root ServerOsgiBundleXmlWebApplicationContext");
logger.debug("Creating an ServerOsgiBundleXmlWebApplicationContext with locations [{}].", ObjectUtils.nullSafeToString(configLocations));
}
/**
* Creates a <code>ServerOsgiBundleXmlWebApplicationContext</code> with the supplied parent.
*
* @param parent the parent {@link ApplicationContext}.
*/
public ServerOsgiBundleXmlWebApplicationContext(ApplicationContext parent) {
super(parent);
setDisplayName("Root ServerOsgiBundleXmlWebApplicationContext");
logger.debug("Creating an ServerOsgiBundleXmlWebApplicationContext with parent [{}].", parent);
}
/**
* Creates a <code>ServerOsgiBundleXmlWebApplicationContext</code> with the supplied parent and config locations.
*
* @param configLocations the config locations.
* @param parent the parent {@link ApplicationContext}.
*/
public ServerOsgiBundleXmlWebApplicationContext(String[] configLocations, ApplicationContext parent) {
super(configLocations, parent);
setDisplayName("Root ServerOsgiBundleXmlWebApplicationContext");
logger.debug("Creating an ServerOsgiBundleXmlWebApplicationContext with locations [{}] and parent [{}].",
ObjectUtils.nullSafeToString(configLocations), parent);
}
/**
* Determines if the supplied <code>location</code> does not have a prefix.
*/
protected static boolean hasNoPrefix(String location) {
return location == null || location.indexOf(PREFIX_DELIMITER) < 1;
}
/**
* {@inheritDoc}
*/
public void setServletContext(final ServletContext servletContext) {
this.servletContext = servletContext;
this.servletContextResourcePatternResolver = new ServletContextResourcePatternResolver(servletContext);
// If the BundleContext has not yet been set, attempt to retrieve it from the ServletContext or the parent
// ApplicationContext.
if (getBundleContext() == null) {
getBundleContext();
}
}
/**
* {@inheritDoc}
*/
@Override
public BundleContext getBundleContext() {
BundleContext bundleContext = super.getBundleContext();
if (bundleContext == null) {
// Attempt to locate the BundleContext in the ServletContext
if (this.servletContext != null) {
Object bundleContextFromServletContext = this.servletContext.getAttribute(BUNDLE_CONTEXT_ATTRIBUTE);
if (bundleContextFromServletContext != null) {
Assert.isInstanceOf(BundleContext.class, bundleContextFromServletContext);
this.logger.debug("Using the BundleContext stored in the ServletContext as '{}'.", BUNDLE_CONTEXT_ATTRIBUTE);
bundleContext = (BundleContext) bundleContextFromServletContext;
setBundleContext(bundleContext);
}
}
// If still not set, fall back to the parent
if (bundleContext == null) {
ApplicationContext parent = getParent();
if (parent instanceof ConfigurableOsgiBundleApplicationContext) {
this.logger.debug("Using the parent ApplicationContext's BundleContext");
bundleContext = ((ConfigurableOsgiBundleApplicationContext) parent).getBundleContext();
setBundleContext(bundleContext);
}
}
}
return bundleContext;
}
/**
* {@inheritDoc}
*/
public ServletContext getServletContext() {
return this.servletContext;
}
/**
* {@inheritDoc}
*/
public void setServletConfig(ServletConfig servletConfig) {
this.servletConfig = servletConfig;
if (servletConfig != null) {
if (getServletContext() == null) {
setServletContext(servletConfig.getServletContext());
}
if (getNamespace() == null) {
setNamespace(servletConfig.getServletName() + DEFAULT_NAMESPACE_SUFFIX);
}
}
}
/**
* {@inheritDoc}
*/
public ServletConfig getServletConfig() {
return this.servletConfig;
}
/**
* Set the config locations for this application context in init-param style, i.e. with distinct locations separated
* by commas, semicolons or whitespace.
* <p>
* If not set, the implementation may use a default as appropriate.
*/
public void setConfigLocation(String location) {
setConfigLocations(StringUtils.tokenizeToStringArray(location, CONFIG_LOCATION_DELIMITERS));
}
/**
* The default location for the root context is "/WEB-INF/applicationContext.xml", and "/WEB-INF/test-servlet.xml"
* for a context with the namespace "test-servlet" (like for a DispatcherServlet instance with the servlet-name
* "test").
*/
@Override
protected String[] getDefaultConfigLocations() {
String ns = getNamespace();
if (ns != null) {
return new String[] { XmlWebApplicationContext.DEFAULT_CONFIG_LOCATION_PREFIX + ns
+ XmlWebApplicationContext.DEFAULT_CONFIG_LOCATION_SUFFIX };
} else {
return new String[] { XmlWebApplicationContext.DEFAULT_CONFIG_LOCATION };
}
}
/**
* {@inheritDoc}
*/
public void setNamespace(String namespace) {
this.namespace = namespace;
if (namespace != null) {
setDisplayName("ServerOsgiBundleXmlWebApplicationContext for namespace '" + namespace + "'");
}
}
/**
* {@inheritDoc}
*/
public String getNamespace() {
return this.namespace;
}
/**
* Register request/session scopes, a {@link ServletContextAwareProcessor}, etc.
*
* @see WebApplicationContextUtils#registerWebApplicationScopes(ConfigurableListableBeanFactory)
*/
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
super.postProcessBeanFactory(beanFactory);
// Drive the kernel's bean factory post processors.
BundleContext bundleContext = getBundleContext();
if (bundleContext != null) {
ServiceReference<OsgiBeanFactoryPostProcessor> sr = bundleContext.getServiceReference(OsgiBeanFactoryPostProcessor.class);
if (sr != null) {
OsgiBeanFactoryPostProcessor kernelPostProcessor = bundleContext.getService(sr);
try {
kernelPostProcessor.postProcessBeanFactory(bundleContext, beanFactory);
} catch (Exception e) {
throw new ApplicationContextException("Kernel bean factory post processor failed", e);
} finally {
bundleContext.ungetService(sr);
}
}
}
beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(getServletContext(), getServletConfig()));
beanFactory.ignoreDependencyInterface(ServletContextAware.class);
beanFactory.ignoreDependencyInterface(ServletConfigAware.class);
beanFactory.registerResolvableDependency(ServletContext.class, getServletContext());
beanFactory.registerResolvableDependency(ServletConfig.class, getServletConfig());
WebApplicationContextUtils.registerWebApplicationScopes(beanFactory);
}
/**
* {@inheritDoc}
*
* Additionally, this implementation publishes the context namespace under the
* <code>org.springframework.web.context.namespace</code> property.
*/
@SuppressWarnings("unchecked")
@Override
protected void customizeApplicationContextServiceProperties(Map serviceProperties) {
super.customizeApplicationContextServiceProperties(serviceProperties);
String ns = getNamespace();
if (ns != null) {
serviceProperties.put(APPLICATION_CONTEXT_SERVICE_NAMESPACE_PROPERTY, ns);
}
}
/**
* Returns a {@link org.springframework.web.context.support.ServletContextResource ServletContextResource} if the supplied <code>location</code> does not have a prefix and
* otherwise delegates to the parent implementation for standard Spring DM resource loading semantics.
* <p/>
* This override is necessary to return a suitable {@link Resource} type for flow-relative views. See DMS-2310.
*/
@Override
public Resource getResource(String location) {
if (hasNoPrefix(location)) {
return this.servletContextResourcePatternResolver.getResource(location);
}
return super.getResource(location);
}
/**
* Returns an array of {@link org.springframework.web.context.support.ServletContextResource ServletContextResource}s if the supplied <code>locationPattern</code> does
* not have a prefix and otherwise delegates to the parent implementation for standard Spring DM resource loading
* semantics.
* <p/>
* This override is necessary to return a suitable {@link Resource} type for flow-relative views. See DMS-2310.
*/
@Override
public Resource[] getResources(String locationPattern) throws IOException {
if (hasNoPrefix(locationPattern)) {
return this.servletContextResourcePatternResolver.getResources(locationPattern);
}
return super.getResources(locationPattern);
}
/**
* Initialize the theme capability.
*/
@Override
protected void onRefresh() {
this.themeSource = UiApplicationContextUtils.initThemeSource(this);
}
/**
* {@inheritDoc}
*/
public Theme getTheme(String themeName) {
return this.themeSource.getTheme(themeName);
}
/**
* {@inheritDoc}
*/
@Override
public ConfigurableWebEnvironment getEnvironment() {
ConfigurableEnvironment env = super.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
return (ConfigurableWebEnvironment) env;
} else {
throw new IllegalStateException("Environment is not a ConfigurableWebEnvironment");
}
}
/**
* {@inheritDoc}
*/
@Override
protected ConfigurableEnvironment createEnvironment() {
return new StandardServletEnvironment();
}
}