001: /**********************************************************************************
002: * $URL: https://source.sakaiproject.org/svn/component/tags/sakai_2-4-1/component-api/component/src/java/org/sakaiproject/util/ComponentsLoader.java $
003: * $Id: ComponentsLoader.java 27985 2007-03-29 00:21:32Z jholtzman@berkeley.edu $
004: ***********************************************************************************
005: *
006: * Copyright (c) 2005, 2006 The Sakai Foundation.
007: *
008: * Licensed under the Educational Community License, Version 1.0 (the "License");
009: * you may not use this file except in compliance with the License.
010: * You may obtain a copy of the License at
011: *
012: * http://www.opensource.org/licenses/ecl1.php
013: *
014: * Unless required by applicable law or agreed to in writing, software
015: * distributed under the License is distributed on an "AS IS" BASIS,
016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: * See the License for the specific language governing permissions and
018: * limitations under the License.
019: *
020: **********************************************************************************/package org.sakaiproject.util;
021:
022: import java.io.File;
023: import java.io.FileFilter;
024: import java.net.URL;
025: import java.net.URLClassLoader;
026: import java.util.Arrays;
027: import java.util.Comparator;
028: import java.util.List;
029: import java.util.Vector;
030:
031: import org.apache.commons.logging.Log;
032: import org.apache.commons.logging.LogFactory;
033: import org.sakaiproject.component.api.ComponentManager;
034: import org.sakaiproject.component.impl.SpringCompMgr;
035: import org.springframework.beans.factory.support.BeanDefinitionRegistry;
036: import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
037: import org.springframework.context.ConfigurableApplicationContext;
038: import org.springframework.core.io.FileSystemResource;
039: import org.springframework.core.io.Resource;
040:
041: /**
042: * <p>
043: * Load the available Sakai components into the shared component manager's Spring ApplicationContext
044: * </p>
045: */
046: public class ComponentsLoader implements
047: org.sakaiproject.component.api.ComponentsLoader {
048: /** Our logger */
049: private static Log M_log = LogFactory
050: .getLog(ComponentsLoader.class);
051:
052: public ComponentsLoader() {
053: }
054:
055: /**
056: *
057: */
058: public void load(ComponentManager mgr, String componentsRoot) {
059: try {
060: // get the ComponentManager's AC - assuming this is a SpringCompMgr. If not, this will throw.
061: ConfigurableApplicationContext ac = ((SpringCompMgr) mgr)
062: .getApplicationContext();
063:
064: // get a list of the folders in the root
065: File root = new File(componentsRoot);
066:
067: // make sure it's a dir.
068: if (!root.isDirectory()) {
069: M_log.warn("load: root not directory: "
070: + componentsRoot);
071: return;
072: }
073:
074: // what component packages are there?
075: File[] packages = root.listFiles();
076:
077: if (packages == null) {
078: M_log.warn("load: empty directory: " + componentsRoot);
079: return;
080: }
081:
082: // for testing, we might reverse load order
083: final int reverse = System
084: .getProperty("sakai.components.reverse.load") != null ? -1
085: : 1;
086:
087: // assure a consistent order - sort these files
088: Arrays.sort(packages, new Comparator() {
089: public int compare(Object o1, Object o2) {
090: File f1 = (File) o1;
091: File f2 = (File) o2;
092: int sort = f1.compareTo(f2);
093: return sort * reverse;
094: }
095: });
096:
097: M_log.info("load: loading components from: "
098: + componentsRoot);
099:
100: // process the packages
101: for (int p = 0; p < packages.length; p++) {
102: // if a valid components directory
103: if (validComponentsPackage(packages[p])) {
104: loadComponentPackage(packages[p], ac);
105: } else {
106: M_log.warn("load: skipping non-package entry: "
107: + packages[p]);
108: }
109: }
110: } catch (Throwable t) {
111: M_log.warn("load: exception: " + t);
112: }
113: }
114:
115: /**
116: * Load one component package into the AC
117: *
118: * @param packageRoot
119: * The file path to the component package
120: * @param ac
121: * The ApplicationContext to load into
122: */
123: protected void loadComponentPackage(File dir,
124: ConfigurableApplicationContext ac) {
125: // setup the classloader onto the thread
126: ClassLoader current = Thread.currentThread()
127: .getContextClassLoader();
128: ClassLoader loader = newPackageClassLoader(dir);
129:
130: M_log.info("loadComponentPackage: " + dir);
131:
132: Thread.currentThread().setContextClassLoader(loader);
133:
134: File xml = null;
135:
136: try {
137: // load this xml file
138: File webinf = new File(dir, "WEB-INF");
139: xml = new File(webinf, "components.xml");
140:
141: // make a reader
142: XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(
143: (BeanDefinitionRegistry) ac.getBeanFactory());
144: Resource[] beanDefs = null;
145:
146: // Load the demo components, if necessary
147: File demoXml = new File(webinf, "components-demo.xml");
148: if ("true".equalsIgnoreCase(System
149: .getProperty("sakai.demo"))) {
150: if (M_log.isDebugEnabled())
151: M_log.debug("Attempting to load demo components");
152: if (demoXml.exists()) {
153: if (M_log.isInfoEnabled())
154: M_log.info("Loading demo components from "
155: + dir);
156: beanDefs = new Resource[] {
157: new FileSystemResource(xml
158: .getCanonicalPath()),
159: new FileSystemResource(demoXml
160: .getCanonicalPath()) };
161: }
162: } else {
163: if (demoXml.exists()) {
164: // Only log that we're skipping the demo components if they exist
165: if (M_log.isInfoEnabled())
166: M_log.info("Skipping demo components from "
167: + dir);
168: }
169: }
170:
171: if (beanDefs == null) {
172: beanDefs = new Resource[] { new FileSystemResource(xml
173: .getCanonicalPath()) };
174: }
175:
176: reader.loadBeanDefinitions(beanDefs);
177: } catch (Throwable t) {
178: M_log.warn("loadComponentPackage: exception loading: "
179: + xml + " : " + t);
180: } finally {
181: // restore the context loader
182: Thread.currentThread().setContextClassLoader(current);
183: }
184: }
185:
186: /**
187: * Test if this File is a valid components package directory.
188: *
189: * @param dir
190: * The file to test
191: * @return true if it is a valid components package directory, false if not.
192: */
193: protected boolean validComponentsPackage(File dir) {
194: // valid if this is a directory with a WEB-INF directory below with a components.xml file
195: if ((dir != null) && (dir.isDirectory())) {
196: File webinf = new File(dir, "WEB-INF");
197: if ((webinf != null) && (webinf.isDirectory())) {
198: File xml = new File(webinf, "components.xml");
199: if ((xml != null) && (xml.isFile())) {
200: return true;
201: }
202: }
203: }
204:
205: return false;
206: }
207:
208: /**
209: * Create the class loader for this component package
210: *
211: * @param dir
212: * The package's root directory.
213: * @return A class loader, whose parent is this class's loader, which has the classes/ and jars for this component.
214: */
215: protected ClassLoader newPackageClassLoader(File dir) {
216: // collect as a List, turn into an array after
217: List urls = new Vector();
218:
219: File webinf = new File(dir, "WEB-INF");
220:
221: // put classes/ on the classpath
222: File classes = new File(webinf, "classes");
223: if ((classes != null) && (classes.isDirectory())) {
224: try {
225: URL url = new URL("file:" + classes.getCanonicalPath()
226: + "/");
227: urls.add(url);
228: } catch (Throwable t) {
229: }
230: }
231:
232: // put each .jar file onto the classpath
233: File lib = new File(webinf, "lib");
234: if ((lib != null) && (lib.isDirectory())) {
235: File[] jars = lib.listFiles(new FileFilter() {
236: public boolean accept(File file) {
237: return (file.isFile() && file.getName().endsWith(
238: ".jar"));
239: }
240: });
241:
242: if (jars != null) {
243: for (int j = 0; j < jars.length; j++) {
244: try {
245: URL url = new URL("file:"
246: + jars[j].getCanonicalPath());
247: urls.add(url);
248: } catch (Throwable t) {
249: }
250: }
251: }
252: }
253:
254: // make the array from the list
255: URL[] urlArray = (URL[]) urls.toArray(new URL[urls.size()]);
256:
257: // make the classloader - my loader is parent
258: URLClassLoader loader = new URLClassLoader(urlArray, getClass()
259: .getClassLoader());
260:
261: return loader;
262: }
263: }
|