Index: src/test/java/org/apache/servicemix/xbean/ClassLoaderXmlPreprocessorTest.java
===================================================================
--- src/test/java/org/apache/servicemix/xbean/ClassLoaderXmlPreprocessorTest.java	(revision 0)
+++ src/test/java/org/apache/servicemix/xbean/ClassLoaderXmlPreprocessorTest.java	(revision 0)
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicemix.xbean;
+
+import java.io.File;
+import java.net.URL;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Unit test for the ClassLoaderXmlPreprocessor, mainly focus on the getResource
+ * URL supports
+ * 
+ * @author jbonofre
+ */
+public class ClassLoaderXmlPreprocessorTest extends TestCase {
+
+    // logging facility
+    private static final transient Log LOG = LogFactory
+            .getLog(ClassLoaderXmlPreprocessorTest.class);
+
+    public void testJarResources() throws Exception {
+        LOG.debug("Define a sample System properties");
+        System.setProperty("servicemix.home", "/tmp");
+        LOG.debug("Create a ClassLoaderXmlPreprocessor with . as root");
+        ClassLoaderXmlPreprocessor classloader = new ClassLoaderXmlPreprocessor(
+                new File("."));
+
+        LOG.debug("Get the /tmp resource URL");
+        List<URL> dirResourceList = classloader.getResources("src/test/resources");
+        assertEquals("The directory resource list has not a correct size", 1, dirResourceList.size());
+        LOG.info("Directory relative resource : " + dirResourceList.get(0).toString());
+        List<URL> dirPropertyResourceList = classloader.getResources("${servicemix.home}");
+        assertEquals("The directory system property resource list has not a correct size", 1, dirPropertyResourceList.size());
+        LOG.info("Directory system property resource : " + dirPropertyResourceList.get(0).toString());
+        List<URL> fileResourceList = classloader.getResources("file:./src/test/resources");
+        assertEquals("The file resource list has not a correct size", 1, fileResourceList.size());
+        LOG.info("File resource : "  + fileResourceList.get(0).toString());
+        /* Comment these tests, the test.ear must be present in src/test/resources directory
+        List<URL> jarResourceList = classloader.getResources("jar:file:./src/test/resources/test.ear!/test");
+        assertEquals("The jar resource list has not a correct size", 1, jarResourceList.size());
+        LOG.info("Inside jar file resource : " + jarResourceList.get(0).toString());
+        List<URL> jarRegexpResourceList = classloader.getResources("jar:file:./src/test/resources/test.ear!/te(.*)");
+        assertEquals("The jar resource regexp list has not a correct size", 1, jarRegexpResourceList.size());
+        LOG.info("Inside jar regexp file resource : " + jarRegexpResourceList.get(0).toString());
+        assertEquals(new URL("jar:file:./src/test/resources/test.ear!/test"), (URL)jarRegexpResourceList.get(0));
+        */
+    }
+
+}
Index: src/main/java/org/apache/servicemix/xbean/ClassLoaderXmlPreprocessor.java
===================================================================
--- src/main/java/org/apache/servicemix/xbean/ClassLoaderXmlPreprocessor.java	(revision 746454)
+++ src/main/java/org/apache/servicemix/xbean/ClassLoaderXmlPreprocessor.java	(working copy)
@@ -22,8 +22,14 @@
 import java.net.URI;
 import java.net.URL;
 import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.ListIterator;
+import java.util.Properties;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
 
 import javax.xml.parsers.DocumentBuilder;
 
@@ -40,46 +46,54 @@
 import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
 
 /**
- * An advanced xml preprocessor that will create a default classloader for the SU if none
- * is configured.
+ * An advanced xml preprocessor that will create a default classloader for the
+ * SU if none is configured.
  * 
  * @author gnodet
  */
 public class ClassLoaderXmlPreprocessor implements SpringXmlPreprocessor {
 
     public static final String CLASSPATH_XML = "classpath.xml";
+
     public static final String LIB_DIR = "/lib";
-    
+
     private final File root;
-    
+
     public ClassLoaderXmlPreprocessor(File root) {
         this.root = root;
     }
 
-    public void preprocess(SpringApplicationContext applicationContext, XmlBeanDefinitionReader reader, Document document) {
+    public void preprocess(SpringApplicationContext applicationContext,
+            XmlBeanDefinitionReader reader, Document document) {
         // determine the classLoader
         ClassLoader classLoader;
-        NodeList classpathElements = document.getDocumentElement().getElementsByTagName("classpath");
+        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) {
                 try {
-                    DocumentBuilder builder = new SourceTransformer().createDocumentBuilder();
-                    Document doc = builder.parse(url.toString());
-                    classLoader = getClassLoader(applicationContext, reader, doc);
+                    DocumentBuilder builder = new SourceTransformer()
+                            .createDocumentBuilder();
+                    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);
+                    throw new FatalBeanException(
+                            "Unable to load classpath.xml file", e);
                 }
             } else {
                 try {
                     URL[] urls = getDefaultLocations();
                     ClassLoader parentLoader = getParentClassLoader(applicationContext);
-                    classLoader = new JarFileClassLoader(applicationContext.getDisplayName(), urls, parentLoader);
+                    classLoader = new JarFileClassLoader(applicationContext
+                            .getDisplayName(), urls, parentLoader);
                     // assign the class loader to the xml reader and the
                     // application context
                 } catch (Exception e) {
-                    throw new FatalBeanException("Unable to create default classloader for SU", e);
+                    throw new FatalBeanException(
+                            "Unable to create default classloader for SU", e);
                 }
             }
         } else {
@@ -89,30 +103,178 @@
         applicationContext.setClassLoader(classLoader);
         Thread.currentThread().setContextClassLoader(classLoader);
     }
+    
+    /**
+     * <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>
+     *
+     * <pre>
+     * StringUtils.replace(null, *, *, *)         = null
+     * StringUtils.replace("", *, *, *)           = ""
+     * StringUtils.replace("any", null, *, *)     = "any"
+     * StringUtils.replace("any", *, null, *)     = "any"
+     * StringUtils.replace("any", "", *, *)       = "any"
+     * StringUtils.replace("any", *, *, 0)        = "any"
+     * StringUtils.replace("abaa", "a", null, -1) = "abaa"
+     * StringUtils.replace("abaa", "a", "", -1)   = "b"
+     * StringUtils.replace("abaa", "a", "z", 0)   = "abaa"
+     * StringUtils.replace("abaa", "a", "z", 1)   = "zbaa"
+     * StringUtils.replace("abaa", "a", "z", 2)   = "zbza"
+     * StringUtils.replace("abaa", "a", "z", -1)  = "zbzz"
+     * </pre>
+     *
+     * @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 values 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 replace(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 URL for a classpath location. This method supports standard
+     * relative file location, <code>file:</code> URL location,
+     * <code>jar:</code> URL location with regexp on the entries.
+     * </p>
+     * 
+     * @param location
+     *            the location where to get the URL
+     * @return an URL list
+     */
+    protected List<URL> getResources(String location) {
+        List<URL> urls = new LinkedList<URL>();
+        // step 1: replace system properties in the location
+        Properties properties = System.getProperties();
+        for (Iterator propertyIterator = properties.keySet().iterator(); propertyIterator.hasNext();) {
+            String property = (String)propertyIterator.next();
+            String value = properties.getProperty(property);
+            location = ClassLoaderXmlPreprocessor.replace(location, "${" + property + "}", value, -1);
+        }
+        // step 2: resolve the location URL resources
+        if (location.startsWith("jar:file:")) {
+            try {
+                return this.getJarResources(location);
+            } catch (Exception e) {
+                throw new IllegalArgumentException(
+                        "Can't get the Jar resource " + location, e);
+            }
+        } else if (location.startsWith("file:")) {
+            try {
+                urls.add(new URL(location));
+            } catch (MalformedURLException e) {
+                throw new IllegalArgumentException(
+                        "Malformed file URL resource " + location, e);
+            }
+        } else {
+            URI uri = root.toURI().resolve(location);
+            File file = new File(uri);
 
-    protected URL getResource(String location) {
-        URI uri = root.toURI().resolve(location);
-        File file = new File(uri);
+            if (!file.canRead()) {
+                return null;
+            }
 
-        if (!file.canRead()) {
-            return null;
+            try {
+                urls.add(file.toURL());
+            } catch (MalformedURLException e) {
+                throw new IllegalArgumentException("Malformed resource " + uri);
+            }
         }
+        return urls;
+    }
 
-        try {
-            return file.toURL();
-        } catch (MalformedURLException e) {
-            throw new IllegalArgumentException("Malformed resource " + uri);
+    /**
+     * Manager Jar location including regexp on the Jar entries. A Jar location
+     * looks like jar:/path/to/my.ear!/entry.jar or
+     * jar:/path/to/my.ear!/ent*.jar
+     * 
+     * @param location
+     *            the Jar location
+     * @return a Jar URL List
+     * @throws Exception
+     *             in case of error while exploring the Jar location
+     */
+    protected List<URL> getJarResources(String location) throws Exception {
+        // create an empty Jar URL List
+        List<URL> urls = new LinkedList<URL>();
+        // parse the jar URI
+        StringBuffer locationBuffer = new StringBuffer(location);
+        if (locationBuffer.length() < 9) {
+            throw new IllegalArgumentException(
+                    "The Jar location is not valid. It must contain at least the jar:file: protocol.");
+        }
+        // get the separator position
+        int separatorPosition = locationBuffer.indexOf("!/");
+        if (separatorPosition == -1) {
+            throw new IllegalArgumentException(
+                    "The Jar location is not valid. Separator !/ not found");
+        }
+        // get the jar file URI
+        String fileName = locationBuffer.substring(9, separatorPosition);
+        // get the jar entry
+        String entryName = locationBuffer.substring(separatorPosition + 2,
+                locationBuffer.length());
+        if (fileName == null || fileName.trim().length() < 1
+                || entryName == null || entryName.trim().length() < 1) {
+            throw new IllegalArgumentException(
+                    "The Jar location is not valid. Jar file or entry is empty");
+        }
+        // read the jar file (as a zip file)
+        ZipFile zipFile = new ZipFile(fileName);
+        // get the zip file entrie
+        Enumeration entries = zipFile.entries();
+        while (entries.hasMoreElements()) {
+            ZipEntry entry = (ZipEntry) entries.nextElement();
+            if (entry.getName().matches(entryName)) {
+                // the zip entry match the regexp, construct the URL
+                String url = "jar:file:" + fileName + "!/" + entry.getName();
+                // add the URLs in the list
+                urls.add(new URL(url));
+            }
         }
+        return urls;
     }
-    
+
     protected URL[] getDefaultLocations() {
         try {
-            File[] jars = new File(root, LIB_DIR).listFiles(new FilenameFilter() {
-                public boolean accept(File dir, String name) {
-                    name = name.toLowerCase();
-                    return name.endsWith(".jar") || name.endsWith(".zip");
-                }
-            });
+            File[] jars = new File(root, LIB_DIR)
+                    .listFiles(new FilenameFilter() {
+                        public boolean accept(File dir, String name) {
+                            name = name.toLowerCase();
+                            return name.endsWith(".jar")
+                                    || name.endsWith(".zip");
+                        }
+                    });
             URL[] urls = new URL[jars != null ? jars.length + 1 : 1];
             urls[0] = root.toURL();
             if (jars != null) {
@@ -122,21 +284,27 @@
             }
             return urls;
         } catch (MalformedURLException e) {
-            throw new FatalBeanException("Unable to get default classpath locations", e);
+            throw new FatalBeanException(
+                    "Unable to get default classpath locations", e);
         }
     }
-    
-    protected ClassLoader getClassLoader(SpringApplicationContext applicationContext, XmlBeanDefinitionReader reader, Document document) {
+
+    protected ClassLoader getClassLoader(
+            SpringApplicationContext applicationContext,
+            XmlBeanDefinitionReader reader, Document document) {
         // determine the classLoader
         ClassLoader classLoader;
-        NodeList classpathElements = document.getDocumentElement().getElementsByTagName("classpath");
+        NodeList classpathElements = document.getDocumentElement()
+                .getElementsByTagName("classpath");
         if (classpathElements.getLength() < 1) {
             classLoader = getParentClassLoader(applicationContext);
         } else if (classpathElements.getLength() > 1) {
-            throw new FatalBeanException("Expected only classpath element but found " + classpathElements.getLength());
+            throw new FatalBeanException(
+                    "Expected only classpath element but found "
+                            + classpathElements.getLength());
         } else {
             Element classpathElement = (Element) classpathElements.item(0);
-            
+
             // Delegation mode
             boolean inverse = false;
             String inverseAttr = classpathElement.getAttribute("inverse");
@@ -146,42 +314,49 @@
 
             // build hidden classes
             List<String> hidden = new ArrayList<String>();
-            NodeList hiddenElems = classpathElement.getElementsByTagName("hidden");
+            NodeList hiddenElems = classpathElement
+                    .getElementsByTagName("hidden");
             for (int i = 0; i < hiddenElems.getLength(); i++) {
                 Element hiddenElement = (Element) hiddenElems.item(i);
-                String pattern = ((Text) hiddenElement.getFirstChild()).getData().trim();
+                String pattern = ((Text) hiddenElement.getFirstChild())
+                        .getData().trim();
                 hidden.add(pattern);
             }
 
             // build non overridable classes
             List<String> nonOverridable = new ArrayList<String>();
-            NodeList nonOverridableElems = classpathElement.getElementsByTagName("nonOverridable");
+            NodeList nonOverridableElems = classpathElement
+                    .getElementsByTagName("nonOverridable");
             for (int i = 0; i < nonOverridableElems.getLength(); i++) {
-                Element nonOverridableElement = (Element) nonOverridableElems.item(i);
-                String pattern = ((Text) nonOverridableElement.getFirstChild()).getData().trim();
+                Element nonOverridableElement = (Element) nonOverridableElems
+                        .item(i);
+                String pattern = ((Text) nonOverridableElement.getFirstChild())
+                        .getData().trim();
                 nonOverridable.add(pattern);
             }
 
             // build the classpath
             List<String> classpath = new ArrayList<String>();
-            NodeList locations = classpathElement.getElementsByTagName("location");
+            NodeList locations = classpathElement
+                    .getElementsByTagName("location");
             for (int i = 0; i < locations.getLength(); i++) {
                 Element locationElement = (Element) locations.item(i);
-                String location = ((Text) locationElement.getFirstChild()).getData().trim();
+                String location = ((Text) locationElement.getFirstChild())
+                        .getData().trim();
                 classpath.add(location);
             }
-            
+
             // convert the paths to URLS
             URL[] urls;
             if (classpath.size() != 0) {
                 urls = new URL[classpath.size()];
-                for (ListIterator<String> iterator = classpath.listIterator(); iterator.hasNext();) {
+                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) {
+                        urls[iterator.previousIndex()] = url;
                     }
-                    urls[iterator.previousIndex()] = url;
                 }
             } else {
                 urls = getDefaultLocations();
@@ -190,12 +365,11 @@
             // create the classloader
             List<ClassLoader> parents = new ArrayList<ClassLoader>();
             parents.add(getParentClassLoader(applicationContext));
-            classLoader = new JarFileClassLoader(applicationContext.getDisplayName(),
-                                                 urls, 
-                                                 parents.toArray(new ClassLoader[parents.size()]),
-                                                 inverse,
-                                                 hidden.toArray(new String[hidden.size()]),
-                                                 nonOverridable.toArray(new String[nonOverridable.size()]));
+            classLoader = new JarFileClassLoader(applicationContext
+                    .getDisplayName(), urls, parents
+                    .toArray(new ClassLoader[parents.size()]), inverse, hidden
+                    .toArray(new String[hidden.size()]), nonOverridable
+                    .toArray(new String[nonOverridable.size()]));
 
             // remove the classpath element so Spring doesn't get confused
             document.getDocumentElement().removeChild(classpathElement);
@@ -203,7 +377,8 @@
         return classLoader;
     }
 
-    private ClassLoader getParentClassLoader(SpringApplicationContext applicationContext) {
+    private ClassLoader getParentClassLoader(
+            SpringApplicationContext applicationContext) {
         ClassLoader classLoader = applicationContext.getClassLoader();
         if (classLoader == null) {
             classLoader = Thread.currentThread().getContextClassLoader();
@@ -213,5 +388,5 @@
         }
         return classLoader;
     }
-    
+
 }
