Files
alfresco-community-repo/data-model/src/main/java/org/alfresco/util/BaseApplicationContextHelper.java

338 lines
11 KiB
Java

/*
* #%L
* Alfresco Data model classes
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.util;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.web.context.support.ServletContextResourcePatternResolver;
/**
* Helper class to provide static and common access to the Spring
* {@link org.springframework.context.ApplicationContext application context}.
*
* @author Derek Hulley
*/
public abstract class BaseApplicationContextHelper
{
private static ClassPathXmlApplicationContext instance;
private static String[] usedConfiguration;
private static String[] usedClassLocations;
private static boolean useLazyLoading = false;
private static boolean noAutoStart = false;
public synchronized static ApplicationContext getApplicationContext(String[] configLocations)
{
if (configLocations == null)
{
throw new IllegalArgumentException("configLocations argument is mandatory.");
}
if (usedConfiguration != null && Arrays.deepEquals(configLocations, usedConfiguration))
{
// The configuration was used to create the current context
return instance;
}
// The config has changed so close the current context (if any)
closeApplicationContext();
if(useLazyLoading || noAutoStart) {
instance = new VariableFeatureClassPathXmlApplicationContext(configLocations);
} else {
instance = new ClassPathXmlApplicationContext(configLocations);
}
usedConfiguration = configLocations;
return instance;
}
/**
* Build a classloader for the given classLocations, using the thread's context class loader as its parent.
*
* @param classLocations String[]
* @return ClassLoader
* @throws IOException
*/
public static ClassLoader buildClassLoader(String[] classLocations) throws IOException
{
ResourceFinder resolver = new ResourceFinder();
// Put the test directories at the front of the classpath
Resource[] resources = resolver.getResources(classLocations);
URL[] classpath = new URL[resources.length];
for (int i = 0; i< resources.length; i++)
{
classpath[i] = resources[i].getURL();
}
// Let's give our classloader 'child-first' resource loading qualities!
ClassLoader classLoader = new URLClassLoader(classpath, Thread.currentThread().getContextClassLoader())
{
@Override
public URL getResource(String name)
{
URL ret = findResource(name);
return ret == null ? super.getResource(name) : ret;
}
@SuppressWarnings("rawtypes")
@Override
public Enumeration<URL> getResources(String name) throws IOException
{
Enumeration[] tmp = new Enumeration[2];
tmp[0] = findResources(name);
tmp[1] = super.getResources(name);
return new CompoundEnumeration<URL>(tmp);
}
};
return classLoader;
}
/**
* Provides a static, single instance of the application context. This method can be
* called repeatedly.
* <p/>
* If the configuration requested differs from one used previously, then the previously-created
* context is shut down.
*
* @return Returns an application context for the given configuration
*/
public synchronized static ApplicationContext getApplicationContext(String[] configLocations, String[] classLocations) throws IOException
{
if (configLocations == null)
{
throw new IllegalArgumentException("configLocations argument is mandatory.");
}
if (usedConfiguration != null && Arrays.deepEquals(configLocations, usedConfiguration) && classLocations != null && Arrays.deepEquals(classLocations, usedClassLocations))
{
// The configuration was used to create the current context
return instance;
}
// The config has changed so close the current context (if any)
closeApplicationContext();
if(useLazyLoading || noAutoStart) {
instance = new VariableFeatureClassPathXmlApplicationContext(configLocations);
} else {
instance = new ClassPathXmlApplicationContext(configLocations, false);
}
if(classLocations != null)
{
ClassLoader classLoader = buildClassLoader(classLocations);
instance.setClassLoader(classLoader);
}
instance.refresh();
usedConfiguration = configLocations;
usedClassLocations = classLocations;
return instance;
}
/**
* Closes and releases the application context. On the next call to
* {@link #getApplicationContext(String[])} , a new context will be given.
*/
public static synchronized void closeApplicationContext()
{
if (instance == null)
{
// Nothing to do
return;
}
instance.close();
instance = null;
usedConfiguration = null;
}
/**
* Should the Spring beans be initilised in a lazy manner, or all in one go?
* Normally lazy loading/intialising shouldn't be used when running with the
* full context, but it may be appropriate to reduce startup times when
* using a small, cut down context.
*/
public static void setUseLazyLoading(boolean lazyLoading)
{
useLazyLoading = lazyLoading;
}
/**
* Will the Spring beans be initilised in a lazy manner, or all in one go?
* The default it to load everything in one go, as spring normally does.
*/
public static boolean isUsingLazyLoading()
{
return useLazyLoading;
}
/**
* Should the autoStart=true property on subsystems be honoured, or should
* this property be ignored and the auto start prevented? Normally we will
* use the spring configuration to decide what to start, but when running
* tests, you can use this to prevent the auto start.
*/
public static void setNoAutoStart(boolean noAutoStart)
{
BaseApplicationContextHelper.noAutoStart = noAutoStart;
}
/**
* Will Subsystems with the autoStart=true property set on them be allowed
* to auto start? The default is to honour the spring configuration and
* allow them to, but they can be prevented if required.
*/
public static boolean isNoAutoStart()
{
return noAutoStart;
}
/**
* Is there currently a context loaded and cached?
*/
public static boolean isContextLoaded()
{
return (instance != null);
}
/**
* A wrapper around {@link ClassPathXmlApplicationContext} which allows us
* to enable lazy loading or prevent Subsystem autostart as requested.
*/
protected static class VariableFeatureClassPathXmlApplicationContext extends ClassPathXmlApplicationContext
{
protected VariableFeatureClassPathXmlApplicationContext(String[] configLocations) throws BeansException
{
super(configLocations);
}
protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader)
{
super.initBeanDefinitionReader(reader);
if (useLazyLoading)
{
LazyClassPathXmlApplicationContext.postInitBeanDefinitionReader(reader);
}
if (noAutoStart)
{
NoAutoStartClassPathXmlApplicationContext.postInitBeanDefinitionReader(reader);
}
}
}
/**
* Can be used in Spring configuration to search for all resources matching an array of patterns.
*
* @author dward
*/
public static class ResourceFinder extends ServletContextResourcePatternResolver
{
public ResourceFinder()
{
super(new DefaultResourceLoader());
}
/**
* The Constructor.
*
* @param resourceLoader
* the resource loader
*/
public ResourceFinder(ResourceLoader resourceLoader)
{
super(resourceLoader);
}
/**
* Gets an array of resources matching the given location patterns.
*
* @param locationPatterns
* the location patterns
* @return the matching resources, ordered by locationPattern index and location in the classpath
* @throws IOException
* Signals that an I/O exception has occurred.
*/
public Resource[] getResources(String... locationPatterns) throws IOException
{
List<Resource> resources = new LinkedList<Resource>();
for (String locationPattern : locationPatterns)
{
resources.addAll(Arrays.asList(getResources(locationPattern)));
}
Resource[] resourceArray = new Resource[resources.size()];
resources.toArray(resourceArray);
return resourceArray;
}
}
}
/*
* A utility class that will enumerate over an array of enumerations.
* was removed in version JDK 1.9
*/
final class CompoundEnumeration<E> implements Enumeration<E> {
private final Enumeration<E>[] enums;
private int index;
public CompoundEnumeration(Enumeration<E>[] enums) {
this.enums = enums;
}
private boolean next() {
while (index < enums.length) {
if (enums[index] != null && enums[index].hasMoreElements()) {
return true;
}
index++;
}
return false;
}
public boolean hasMoreElements() {
return next();
}
public E nextElement() {
if (!next()) {
throw new NoSuchElementException();
}
return enums[index].nextElement();
}
}