/********************************************************************************** * $URL: https://source.sakaiproject.org/svn/kernel/trunk/component-manager/src/main/java/org/sakaiproject/util/ComponentsLoader.java $ * $Id: ComponentsLoader.java 105077 2012-02-24 22:54:29Z ottenhoff@longsight.com $ *********************************************************************************** * * Copyright (c) 2005, 2006, 2007, 2008 Sakai Foundation * * Licensed under the Educational Community 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 * * http://www.opensource.org/licenses/ECL-2.0 * * 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.sakaiproject.util; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Vector; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; /** * <p> * Load the available Sakai components into the shared component manager's Spring ApplicationContext * </p> */ public class ComponentsLoader { /** Our logger */ private static Log M_log = LogFactory.getLog(ComponentsLoader.class); public ComponentsLoader() { } /** * */ public void load(ConfigurableApplicationContext ac, String componentsRoot) { try { // get a list of the folders in the root File root = new File(componentsRoot); // make sure it's a dir. if (!root.isDirectory()) { M_log.warn("load: root not directory: " + componentsRoot); return; } // what component packages are there? File[] packageArray = root.listFiles(); if (packageArray == null) { M_log.warn("load: empty directory: " + componentsRoot); return; } List<File> packages = new ArrayList<File>(Arrays.asList(packageArray)); // for testing, we might reverse load order final int reverse = System.getProperty("sakai.components.reverse.load") != null ? -1 : 1; // assure a consistent order - sort these files Collections.sort(packages, new Comparator<Object>() { public int compare(Object o1, Object o2) { File f1 = (File) o1; File f2 = (File) o2; int sort = f1.compareTo(f2); return sort * reverse; } }); M_log.info("load: loading components from: " + componentsRoot); // process the packages for (File packageDir : packages) { // if a valid components directory if (validComponentsPackage(packageDir)) { loadComponentPackage(packageDir, ac); } else { M_log.warn("load: skipping non-package entry: " + packageDir); } } } catch (Exception e) { M_log.warn("load: exception: " + e, e); } } /** * Load one component package into the AC * * @param packageRoot * The file path to the component package * @param ac * The ApplicationContext to load into */ protected void loadComponentPackage(File dir, ConfigurableApplicationContext ac) { // setup the classloader onto the thread ClassLoader current = Thread.currentThread().getContextClassLoader(); ClassLoader loader = newPackageClassLoader(dir); M_log.info("loadComponentPackage: " + dir); Thread.currentThread().setContextClassLoader(loader); File xml = null; try { // load this xml file File webinf = new File(dir, "WEB-INF"); xml = new File(webinf, "components.xml"); // make a reader XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader((BeanDefinitionRegistry) ac.getBeanFactory()); // In Spring 2, classes aren't loaded during bean parsing unless this // classloader property is set. reader.setBeanClassLoader(loader); List<Resource> beanDefList = new ArrayList<Resource>(); beanDefList.add(new FileSystemResource(xml.getCanonicalPath())); // Load the demo components, if necessary File demoXml = new File(webinf, "components-demo.xml"); if("true".equalsIgnoreCase(System.getProperty("sakai.demo"))) { if(M_log.isDebugEnabled()) M_log.debug("Attempting to load demo components"); if(demoXml.exists()) { if(M_log.isInfoEnabled()) M_log.info("Loading demo components from " + dir); beanDefList.add(new FileSystemResource(demoXml.getCanonicalPath())); } } else { if(demoXml.exists()) { // Only log that we're skipping the demo components if they exist if(M_log.isInfoEnabled()) M_log.info("Skipping demo components from " + dir); } } reader.loadBeanDefinitions(beanDefList.toArray(new Resource[0])); } catch (Exception e) { M_log.warn("loadComponentPackage: exception loading: " + xml + " : " + e, e); } finally { // restore the context loader Thread.currentThread().setContextClassLoader(current); } } /** * Test if this File is a valid components package directory. * * @param dir * The file to test * @return true if it is a valid components package directory, false if not. */ protected boolean validComponentsPackage(File dir) { // valid if this is a directory with a WEB-INF directory below with a components.xml file if ((dir != null) && (dir.isDirectory())) { File webinf = new File(dir, "WEB-INF"); if ((webinf != null) && (webinf.isDirectory())) { File xml = new File(webinf, "components.xml"); if ((xml != null) && (xml.isFile())) { return true; } } } return false; } /** * Create the class loader for this component package * * @param dir * The package's root directory. * @return A class loader, whose parent is this class's loader, which has the classes/ and jars for this component. */ protected ClassLoader newPackageClassLoader(File dir) { // collect as a List, turn into an array after List urls = new Vector(); File webinf = new File(dir, "WEB-INF"); // put classes/ on the classpath File classes = new File(webinf, "classes"); if ((classes != null) && (classes.isDirectory())) { try { URL url = new URL("file:" + classes.getCanonicalPath() + "/"); urls.add(url); } catch (Exception e) { M_log.warn("Bad url for classes: "+classes.getPath()+" : "+e); } } // put each .jar file onto the classpath File lib = new File(webinf, "lib"); if ((lib != null) && (lib.isDirectory())) { File[] jars = lib.listFiles(new FileFilter() { public boolean accept(File file) { return (file.isFile() && file.getName().endsWith(".jar")); } }); if (jars != null) { for (int j = 0; j < jars.length; j++) { if (jars[j] != null) { try { URL url = new URL("file:" + jars[j].getCanonicalPath()); urls.add(url); } catch (Exception e) { M_log.warn("Bad url for jar: "+jars[j].getPath()+" : "+e); } } } } } // make the array from the list URL[] urlArray = (URL[]) urls.toArray(new URL[urls.size()]); ClassLoader loader = null; // Check to see if Terracotta clustering is turned on // String clusterTerracotta = ServerConfigurationService.getString("cluster.terracotta","false"); String clusterTerracotta = System.getProperty("sakai.cluster.terracotta"); if ("true".equals(clusterTerracotta)) { // If Terracotta clustering is turned on then use the Special Terracotta Class loader loader = new TerracottaClassLoader(urlArray, getClass().getClassLoader(), dir.getName()); } else { // Terracotta clustering is turned off, so use the normal URLClassLoader loader = new URLClassLoader(urlArray, getClass().getClassLoader()); } return loader; } }