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 }