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