FELIX-2104: Add an optional faster stax based parser

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@911818 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/bundlerepository/pom.xml b/bundlerepository/pom.xml
index e3395ae..5d62239 100644
--- a/bundlerepository/pom.xml
+++ b/bundlerepository/pom.xml
@@ -56,6 +56,11 @@
         <artifactId>org.osgi.core</artifactId>
         <version>4.1.0</version>
     </dependency>
+    <dependency>
+        <groupId>org.codehaus.woodstox</groupId>
+        <artifactId>woodstox-core-asl</artifactId>
+        <version>4.0.7</version>
+    </dependency>
   </dependencies>
   <build>
     <plugins>
@@ -68,7 +73,7 @@
           <instructions>
             <Export-Package>org.osgi.service.obr</Export-Package>
             <Private-Package>org.kxml2.*,org.xmlpull.*;-split-package:=merge-first,org.apache.felix.bundlerepository.*</Private-Package>
-            <Import-Package>!javax.xml.parsers,!org.xml.sax,org.osgi.service.log; version="1.3.0"; resolution:=optional,*</Import-Package>
+            <Import-Package>!javax.xml.parsers,!org.xml.sax,org.osgi.service.log; version="1.3.0"; resolution:=optional,javax.xml.stream;resolution:=optional,*</Import-Package>
             <DynamicImport-Package>org.apache.felix.shell</DynamicImport-Package>
             <Bundle-Activator>${pom.artifactId}.Activator</Bundle-Activator>
             <Bundle-DocURL>http://felix.apache.org/site/apache-felix-osgi-bundle-repository.html</Bundle-DocURL>
diff --git a/bundlerepository/src/main/java/org/apache/felix/bundlerepository/RepositoryImpl.java b/bundlerepository/src/main/java/org/apache/felix/bundlerepository/RepositoryImpl.java
index 0e36537..43706e5 100644
--- a/bundlerepository/src/main/java/org/apache/felix/bundlerepository/RepositoryImpl.java
+++ b/bundlerepository/src/main/java/org/apache/felix/bundlerepository/RepositoryImpl.java
@@ -43,6 +43,9 @@
 
 public class RepositoryImpl implements Repository
 {
+    public static final String OBR_PARSER_CLASS = "obr.parser.class";
+    public static final String OBR_PARSER_CLASS_DEFAULT = "org.apache.felix.bundlerepository.StaxParser";
+
     private String m_name = null;
     private long m_lastmodified = 0;
     private URL m_url = null;
@@ -179,7 +182,7 @@
         return null;
     }
 
-    private void parseRepositoryFile(int hopCount) throws Exception
+    protected void parseRepositoryFile(int hopCount) throws Exception
     {
         InputStream is = null;
         BufferedReader br = null;
@@ -222,38 +225,8 @@
 
             if (is != null)
             {
-                // Create the parser Kxml
-                XmlCommonHandler handler = new XmlCommonHandler(m_logger);
-                Object factory = new Object()
-                {
-                    public RepositoryImpl newInstance()
-                    {
-                        return RepositoryImpl.this;
-                    }
-                };
+                parseRepository(is);
 
-                // Get default setter method for Repository.
-                Method repoSetter = RepositoryImpl.class.getDeclaredMethod("put", new Class[]
-                    { Object.class, Object.class });
-
-                // Get default setter method for Resource.
-                Method resSetter = ResourceImpl.class.getDeclaredMethod("put", new Class[]
-                    { Object.class, Object.class });
-
-                // Map XML tags to types.
-                handler.addType("repository", factory, Repository.class, repoSetter);
-                handler.addType("referral", Referral.class, null, null);
-                handler.addType("resource", ResourceImpl.class, Resource.class, resSetter);
-                handler.addType("category", CategoryImpl.class, null, null);
-                handler.addType("require", RequirementImpl.class, Requirement.class, null);
-                handler.addType("capability", CapabilityImpl.class, Capability.class, null);
-                handler.addType("p", PropertyImpl.class, null, null);
-                handler.setDefaultType(String.class, null, null);
-
-                br = new BufferedReader(new InputStreamReader(is));
-                KXml2SAXParser parser;
-                parser = new KXml2SAXParser(br);
-                parser.parseXML(handler);
 
                 // resolve referrals
                 hopCount--;
@@ -289,4 +262,72 @@
             }
         }
     }
+
+    protected void parseRepository(InputStream is) throws Exception
+    {
+        RepositoryParser parser = null;
+        try
+        {
+            String className = (String) RepositoryAdminImpl.m_context.getProperty(OBR_PARSER_CLASS);
+            if (className == null || className.length() == 0) {
+                className = OBR_PARSER_CLASS_DEFAULT;
+            }
+            parser = (RepositoryParser) Class.forName(className).newInstance();
+        }
+        catch (Throwable t)
+        {
+        }
+        if (parser == null)
+        {
+            parser = new KXml2Parser();
+
+        }
+        parser.parse(this, is);
+    }
+
+    public interface RepositoryParser {
+
+        void parse(RepositoryImpl repository, InputStream is) throws Exception;
+
+    }
+
+    public static class KXml2Parser implements RepositoryParser
+    {
+        public void parse(final RepositoryImpl repository, final InputStream is) throws Exception
+        {
+            BufferedReader br;// Create the parser Kxml
+            XmlCommonHandler handler = new XmlCommonHandler(repository.m_logger);
+            Object factory = new Object()
+            {
+                public RepositoryImpl newInstance()
+                {
+                    return repository;
+                }
+            };
+
+            // Get default setter method for Repository.
+            Method repoSetter = RepositoryImpl.class.getDeclaredMethod("put", new Class[]
+                { Object.class, Object.class });
+
+            // Get default setter method for Resource.
+            Method resSetter = ResourceImpl.class.getDeclaredMethod("put", new Class[]
+                { Object.class, Object.class });
+
+            // Map XML tags to types.
+            handler.addType("repository", factory, Repository.class, repoSetter);
+            handler.addType("referral", Referral.class, null, null);
+            handler.addType("resource", ResourceImpl.class, Resource.class, resSetter);
+            handler.addType("category", CategoryImpl.class, null, null);
+            handler.addType("require", RequirementImpl.class, Requirement.class, null);
+            handler.addType("capability", CapabilityImpl.class, Capability.class, null);
+            handler.addType("p", PropertyImpl.class, null, null);
+            handler.setDefaultType(String.class, null, null);
+
+            br = new BufferedReader(new InputStreamReader(is));
+            KXml2SAXParser parser;
+            parser = new KXml2SAXParser(br);
+            parser.parseXML(handler);
+        }
+    }
+
 }
\ No newline at end of file
diff --git a/bundlerepository/src/main/java/org/apache/felix/bundlerepository/StaxParser.java b/bundlerepository/src/main/java/org/apache/felix/bundlerepository/StaxParser.java
new file mode 100644
index 0000000..5331ab8
--- /dev/null
+++ b/bundlerepository/src/main/java/org/apache/felix/bundlerepository/StaxParser.java
@@ -0,0 +1,270 @@
+/*
+ * 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.felix.bundlerepository;
+
+import java.io.InputStream;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamConstants;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+
+/**
+ * Repository XML parser based on StaX 
+ */
+public class StaxParser implements RepositoryImpl.RepositoryParser
+{
+
+    private static final String REPOSITORY = "repository";
+    private static final String NAME = "name";
+    private static final String LASTMODIFIED = "lastmodified";
+    private static final String REFERRAL = "referral";
+    private static final String RESOURCE = "resource";
+    private static final String DEPTH = "depth";
+    private static final String URL = "url";
+    private static final String CATEGORY = "category";
+    private static final String ID = "id";
+    private static final String CAPABILITY = "capability";
+    private static final String REQUIRE = "require";
+    private static final String P = "p";
+    private static final String N = "n";
+    private static final String T = "t";
+    private static final String V = "v";
+    private static final String FILTER = "filter";
+    private static final String EXTEND = "extend";
+    private static final String MULTIPLE = "multiple";
+    private static final String OPTIONAL = "optional";
+
+
+    static XMLInputFactory factory;
+
+    public StaxParser()
+    {
+        synchronized (StaxParser.class) {
+            if (factory == null) {
+                factory = XMLInputFactory.newInstance();
+                setProperty(factory, XMLInputFactory.IS_NAMESPACE_AWARE, false);
+                setProperty(factory, XMLInputFactory.IS_VALIDATING, false);
+                setProperty(factory, XMLInputFactory.IS_COALESCING, false);
+            }
+        }
+    }
+
+    protected static boolean setProperty(XMLInputFactory factory, String name, boolean value) {
+        try {
+            factory.setProperty(name, Boolean.valueOf(value));
+            return true;
+        } catch (Throwable t) {
+        }
+        return false;
+    }
+
+    public void parse(RepositoryImpl repository, InputStream is) throws Exception
+    {
+        XMLStreamReader reader = factory.createXMLStreamReader(is);
+        try {
+            int event = reader.nextTag();
+            if (event != XMLStreamConstants.START_ELEMENT || !REPOSITORY.equals(reader.getLocalName())) {
+                throw new Exception("Expected element 'repository' at the root of the document");
+            }
+            for (int i = 0, nb = reader.getAttributeCount(); i < nb; i++) {
+                String name = reader.getAttributeLocalName(i);
+                String value = reader.getAttributeValue(i);
+                if (NAME.equals(name)) {
+                    repository.setName(value);
+                } else if (LASTMODIFIED.equals(name)) {
+                    repository.setLastmodified(value);
+                }
+            }
+            while ((event = reader.nextTag()) == XMLStreamConstants.START_ELEMENT) {
+                String element = reader.getLocalName();
+                if (REFERRAL.equals(element)) {
+                    Referral referral = parseReferral(reader);
+                    repository.addReferral(referral);
+                } else if (RESOURCE.equals(element)) {
+                    ResourceImpl resource = parseResource(reader);
+                    repository.addResource(resource);
+                } else {
+                    ignoreTag(reader);
+                }
+            }
+            // Sanity check
+            sanityCheckEndElement(reader, event, REPOSITORY);
+        }
+        finally
+        {
+            reader.close();
+        }
+    }
+
+    private void sanityCheckEndElement(XMLStreamReader reader, int event, String element) {
+        if (event != XMLStreamConstants.END_ELEMENT || !element.equals(reader.getLocalName())) {
+            throw new IllegalStateException("Unexpected state while finishing element " + element);
+        }
+    }
+
+    private Referral parseReferral(XMLStreamReader reader) throws Exception {
+        Referral referral = new Referral();
+        for (int i = 0, nb = reader.getAttributeCount(); i < nb; i++) {
+            String name = reader.getAttributeLocalName(i);
+            String value = reader.getAttributeValue(i);
+            if (DEPTH.equals(name)) {
+                referral.setDepth(value);
+            } else if (URL.equals(name)) {
+                referral.setUrl(value);
+            }
+        }
+        sanityCheckEndElement(reader, reader.nextTag(), REFERRAL);
+        return referral;
+    }
+
+    private ResourceImpl parseResource(XMLStreamReader reader) throws Exception {
+        ResourceImpl resource = new ResourceImpl();
+        for (int i = 0, nb = reader.getAttributeCount(); i < nb; i++) {
+            resource.put(reader.getAttributeLocalName(i), reader.getAttributeValue(i));
+        }
+        int event;
+        while ((event = reader.nextTag()) == XMLStreamConstants.START_ELEMENT) {
+            String element = reader.getLocalName();
+            if (CATEGORY.equals(element)) {
+                CategoryImpl category = parseCategory(reader);
+                resource.addCategory(category);
+            } else if (CAPABILITY.equals(element)) {
+                CapabilityImpl capability = parseCapability(reader);
+                resource.addCapability(capability);
+            } else if (REQUIRE.equals(element)) {
+                RequirementImpl requirement = parseRequire(reader);
+                resource.addRequire(requirement);
+            } else {
+                ignoreTag(reader);
+            }
+        }
+        // Sanity check
+        if (event != XMLStreamConstants.END_ELEMENT || !RESOURCE.equals(reader.getLocalName())) {
+            throw new Exception("Unexpected state");
+        }
+        return resource;
+    }
+
+    private CategoryImpl parseCategory(XMLStreamReader reader) throws XMLStreamException {
+        CategoryImpl category = new CategoryImpl();
+        for (int i = 0, nb = reader.getAttributeCount(); i < nb; i++) {
+            if (ID.equals(reader.getAttributeLocalName(i))) {
+               category.setId(reader.getAttributeValue(i));
+            };
+        }
+        sanityCheckEndElement(reader, reader.nextTag(), CATEGORY);
+        return category;
+    }
+
+    private CapabilityImpl parseCapability(XMLStreamReader reader) throws Exception {
+        CapabilityImpl capability = new CapabilityImpl();
+        for (int i = 0, nb = reader.getAttributeCount(); i < nb; i++) {
+            String name = reader.getAttributeLocalName(i);
+            String value = reader.getAttributeValue(i);
+            if (NAME.equals(name)) {
+                capability.setName(value);
+            }
+        }
+        int event;
+        while ((event = reader.nextTag()) == XMLStreamConstants.START_ELEMENT) {
+            String element = reader.getLocalName();
+            if (P.equals(element)) {
+                PropertyImpl prop = parseProperty(reader);
+                capability.addP(prop);
+            } else {
+                ignoreTag(reader);
+            }
+        }
+        // Sanity check
+        sanityCheckEndElement(reader, event, CAPABILITY);
+        return capability;
+    }
+
+    private PropertyImpl parseProperty(XMLStreamReader reader) throws Exception {
+        String n = null, t = null, v = null;
+        for (int i = 0, nb = reader.getAttributeCount(); i < nb; i++) {
+            String name = reader.getAttributeLocalName(i);
+            String value = reader.getAttributeValue(i);
+            if (N.equals(name)) {
+                n = value;
+            } else if (T.equals(name)) {
+                t = value;
+            } else if (V.equals(name)) {
+                v = value;
+            }
+        }
+        PropertyImpl prop = new PropertyImpl(n, t, v);
+        // Sanity check
+        sanityCheckEndElement(reader, reader.nextTag(), P);
+        return prop;
+    }
+
+    private RequirementImpl parseRequire(XMLStreamReader reader) throws Exception {
+        RequirementImpl requirement = new RequirementImpl();
+        for (int i = 0, nb = reader.getAttributeCount(); i < nb; i++) {
+            String name = reader.getAttributeLocalName(i);
+            String value = reader.getAttributeValue(i);
+            if (NAME.equals(name)) {
+                requirement.setName(value);
+            } else if (FILTER.equals(name)) {
+                requirement.setFilter(value);
+            } else if (EXTEND.equals(name)) {
+                requirement.setExtend(value);
+            } else if (MULTIPLE.equals(name)) {
+                requirement.setMultiple(value);
+            } else if (OPTIONAL.equals(name)) {
+                requirement.setOptional(value);
+            }
+        }
+        int event;
+        StringBuffer sb = null;
+        while ((event = reader.next()) != XMLStreamConstants.END_ELEMENT) {
+            switch (event) {
+                case XMLStreamConstants.START_ELEMENT:
+                    throw new Exception("Unexpected element inside <require/> element");
+                case XMLStreamConstants.CHARACTERS:
+                    if (sb == null) {
+                        sb = new StringBuffer();
+                    }
+                    sb.append(reader.getText());
+                    break;
+            }
+        }
+        if (sb != null) {
+            requirement.addText(sb.toString());
+        }
+        // Sanity check
+        sanityCheckEndElement(reader, event, REQUIRE);
+        return requirement;
+    }
+
+    private void ignoreTag(XMLStreamReader reader) throws XMLStreamException {
+        int level = 1;
+        int event = 0;
+        while (level > 0) {
+            event = reader.next();
+            if (event == XMLStreamConstants.START_ELEMENT) {
+                level++;
+            } else if (event == XMLStreamConstants.END_ELEMENT) {
+                level--;
+            }
+        }
+    }
+
+}
diff --git a/bundlerepository/src/test/java/org/apache/felix/bundlerepository/MockBundleContext.java b/bundlerepository/src/test/java/org/apache/felix/bundlerepository/MockBundleContext.java
index cc394d5..350f587 100644
--- a/bundlerepository/src/test/java/org/apache/felix/bundlerepository/MockBundleContext.java
+++ b/bundlerepository/src/test/java/org/apache/felix/bundlerepository/MockBundleContext.java
@@ -20,8 +20,8 @@
 
 import java.io.File;
 import java.io.InputStream;
-import java.net.URL;
 import java.util.Dictionary;
+import java.util.Properties;
 
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
@@ -34,6 +34,13 @@
 
 public class MockBundleContext implements BundleContext
 {
+    private Properties props = new Properties();
+
+    public void setProperty(String key, String val)
+    {
+        props.setProperty(key, val);
+    }
+
     public void addBundleListener(BundleListener arg0)
     {
     }
@@ -99,16 +106,7 @@
 
     public String getProperty(String name)
     {
-        if (RepositoryAdminImpl.REPOSITORY_URL_PROP.equals(name))
-        {
-            URL url = getClass().getResource("/referred.xml");
-            if (url != null)
-            {
-                return url.toExternalForm();
-            }
-        }
-
-        return null;
+        return props.getProperty(name);
     }
 
     public Object getService(ServiceReference arg0)
diff --git a/bundlerepository/src/test/java/org/apache/felix/bundlerepository/RepositoryImplTest.java b/bundlerepository/src/test/java/org/apache/felix/bundlerepository/RepositoryImplTest.java
index b4cf628..0413378 100644
--- a/bundlerepository/src/test/java/org/apache/felix/bundlerepository/RepositoryImplTest.java
+++ b/bundlerepository/src/test/java/org/apache/felix/bundlerepository/RepositoryImplTest.java
@@ -18,21 +18,9 @@
  */
 package org.apache.felix.bundlerepository;
 
-import java.io.File;
-import java.io.InputStream;
 import java.net.URL;
-import java.util.Dictionary;
 
 import junit.framework.TestCase;
-
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.BundleListener;
-import org.osgi.framework.Filter;
-import org.osgi.framework.FrameworkListener;
-import org.osgi.framework.ServiceListener;
-import org.osgi.framework.ServiceReference;
-import org.osgi.framework.ServiceRegistration;
 import org.osgi.service.obr.Repository;
 import org.osgi.service.obr.Resource;
 
@@ -107,135 +95,4 @@
         return repoAdmin;
     }
 
-    private static class MockBundleContext implements BundleContext
-    {
-        public void addBundleListener(BundleListener arg0)
-        {
-        }
-
-        public void addFrameworkListener(FrameworkListener arg0)
-        {
-        }
-
-        public void addServiceListener(ServiceListener arg0)
-        {
-        }
-
-        public void addServiceListener(ServiceListener arg0, String arg1)
-        {
-        }
-
-        public Filter createFilter(String arg0)
-        {
-            // returns a match-all filter always
-            return new Filter()
-            {
-                public boolean matchCase(Dictionary arg0)
-                {
-                    return true;
-                }
-
-                public boolean match(Dictionary arg0)
-                {
-                    return true;
-                }
-
-                public boolean match(ServiceReference arg0)
-                {
-                    return true;
-                }
-            };
-        }
-
-        public ServiceReference[] getAllServiceReferences(String arg0, String arg1)
-        {
-            return null;
-        }
-
-        public Bundle getBundle()
-        {
-            return null;
-        }
-
-        public Bundle getBundle(long arg0)
-        {
-            return null;
-        }
-
-        public Bundle[] getBundles()
-        {
-            return null;
-        }
-
-        public File getDataFile(String arg0)
-        {
-            return null;
-        }
-
-        public String getProperty(String name)
-        {
-            if (RepositoryAdminImpl.REPOSITORY_URL_PROP.equals(name))
-            {
-                URL url = getClass().getResource("/referred.xml");
-                if (url != null)
-                {
-                    return url.toExternalForm();
-                }
-            }
-
-            return null;
-        }
-
-        public Object getService(ServiceReference arg0)
-        {
-            return null;
-        }
-
-        public ServiceReference getServiceReference(String arg0)
-        {
-            return null;
-        }
-
-        public ServiceReference[] getServiceReferences(String arg0, String arg1)
-        {
-            return null;
-        }
-
-        public Bundle installBundle(String arg0)
-        {
-            return null;
-        }
-
-        public Bundle installBundle(String arg0, InputStream arg1)
-        {
-            return null;
-        }
-
-        public ServiceRegistration registerService(String[] arg0, Object arg1, Dictionary arg2)
-        {
-            return null;
-        }
-
-        public ServiceRegistration registerService(String arg0, Object arg1, Dictionary arg2)
-        {
-            return null;
-        }
-
-        public void removeBundleListener(BundleListener arg0)
-        {
-        }
-
-        public void removeFrameworkListener(FrameworkListener arg0)
-        {
-        }
-
-        public void removeServiceListener(ServiceListener arg0)
-        {
-        }
-
-        public boolean ungetService(ServiceReference arg0)
-        {
-            return false;
-        }
-    }
 }
\ No newline at end of file
diff --git a/bundlerepository/src/test/java/org/apache/felix/bundlerepository/ResolverImplTest.java b/bundlerepository/src/test/java/org/apache/felix/bundlerepository/ResolverImplTest.java
index c58f152..293ef34 100644
--- a/bundlerepository/src/test/java/org/apache/felix/bundlerepository/ResolverImplTest.java
+++ b/bundlerepository/src/test/java/org/apache/felix/bundlerepository/ResolverImplTest.java
@@ -62,6 +62,8 @@
     private RepositoryAdminImpl createRepositoryAdmin()
     {
         final MockBundleContext bundleContext = new MockBundleContext();
+        bundleContext.setProperty(RepositoryAdminImpl.REPOSITORY_URL_PROP,
+                                  getClass().getResource("/referred.xml").toExternalForm());
         RepositoryAdminImpl repoAdmin = new RepositoryAdminImpl(bundleContext, new Logger(bundleContext));
 
         // force initialization && remove all initial repositories
diff --git a/bundlerepository/src/test/java/org/apache/felix/bundlerepository/StaxParserTest.java b/bundlerepository/src/test/java/org/apache/felix/bundlerepository/StaxParserTest.java
new file mode 100644
index 0000000..9545727
--- /dev/null
+++ b/bundlerepository/src/test/java/org/apache/felix/bundlerepository/StaxParserTest.java
@@ -0,0 +1,138 @@
+/*
+ * 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.felix.bundlerepository;
+
+import java.io.File;
+import java.net.URL;
+
+import junit.framework.TestCase;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.osgi.service.obr.Repository;
+import org.osgi.service.obr.Resolver;
+import org.osgi.service.obr.Resource;
+
+public class StaxParserTest extends TestCase
+{
+    public void testStaxParser() throws Exception
+    {
+
+        URL url = getClass().getResource("/repo_for_resolvertest.xml");
+        RepositoryAdminImpl repoAdmin = createRepositoryAdmin(StaxParser.class);
+        RepositoryImpl repo = (RepositoryImpl) repoAdmin.addRepository(url);
+
+        Resolver resolver = repoAdmin.resolver();
+
+        Resource r = null;
+        //MockContext doesn't support filtering!
+        Resource[] discoverResources = repoAdmin.discoverResources("");
+        for (int i = 0; i < discoverResources.length; i++) {
+            Resource resource = discoverResources[i];
+            if (resource.getSymbolicName().contains("org.apache.felix.test"))
+            {
+                r = resource;
+            }
+        }
+        assertNotNull(r);
+
+        resolver.add(r);
+        assertTrue(resolver.resolve());
+
+    }
+
+    public void testPerfs() throws Exception {
+//        for (int i = 0; i < 10; i++) {
+//            testPerfs(new File(System.getProperty("user.home"), ".m2/repository/repository.xml").toURI().toURL(), 0, 100);
+//        }
+    }
+
+    protected void testPerfs(URL url, int nbWarm, int nbTest) throws Exception
+    {
+        long t0, t1;
+
+        StaxParser.factory = null;
+        System.setProperty("javax.xml.stream.XMLInputFactory", "com.ctc.wstx.stax.WstxInputFactory");
+        for (int i = 0; i < nbWarm; i++) {
+            RepositoryAdminImpl repoAdmin = createRepositoryAdmin(StaxParser.class);
+            RepositoryImpl repo = (RepositoryImpl) repoAdmin.addRepository(url);
+        }
+        t0 = System.currentTimeMillis();
+        for (int i = 0; i < nbTest; i++) {
+            RepositoryAdminImpl repoAdmin = createRepositoryAdmin(StaxParser.class);
+            RepositoryImpl repo = (RepositoryImpl) repoAdmin.addRepository(url);
+        }
+        t1 = System.currentTimeMillis();
+        System.err.println("Woodstox: " + (t1 - t0) + " ms");
+
+
+        StaxParser.factory = null;
+        System.setProperty("javax.xml.stream.XMLInputFactory", "com.sun.xml.internal.stream.XMLInputFactoryImpl");
+        for (int i = 0; i < nbWarm; i++) {
+            RepositoryAdminImpl repoAdmin = createRepositoryAdmin(StaxParser.class);
+            RepositoryImpl repo = (RepositoryImpl) repoAdmin.addRepository(url);
+        }
+        t0 = System.currentTimeMillis();
+        for (int i = 0; i < nbTest; i++) {
+            RepositoryAdminImpl repoAdmin = createRepositoryAdmin(StaxParser.class);
+            RepositoryImpl repo = (RepositoryImpl) repoAdmin.addRepository(url);
+        }
+        t1 = System.currentTimeMillis();
+        System.err.println("DefStax: " + (t1 - t0) + " ms");
+
+        for (int i = 0; i < nbWarm; i++) {
+            RepositoryAdminImpl repoAdmin = createRepositoryAdmin(RepositoryImpl.KXml2Parser.class);
+            RepositoryImpl repo = (RepositoryImpl) repoAdmin.addRepository(url);
+        }
+        t0 = System.currentTimeMillis();
+        for (int i = 0; i < nbTest; i++) {
+            RepositoryAdminImpl repoAdmin = createRepositoryAdmin(RepositoryImpl.KXml2Parser.class);
+            RepositoryImpl repo = (RepositoryImpl) repoAdmin.addRepository(url);
+        }
+        t1 = System.currentTimeMillis();
+        System.err.println("KXmlParser: " + (t1 - t0) + " ms");
+
+
+    }
+
+    public static void main(String[] args) throws Exception {
+
+        new StaxParserTest().testStaxParser();
+
+    }
+
+    private RepositoryAdminImpl createRepositoryAdmin(Class repositoryParser)
+    {
+        final MockBundleContext bundleContext = new MockBundleContext();
+        bundleContext.setProperty(RepositoryAdminImpl.REPOSITORY_URL_PROP,
+                                  getClass().getResource("/referral1_repository.xml").toExternalForm());
+        bundleContext.setProperty(RepositoryImpl.OBR_PARSER_CLASS,
+                                  repositoryParser.getName());
+        RepositoryAdminImpl repoAdmin = new RepositoryAdminImpl(bundleContext, new Logger(bundleContext));
+
+        // force initialization && remove all initial repositories
+        Repository[] repos = repoAdmin.listRepositories();
+        for (int i = 0; repos != null && i < repos.length; i++)
+        {
+            repoAdmin.removeRepository(repos[i].getURL());
+        }
+
+        return repoAdmin;
+    }
+
+}
\ No newline at end of file