/** * 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.apereo.portal.utils; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apereo.portal.utils.threading.SingletonDoubleCheckedCreator; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.GenericApplicationContext; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; /** * Provides standard access to the portal's {@link ApplicationContext}. If running in a web * application a {@link WebApplicationContext} is available. * * <p>{@link #getApplicationContext()} should be used by most uPortal code that needs access to the * portal's {@link ApplicationContext}. It ensures that a single {@link ApplicationContext} is used * portal-wide both when the portal is running as a web-application and when tools are run from the * command line. * * <p>For legacy portal code that is not yet Spring managed and does not have access to the {@link * ServletContext} this class provides similar functionality to {@link WebApplicationContextUtils} * via the {@link #getWebApplicationContext()} and {@link #getRequiredWebApplicationContext()}. * These methods are deprecated as any code that requires a {@link WebApplicationContext} should * either be refactored as a Spring managed bean or have access to the {@link ServletContext} * */ public class PortalApplicationContextLocator implements ServletContextListener { private static String LOGGER_NAME = PortalApplicationContextLocator.class.getName(); private static Log logger; /** * Inits and/or returns already initialized logger. <br> * You have to use this method in order to use the logger,<br> * you should not call the private variable directly.<br> * This was done because Tomcat may instantiate all listeners before calling contextInitialized * on any listener.<br> * Note that there is no synchronization here on purpose. The object returned by getLog for a * logger name is<br> * idempotent and getLog itself is thread safe. Eventually all <br> * threads will see an instance level logger variable and calls to getLog will stop. * * @return the log for this class */ protected static Log getLogger() { Log l = logger; if (l == null) { l = LogFactory.getLog(LOGGER_NAME); logger = l; } return l; } private static final SingletonDoubleCheckedCreator<ConfigurableApplicationContext> applicationContextCreator = new PortalApplicationContextCreator(); private static Throwable directCreatorThrowable; private static ServletContext servletContext; /* (non-Javadoc) * @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent) */ @Override public void contextInitialized(ServletContextEvent sce) { servletContext = sce.getServletContext(); } /* (non-Javadoc) * @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.ServletContextEvent) */ @Override public void contextDestroyed(ServletContextEvent sce) { servletContext = null; } /** * @return <code>true</code> if a WebApplicationContext is available, <code>false</code> if only * an ApplicationContext is available * @deprecated Only needed for using {@link #getRequiredWebApplicationContext()} or {@link * #getWebApplicationContext()}. */ @Deprecated public static boolean isRunningInWebApplication() { return servletContext != null; } /** * @return The WebApplicationContext for the portal * @throws IllegalStateException if no ServletContext is available to retrieve a * WebApplicationContext for or if the root WebApplicationContext could not be found * @deprecated This method is a work-around for areas in uPortal that do not have the ability to * use the {@link * WebApplicationContextUtils#getRequiredWebApplicationContext(ServletContext)} directly. */ @Deprecated public static WebApplicationContext getRequiredWebApplicationContext() { final ServletContext context = servletContext; if (context == null) { throw new IllegalStateException( "No ServletContext is available to load a WebApplicationContext for. Is this ServletContextListener not configured or has the ServletContext been destroyed?"); } return WebApplicationContextUtils.getRequiredWebApplicationContext(context); } /** * @return The WebApplicationContext for the portal, null if no ServletContext is available * @deprecated This method is a work-around for areas in uPortal that do not have the ability to * use the {@link WebApplicationContextUtils#getWebApplicationContext(ServletContext)} * directly. */ @Deprecated public static WebApplicationContext getWebApplicationContext() { final ServletContext context = servletContext; if (context == null) { return null; } return WebApplicationContextUtils.getWebApplicationContext(context); } /** * If running in a web application the existing {@link WebApplicationContext} will be returned. * if not a singleton {@link ApplicationContext} is created if needed and returned. Unless a * {@link WebApplicationContext} is specifically needed this method should be used as it will * work both when running in and out of a web application * * @return The {@link ApplicationContext} for the portal. */ public static ApplicationContext getApplicationContext() { final ServletContext context = servletContext; if (context != null) { getLogger().debug("Using WebApplicationContext"); if (applicationContextCreator.isCreated()) { final IllegalStateException createException = new IllegalStateException( "A portal managed ApplicationContext has already been created but now a ServletContext is available and a WebApplicationContext will be returned. " + "This situation should be resolved by delaying calls to this class until after the web-application has completely initialized."); getLogger().error(createException, createException); getLogger() .error( "Stack trace of original ApplicationContext creator", directCreatorThrowable); throw createException; } final WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(context); if (webApplicationContext == null) { throw new IllegalStateException( "ServletContext is available but WebApplicationContextUtils.getWebApplicationContext(ServletContext) returned null. Either the application context failed to load or is not yet done loading."); } return webApplicationContext; } return applicationContextCreator.get(); } /** * If the ApplicationContext returned by {@link #getApplicationContext()} is 'portal managed' * the shutdown hook for the context is called, closing and cleaning up all spring managed * resources. * * <p>If the ApplicationContext returned by {@link #getApplicationContext()} is actually a * WebApplicationContext this method does nothing but log an error message. */ public static void shutdown() { if (applicationContextCreator.isCreated()) { final ConfigurableApplicationContext applicationContext = applicationContextCreator.get(); applicationContext.close(); } else { final IllegalStateException createException = new IllegalStateException( "No portal managed ApplicationContext has been created, there is nothing to shutdown."); getLogger().error(createException, createException); } } /** * Creator class that knows how to instantiate the lazily initialized portal application context * if needed */ private static class PortalApplicationContextCreator extends SingletonDoubleCheckedCreator<ConfigurableApplicationContext> { @Override protected ConfigurableApplicationContext createSingleton(Object... args) { if (Boolean.getBoolean("org.apereo.portal.test")) { throw new IllegalStateException( PortalApplicationContextLocator.class.getName() + " MUST NOT be used in unit tests"); } getLogger() .info( "Creating new lazily initialized GenericApplicationContext for the portal"); final long startTime = System.currentTimeMillis(); final GenericApplicationContext genericApplicationContext = new GenericApplicationContext(); final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(genericApplicationContext); reader.setDocumentReaderClass(LazyInitByDefaultBeanDefinitionDocumentReader.class); reader.loadBeanDefinitions("/properties/contexts/*.xml"); genericApplicationContext.refresh(); genericApplicationContext.registerShutdownHook(); directCreatorThrowable = new Throwable(); directCreatorThrowable.fillInStackTrace(); getLogger() .info( "Created new lazily initialized GenericApplicationContext for the portal in " + (System.currentTimeMillis() - startTime) + "ms"); return genericApplicationContext; } } }