001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.xbean.server.spring.configuration;
018
019 import java.net.URL;
020 import java.util.ArrayList;
021 import java.util.List;
022 import java.util.ListIterator;
023
024 import org.apache.xbean.classloader.JarFileClassLoader;
025 import org.apache.xbean.server.repository.Repository;
026 import org.apache.xbean.server.spring.loader.SpringLoader;
027 import org.apache.xbean.spring.context.SpringApplicationContext;
028 import org.apache.xbean.spring.context.SpringXmlPreprocessor;
029 import org.springframework.beans.FatalBeanException;
030 import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
031 import org.w3c.dom.Document;
032 import org.w3c.dom.Element;
033 import org.w3c.dom.NodeList;
034 import org.w3c.dom.Text;
035
036 /**
037 * ClassLoaderXmlPreprocessor extracts a ClassLoader definition from the xml document, builds a class loader, assigns
038 * the class loader to the application context and xml reader, and removes the classpath element from document.
039 *
040 * @org.apache.xbean.XBean namespace="http://xbean.apache.org/schemas/server" element="class-loader-xml-preprocessor"
041 * description="Extracts a ClassLoader definition from the xml document."
042 *
043 * @author Dain Sundstrom
044 * @version $Id$
045 * @since 2.0
046 */
047 public class ClassLoaderXmlPreprocessor implements SpringXmlPreprocessor {
048 private final Repository repository;
049
050 /**
051 * Creates a ClassLoaderXmlPreprocessor that uses the specified repository to resolve the class path locations.
052 * @param repository the repository used to resolve the class path locations
053 */
054 public ClassLoaderXmlPreprocessor(Repository repository) {
055 this.repository = repository;
056 }
057
058 /**
059 * Extracts a ClassLoader definition from the xml document, builds a class loader, assigns
060 * the class loader to the application context and xml reader, and removes the classpath element from document.
061 *
062 * @param applicationContext the application context on which the class loader will be set
063 * @param reader the xml reader on which the class loader will be set
064 * @param document the xml document to inspect
065 */
066 public void preprocess(SpringApplicationContext applicationContext, XmlBeanDefinitionReader reader, Document document) {
067 // determine the classLoader
068 ClassLoader classLoader;
069 NodeList classpathElements = document.getDocumentElement().getElementsByTagName("classpath");
070 if (classpathElements.getLength() < 1) {
071 classLoader = getClassLoader(applicationContext);
072 } else if (classpathElements.getLength() > 1) {
073 throw new FatalBeanException("Expected only classpath element but found " + classpathElements.getLength());
074 } else {
075 Element classpathElement = (Element) classpathElements.item(0);
076
077 // Delegation mode
078 boolean inverse = false;
079 String inverseAttr = classpathElement.getAttribute("inverse");
080 if (inverseAttr != null && "true".equalsIgnoreCase(inverseAttr)) {
081 inverse = true;
082 }
083
084 // build hidden classes
085 List hidden = new ArrayList();
086 NodeList hiddenElems = classpathElement.getElementsByTagName("hidden");
087 for (int i = 0; i < hiddenElems.getLength(); i++) {
088 Element hiddenElement = (Element) hiddenElems.item(i);
089 String pattern = ((Text) hiddenElement.getFirstChild()).getData().trim();
090 hidden.add(pattern);
091 }
092
093 // build non overridable classes
094 List nonOverridable = new ArrayList();
095 NodeList nonOverridableElems = classpathElement.getElementsByTagName("nonOverridable");
096 for (int i = 0; i < nonOverridableElems.getLength(); i++) {
097 Element nonOverridableElement = (Element) nonOverridableElems.item(i);
098 String pattern = ((Text) nonOverridableElement.getFirstChild()).getData().trim();
099 nonOverridable.add(pattern);
100 }
101
102 // build the classpath
103 List classpath = new ArrayList();
104 NodeList locations = classpathElement.getElementsByTagName("location");
105 for (int i = 0; i < locations.getLength(); i++) {
106 Element locationElement = (Element) locations.item(i);
107 String location = ((Text) locationElement.getFirstChild()).getData().trim();
108 classpath.add(location);
109 }
110
111 // convert the paths to URLS
112 URL[] urls = new URL[classpath.size()];
113 for (ListIterator iterator = classpath.listIterator(); iterator.hasNext();) {
114 String location = (String) iterator.next();
115 URL url = repository.getResource(location);
116 if (url == null) {
117 throw new FatalBeanException("Unable to resolve classpath location " + location);
118 }
119 urls[iterator.previousIndex()] = url;
120 }
121
122 // create the classloader
123 ClassLoader parentLoader = getClassLoader(applicationContext);
124 classLoader = new JarFileClassLoader(applicationContext.getDisplayName(),
125 urls,
126 parentLoader,
127 inverse,
128 (String[]) hidden.toArray(new String[hidden.size()]),
129 (String[]) nonOverridable.toArray(new String[nonOverridable.size()]));
130
131 // remove the classpath element so Spring doesn't get confused
132 document.getDocumentElement().removeChild(classpathElement);
133 }
134
135 // assign the class loader to the xml reader and the application context
136 reader.setBeanClassLoader(classLoader);
137 applicationContext.setClassLoader(classLoader);
138 Thread.currentThread().setContextClassLoader(classLoader);
139 }
140
141 private static ClassLoader getClassLoader(SpringApplicationContext applicationContext) {
142 ClassLoader classLoader = applicationContext.getClassLoader();
143 if (classLoader == null) {
144 classLoader = Thread.currentThread().getContextClassLoader();
145 }
146 if (classLoader == null) {
147 classLoader = SpringLoader.class.getClassLoader();
148 }
149 return classLoader;
150 }
151
152 }