Author: jbonofre
Date: Wed Jun 23 19:55:09 2010
New Revision: 957324
URL: http://svn.apache.org/viewvc?rev=957324&view=rev
Log:
[SMXCOMP-765] Merge XBean classloader extension feature present in lightweight
container.
Modified:
servicemix/components/shared-libraries/trunk/servicemix-common/src/main/java/org/apache/servicemix/common/xbean/ClassLoaderXmlPreprocessor.java
Modified:
servicemix/components/shared-libraries/trunk/servicemix-common/src/main/java/org/apache/servicemix/common/xbean/ClassLoaderXmlPreprocessor.java
URL:
http://svn.apache.org/viewvc/servicemix/components/shared-libraries/trunk/servicemix-common/src/main/java/org/apache/servicemix/common/xbean/ClassLoaderXmlPreprocessor.java?rev=957324&r1=957323&r2=957324&view=diff
==============================================================================
---
servicemix/components/shared-libraries/trunk/servicemix-common/src/main/java/org/apache/servicemix/common/xbean/ClassLoaderXmlPreprocessor.java
(original)
+++
servicemix/components/shared-libraries/trunk/servicemix-common/src/main/java/org/apache/servicemix/common/xbean/ClassLoaderXmlPreprocessor.java
Wed Jun 23 19:55:09 2010
@@ -18,12 +18,18 @@ package org.apache.servicemix.common.xbe
import java.io.File;
import java.io.FilenameFilter;
+import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URI;
import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
+import java.util.Properties;
+import java.util.jar.JarInputStream;
+import java.util.zip.ZipEntry;
import javax.xml.parsers.DocumentBuilder;
@@ -44,6 +50,7 @@ import org.w3c.dom.Text;
* is configured.
*
* @author gnodet
+ * @author jbonofre
*/
public class ClassLoaderXmlPreprocessor implements SpringXmlPreprocessor {
@@ -62,18 +69,22 @@ public class ClassLoaderXmlPreprocessor
this.component = component;
}
+ /*
+ * (non-Javadoc)
+ * @see
org.apache.xbean.spring.context.SpringXmlPreprocessor#preprocess(org.apache.xbean.spring.context.SpringApplicationContext,
org.springframework.beans.factory.xml.XmlBeanDefinitionReader,
org.w3c.dom.Document)
+ */
public void preprocess(SpringApplicationContext applicationContext,
XmlBeanDefinitionReader reader, Document document) {
// determine the classLoader
ClassLoader classLoader;
NodeList classpathElements =
document.getDocumentElement().getElementsByTagName("classpath");
if (classpathElements.getLength() == 0) {
// Check if a classpath.xml file exists in the root of the SU
- URL url = getResource(CLASSPATH_XML);
- if (url != null) {
+ List<URL> classpathUrls = getResources(CLASSPATH_XML);
+ if (classpathUrls.size() == 1) {
DocumentBuilder builder = null;
try {
builder = DOMUtil.getBuilder();
- Document doc = builder.parse(url.toString());
+ Document doc =
builder.parse(classpathUrls.get(0).toString());
classLoader = getClassLoader(applicationContext, reader,
doc);
} catch (Exception e) {
throw new FatalBeanException("Unable to load classpath.xml
file", e);
@@ -99,21 +110,193 @@ public class ClassLoaderXmlPreprocessor
Thread.currentThread().setContextClassLoader(classLoader);
}
- protected URL getResource(String location) {
- URI uri = root.toURI().resolve(location);
- File file = new File(uri);
-
- if (!file.canRead()) {
- return null;
+ /**
+ * <p>
+ * Replaces a String with another String inside a larger String,
+ * for the first <code>max</code> values of the search String.
+ * </p>
+ * <p>A <code>null</code> reference passed to this method is a no-op.</p>
+ *
+ * @param text text to search and replace in, may be null.
+ * @param searchString the String to search for, may be null.
+ * @param replacement the String to replace it with, may be null.
+ * @param max maximum number of value to replace, or <code>-1</code> if no
maximum.
+ * @return the text with any replacements processed, <code>null</code> if
null String input.
+ */
+ private static String replaceString(String text, String searchString,
String replacement, int max) {
+ if (text == null || text.length() == 0
+ || searchString == null || searchString.length() == 0
+ || replacement == null
+ || max == 0) {
+ return text;
}
-
+ int start = 0;
+ int end = text.indexOf(searchString, start);
+ if (end == -1) {
+ return text;
+ }
+ int replLength = searchString.length();
+ int increase = replacement.length() - replLength;
+ increase = increase < 0 ? 0 : increase;
+ increase *= max < 0 ? 16 : (max > 64 ? 64 : max);
+ StringBuffer buffer = new StringBuffer(text.length() + increase);
+ while (end != -1) {
+ buffer.append(text.substring(start, end)).append(replacement);
+ start = end + replLength;
+ if (--max == 0) {
+ break;
+ }
+ end = text.indexOf(searchString, start);
+ }
+ buffer.append(text.substring(start));
+ return buffer.toString();
+ }
+
+ /**
+ * <p>
+ * Get the URLs for a classpath location. This method supports standard
+ * relative file location, <code>file:</code> URL location (including
entries
+ * regexp filter support), <code>jar:</code> URL location (including
entries
+ * regexp filter support).
+ * </p>
+ *
+ * @param location the location where to get the URLs.
+ * @return the URLs list.
+ */
+ protected List<URL> getResources(String location) {
+ // step 1: replace system properties in the location
+ Properties systemProperties = System.getProperties();
+ for (Iterator systemPropertiesIterator =
systemProperties.keySet().iterator(); systemPropertiesIterator.hasNext(); ) {
+ String property = (String) systemPropertiesIterator.next();
+ String value = systemProperties.getProperty(property);
+ location = ClassLoaderXmlPreprocessor.replaceString(location, "${"
+ property + "}", value, -1);
+ }
+ // step 2: apply regexp search for file: or jar: protocol based URL
+ if (location.startsWith("jar:")) {
+ return this.getJarResources(location);
+ } else if (location.startsWith("file:")) {
+ return this.getFileResources(location);
+ } else {
+ // relative location
+ List<URL> urls = new LinkedList<URL>();
+ URI uri = root.toURI().resolve(location);
+ File file = new File(uri);
+ if (file.canRead()) {
+ try {
+ urls.add(file.toURL());
+ } catch (MalformedURLException e) {
+ throw new IllegalArgumentException("Malformed resource
location " + uri + ".", e);
+ }
+ }
+ return urls;
+ }
+ }
+
+ /**
+ * <p>
+ * Get the URLs for a jar: protocol based location. This method supports
+ * regexp to add several entries.
+ * </p>
+ *
+ * <pre>
+ * jar:file:/path/to/my.ear!/entry.jar
+ * jar:file:/path/to/my.ear!/en*.jar
+ * </pre>
+ *
+ * @param location the jar location.
+ * @return the jar location URLs.
+ */
+ protected List<URL> getJarResources(String location) {
+ List<URL> urls = new LinkedList<URL>();
+ // get the !/ separator index
+ int separatorIndex = location.indexOf("!/");
+ if (separatorIndex == -1) {
+ throw new IllegalArgumentException("The jar URL " + location + "
is not valid. !/ separator not found.");
+ }
+ // extract the jar location
+ String jarLocation = location.substring(4, separatorIndex);
+ // extract the entry location
+ String entryLocation = location.substring(separatorIndex + 2);
+ if (jarLocation == null || jarLocation.length() == 0
+ || entryLocation == null || entryLocation.length() == 0) {
+ throw new IllegalArgumentException("The jar URL " + location + "
is not valid. Jar URL or entry not found.");
+ }
+ // contruct the jar URL
+ JarInputStream jarInputStream;
try {
- return file.toURL();
- } catch (MalformedURLException e) {
- throw new IllegalArgumentException("Malformed resource " + uri);
+ jarInputStream = new JarInputStream(new
URL(jarLocation).openStream());
+ } catch (Exception e) {
+ throw new IllegalArgumentException("The jar URL " + jarLocation +
" is not valid.", e);
+ }
+ // iterate into the entries
+ try {
+ ZipEntry entry = jarInputStream.getNextEntry();
+ while (entry != null) {
+ if (entry.getName().matches(entryLocation)) {
+ // the entry matches the regexp, construct the entry URL
+ String entryUrl = "jar:" + jarLocation + "!/" +
entry.getName();
+ // add the entry URL into the URLs list
+ urls.add(new URL(entryUrl));
+ }
+ entry = jarInputStream.getNextEntry();
+ }
+ } catch (IOException ioException) {
+ throw new IllegalArgumentException("Can't read jar entries.",
ioException);
}
+ return urls;
}
+ /**
+ * <p>
+ * Get the URLs for a file: protocol based location. This methods supports
+ * regexp to add severnal entries.
+ * </p>
+ *
+ * <pre>
+ * file:/path/to/my.jar
+ * file:/path/to/my*.jar
+ * </pre>
+ *
+ * @param location
+ * @return
+ */
+ protected List<URL> getFileResources(String location) {
+ List<URL> urls = new LinkedList<URL>();
+ int starIndex = location.indexOf("*");
+ if (starIndex == -1) {
+ // no regexp pattern found
+ try {
+ urls.add(new URL(location));
+ } catch (MalformedURLException malformedURLException) {
+ throw new IllegalArgumentException("Invalid URL " + location +
".", malformedURLException);
+ }
+ } else {
+ // user has defined a regexp file name filter
+ int lastSeparatorIndex = location.lastIndexOf("/");
+ if (starIndex < lastSeparatorIndex) {
+ throw new IllegalArgumentException("Regexp is supported only
on file name, not on directory.");
+ }
+ String dirPath = location.substring(0, lastSeparatorIndex);
+ File dir = new File(dirPath);
+ if (!dir.isDirectory()) {
+ throw new IllegalArgumentException("The regexp basedir " +
dirPath + " is not a directory.");
+ }
+ File[] entries = dir.listFiles();
+ String fileNameRegexp = location.substring(lastSeparatorIndex);
+ for (int i = 0; i < entries.length; i++) {
+ File entry = entries[i];
+ if (entry.getName().matches(fileNameRegexp)) {
+ try {
+ urls.add(new URL(dirPath + "/" + entry.getName()));
+ } catch (Exception e) {
+ throw new IllegalArgumentException("The URL " +
dirPath + "/" + entry.getName() + " is invalid.", e);
+ }
+ }
+ }
+ }
+ return urls;
+ }
+
protected URL[] getDefaultLocations() {
try {
File[] jars = new File(root, LIB_DIR).listFiles(new
FilenameFilter() {
@@ -201,15 +384,15 @@ public class ClassLoaderXmlPreprocessor
// convert the paths to URLS
URL[] urls;
if (classpath.size() != 0) {
- urls = new URL[classpath.size()];
+ List<URL> urlsList = new LinkedList<URL>();
for (ListIterator<String> iterator = classpath.listIterator();
iterator.hasNext();) {
String location = iterator.next();
- URL url = getResource(location);
- if (url == null) {
- throw new FatalBeanException("Unable to resolve
classpath location " + location);
+ List<URL> locationUrls = getResources(location);
+ for (URL url : locationUrls) {
+ urlsList.add(url);
}
- urls[iterator.previousIndex()] = url;
}
+ urls = urlsList.toArray(new URL[urlsList.size()]);
} else {
urls = getDefaultLocations();
}