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