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.servicemix.xbean;
018
019 import java.io.File;
020 import java.io.FilenameFilter;
021 import java.net.MalformedURLException;
022 import java.net.URI;
023 import java.net.URL;
024 import java.util.ArrayList;
025 import java.util.List;
026 import java.util.ListIterator;
027
028 import javax.xml.parsers.DocumentBuilder;
029
030 import org.w3c.dom.Document;
031 import org.w3c.dom.Element;
032 import org.w3c.dom.NodeList;
033 import org.w3c.dom.Text;
034
035 import org.apache.servicemix.jbi.jaxp.SourceTransformer;
036 import org.apache.xbean.classloader.JarFileClassLoader;
037 import org.apache.xbean.spring.context.SpringApplicationContext;
038 import org.apache.xbean.spring.context.SpringXmlPreprocessor;
039 import org.springframework.beans.FatalBeanException;
040 import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
041
042 /**
043 * An advanced xml preprocessor that will create a default classloader for the SU if none
044 * is configured.
045 *
046 * @author gnodet
047 */
048 public class ClassLoaderXmlPreprocessor implements SpringXmlPreprocessor {
049
050 public static final String CLASSPATH_XML = "classpath.xml";
051 public static final String LIB_DIR = "/lib";
052
053 private final File root;
054
055 public ClassLoaderXmlPreprocessor(File root) {
056 this.root = root;
057 }
058
059 public void preprocess(SpringApplicationContext applicationContext, XmlBeanDefinitionReader reader, Document document) {
060 // determine the classLoader
061 ClassLoader classLoader;
062 NodeList classpathElements = document.getDocumentElement().getElementsByTagName("classpath");
063 if (classpathElements.getLength() == 0) {
064 // Check if a classpath.xml file exists in the root of the SU
065 URL url = getResource(CLASSPATH_XML);
066 if (url != null) {
067 try {
068 DocumentBuilder builder = new SourceTransformer().createDocumentBuilder();
069 Document doc = builder.parse(url.toString());
070 classLoader = getClassLoader(applicationContext, reader, doc);
071 } catch (Exception e) {
072 throw new FatalBeanException("Unable to load classpath.xml file", e);
073 }
074 } else {
075 try {
076 URL[] urls = getDefaultLocations();
077 ClassLoader parentLoader = getParentClassLoader(applicationContext);
078 classLoader = new JarFileClassLoader(applicationContext.getDisplayName(), urls, parentLoader);
079 // assign the class loader to the xml reader and the
080 // application context
081 } catch (Exception e) {
082 throw new FatalBeanException("Unable to create default classloader for SU", e);
083 }
084 }
085 } else {
086 classLoader = getClassLoader(applicationContext, reader, document);
087 }
088 reader.setBeanClassLoader(classLoader);
089 applicationContext.setClassLoader(classLoader);
090 Thread.currentThread().setContextClassLoader(classLoader);
091 }
092
093 protected URL getResource(String location) {
094 URI uri = root.toURI().resolve(location);
095 File file = new File(uri);
096
097 if (!file.canRead()) {
098 return null;
099 }
100
101 try {
102 return file.toURL();
103 } catch (MalformedURLException e) {
104 throw new IllegalArgumentException("Malformed resource " + uri);
105 }
106 }
107
108 protected URL[] getDefaultLocations() {
109 try {
110 File[] jars = new File(root, LIB_DIR).listFiles(new FilenameFilter() {
111 public boolean accept(File dir, String name) {
112 name = name.toLowerCase();
113 return name.endsWith(".jar") || name.endsWith(".zip");
114 }
115 });
116 URL[] urls = new URL[jars != null ? jars.length + 1 : 1];
117 urls[0] = root.toURL();
118 if (jars != null) {
119 for (int i = 0; i < jars.length; i++) {
120 urls[i + 1] = jars[i].toURL();
121 }
122 }
123 return urls;
124 } catch (MalformedURLException e) {
125 throw new FatalBeanException("Unable to get default classpath locations", e);
126 }
127 }
128
129 protected ClassLoader getClassLoader(SpringApplicationContext applicationContext, XmlBeanDefinitionReader reader, Document document) {
130 // determine the classLoader
131 ClassLoader classLoader;
132 NodeList classpathElements = document.getDocumentElement().getElementsByTagName("classpath");
133 if (classpathElements.getLength() < 1) {
134 classLoader = getParentClassLoader(applicationContext);
135 } else if (classpathElements.getLength() > 1) {
136 throw new FatalBeanException("Expected only classpath element but found " + classpathElements.getLength());
137 } else {
138 Element classpathElement = (Element) classpathElements.item(0);
139
140 // Delegation mode
141 boolean inverse = false;
142 String inverseAttr = classpathElement.getAttribute("inverse");
143 if (inverseAttr != null && "true".equalsIgnoreCase(inverseAttr)) {
144 inverse = true;
145 }
146
147 // build hidden classes
148 List<String> hidden = new ArrayList<String>();
149 NodeList hiddenElems = classpathElement.getElementsByTagName("hidden");
150 for (int i = 0; i < hiddenElems.getLength(); i++) {
151 Element hiddenElement = (Element) hiddenElems.item(i);
152 String pattern = ((Text) hiddenElement.getFirstChild()).getData().trim();
153 hidden.add(pattern);
154 }
155
156 // build non overridable classes
157 List<String> nonOverridable = new ArrayList<String>();
158 NodeList nonOverridableElems = classpathElement.getElementsByTagName("nonOverridable");
159 for (int i = 0; i < nonOverridableElems.getLength(); i++) {
160 Element nonOverridableElement = (Element) nonOverridableElems.item(i);
161 String pattern = ((Text) nonOverridableElement.getFirstChild()).getData().trim();
162 nonOverridable.add(pattern);
163 }
164
165 // build the classpath
166 List<String> classpath = new ArrayList<String>();
167 NodeList locations = classpathElement.getElementsByTagName("location");
168 for (int i = 0; i < locations.getLength(); i++) {
169 Element locationElement = (Element) locations.item(i);
170 String location = ((Text) locationElement.getFirstChild()).getData().trim();
171 classpath.add(location);
172 }
173
174 // convert the paths to URLS
175 URL[] urls;
176 if (classpath.size() != 0) {
177 urls = new URL[classpath.size()];
178 for (ListIterator<String> iterator = classpath.listIterator(); iterator.hasNext();) {
179 String location = iterator.next();
180 URL url = getResource(location);
181 if (url == null) {
182 throw new FatalBeanException("Unable to resolve classpath location " + location);
183 }
184 urls[iterator.previousIndex()] = url;
185 }
186 } else {
187 urls = getDefaultLocations();
188 }
189
190 // create the classloader
191 List<ClassLoader> parents = new ArrayList<ClassLoader>();
192 parents.add(getParentClassLoader(applicationContext));
193 classLoader = new JarFileClassLoader(applicationContext.getDisplayName(),
194 urls,
195 parents.toArray(new ClassLoader[parents.size()]),
196 inverse,
197 hidden.toArray(new String[hidden.size()]),
198 nonOverridable.toArray(new String[nonOverridable.size()]));
199
200 // remove the classpath element so Spring doesn't get confused
201 document.getDocumentElement().removeChild(classpathElement);
202 }
203 return classLoader;
204 }
205
206 private ClassLoader getParentClassLoader(SpringApplicationContext applicationContext) {
207 ClassLoader classLoader = applicationContext.getClassLoader();
208 if (classLoader == null) {
209 classLoader = Thread.currentThread().getContextClassLoader();
210 }
211 if (classLoader == null) {
212 classLoader = getClass().getClassLoader();
213 }
214 return classLoader;
215 }
216
217 }