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