Bug 510949: Ensure bundle can be scanned even if it is in exploded form.
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/Activator.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/Activator.java
index 7838925..b1b3e57 100644
--- a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/Activator.java
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/Activator.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2009, 2015 VMware Inc.
+ * Copyright (c) 2009, 2017 VMware Inc.
  *
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
@@ -24,18 +24,24 @@
 import org.eclipse.gemini.web.core.WebContainerProperties;
 import org.eclipse.gemini.web.core.spi.ServletContainer;
 import org.eclipse.gemini.web.tomcat.internal.bundleresources.BundleURLStreamHandlerService;
+import org.eclipse.osgi.service.urlconversion.URLConverter;
 import org.eclipse.virgo.util.osgi.ServiceRegistrationTracker;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.BundleException;
+import org.osgi.framework.Filter;
 import org.osgi.framework.ServiceRegistration;
 import org.osgi.service.url.URLConstants;
 import org.osgi.service.url.URLStreamHandlerService;
+import org.osgi.util.tracker.ServiceTracker;
 
 public class Activator implements BundleActivator {
 
     private static final String WAR_PROTOCOL = "war";
 
+    private static final String FILTER = "(&(objectClass=" + URLConverter.class.getName()
+            + ")(protocol=bundleentry))";
+
     private static final String EXPRESSION_FACTORY = "javax.el.ExpressionFactory";
 
     private static final String EXPRESSION_FACTORY_IMPL = "org.apache.el.ExpressionFactoryImpl";
@@ -46,6 +52,8 @@
 
     private TomcatServletContainer container;
 
+    private ServiceTracker<URLConverter, URLConverter> urlConverterTracker;
+
     private String oldExpressionFactory;
 
     @Override
@@ -55,6 +63,10 @@
         registerURLStreamHandler(context);
         registerConnectorDescriptors(context);
 
+        Filter filter = context.createFilter(FILTER);
+        this.urlConverterTracker = new ServiceTracker<>(context, filter, null);
+        this.urlConverterTracker.open();
+
         TomcatServletContainer container = createContainer(context);
         container.start();
 
@@ -98,6 +110,8 @@
         if (this.oldExpressionFactory != null) {
             System.setProperty(EXPRESSION_FACTORY, this.oldExpressionFactory);
         }
+
+        this.urlConverterTracker.close();
     }
 
     private TomcatServletContainer createContainer(BundleContext context) throws BundleException {
@@ -105,7 +119,7 @@
         InputStream configFile = null;
         try {
             configFile = resolveConfigFile(context);
-            return factory.createContainer(configFile, context);
+            return factory.createContainer(configFile, context, this.urlConverterTracker);
         } finally {
             if (configFile != null) {
                 try {
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/BundleDependenciesJarScanner.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/BundleDependenciesJarScanner.java
index 7cdf636..43c8bfd 100644
--- a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/BundleDependenciesJarScanner.java
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/BundleDependenciesJarScanner.java
@@ -35,9 +35,11 @@
 import org.eclipse.gemini.web.tomcat.internal.loader.BundleWebappClassLoader;
 import org.eclipse.gemini.web.tomcat.internal.support.BundleDependencyDeterminer;
 import org.eclipse.gemini.web.tomcat.internal.support.BundleFileResolver;
+import org.eclipse.osgi.service.urlconversion.URLConverter;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.FrameworkUtil;
+import org.osgi.util.tracker.ServiceTracker;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -64,13 +66,16 @@
 
     private final BundleFileResolver bundleFileResolver;
 
+    private final ServiceTracker<URLConverter, URLConverter> urlConverterTracker;
+
     private JarScanFilter jarScanFilter;
 
     BundleDependenciesJarScanner(BundleDependencyDeterminer bundleDependencyDeterminer, BundleFileResolver bundleFileResolver,
-        BundleContext bundleContext) {
+        BundleContext bundleContext, ServiceTracker<URLConverter, URLConverter> urlConverterTracker) {
         this.bundleDependencyDeterminer = bundleDependencyDeterminer;
         this.bundleFileResolver = bundleFileResolver;
         this.jarScanFilter = new BundleDependenciesJarScanFilter(bundleContext);
+        this.urlConverterTracker = urlConverterTracker;
     }
 
     @Override
@@ -117,23 +122,27 @@
         if (bundleFile != null) {
             scanBundleFile(bundleFile, callback, isWebapp);
         } else {
-            scanJarUrlConnection(bundle, callback, isWebapp);
+            URL root = bundle.getEntry("/");
+            try {
+                URLConverter converter = this.urlConverterTracker.getService();
+                if (converter != null) {
+                    root = converter.resolve(root);
+                }
+                if ("file".equals(root.getProtocol())) {
+                    scanBundleFile(new File(root.getPath()), callback, isWebapp);
+                } else if ("jar".equals(root.getProtocol())) {
+                    scanBundleUrl(root, callback, isWebapp);
+                } else {
+                    URL bundleUrl = new URL(JAR_URL_PREFIX + root.toExternalForm() + JAR_URL_SUFFIX);
+                    scanBundleUrl(bundleUrl, callback, isWebapp);
+                }
+            } catch (IOException e) {
+                LOGGER.warn("Failed to scan the bundle location [" + root + "].");
+                return;
+            }
         }
     }
 
-    private void scanJarUrlConnection(Bundle bundle, JarScannerCallback callback, boolean isWebapp) {
-        URL bundleUrl;
-        URL root = bundle.getEntry("/");
-        try {
-            bundleUrl = new URL(JAR_URL_PREFIX + root.toExternalForm() + JAR_URL_SUFFIX);
-        } catch (MalformedURLException e) {
-            LOGGER.warn("Failed to create jar: url for bundle location [" + root + "].");
-            return;
-        }
-
-        scanBundleUrl(bundleUrl, callback, isWebapp);
-    }
-
     private void scanBundleFile(File bundleFile, JarScannerCallback callback, boolean isWebapp) {
         if (bundleFile.isDirectory()) {
             try {
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/OsgiAwareEmbeddedTomcat.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/OsgiAwareEmbeddedTomcat.java
index f1c69b8..db5081a 100644
--- a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/OsgiAwareEmbeddedTomcat.java
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/OsgiAwareEmbeddedTomcat.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2009, 2016 VMware Inc.
+ * Copyright (c) 2009, 2017 VMware Inc.
  *
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
@@ -56,10 +56,12 @@
 import org.eclipse.gemini.web.tomcat.internal.loader.ChainedClassLoader;
 import org.eclipse.gemini.web.tomcat.internal.support.BundleFileResolverFactory;
 import org.eclipse.gemini.web.tomcat.internal.support.PackageAdminBundleDependencyDeterminer;
+import org.eclipse.osgi.service.urlconversion.URLConverter;
 import org.eclipse.virgo.util.osgi.ServiceRegistrationTracker;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceRegistration;
+import org.osgi.util.tracker.ServiceTracker;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.xml.sax.SAXException;
@@ -119,10 +121,11 @@
 
     private String hostConfigDir;
 
-    OsgiAwareEmbeddedTomcat(BundleContext context) {
+    OsgiAwareEmbeddedTomcat(BundleContext context,
+            ServiceTracker<URLConverter, URLConverter> urlConverterTracker) {
         this.bundleContext = context;
         this.bundleDependenciesJarScanner = new BundleDependenciesJarScanner(new PackageAdminBundleDependencyDeterminer(),
-            BundleFileResolverFactory.createBundleFileResolver(), context);
+            BundleFileResolverFactory.createBundleFileResolver(), context, urlConverterTracker);
         this.defaultJarScanner = new StandardJarScanner();
         this.jarScannerCustomizer = new DelegatingJarScannerCustomizer(context);
     }
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/TomcatServletContainerFactory.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/TomcatServletContainerFactory.java
index 531fc69..c3eba2e 100644
--- a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/TomcatServletContainerFactory.java
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/TomcatServletContainerFactory.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2009, 2012 VMware Inc.
+ * Copyright (c) 2009, 2017 VMware Inc.
  *
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
@@ -18,12 +18,15 @@
 
 import java.io.InputStream;
 
+import org.eclipse.osgi.service.urlconversion.URLConverter;
 import org.osgi.framework.BundleContext;
+import org.osgi.util.tracker.ServiceTracker;
 
 final class TomcatServletContainerFactory {
 
-    public TomcatServletContainer createContainer(InputStream configuration, BundleContext context) {
-        OsgiAwareEmbeddedTomcat catalina = new OsgiAwareEmbeddedTomcat(context);
+    public TomcatServletContainer createContainer(InputStream configuration, BundleContext context,
+            ServiceTracker<URLConverter, URLConverter> urlConverterTracker) {
+        OsgiAwareEmbeddedTomcat catalina = new OsgiAwareEmbeddedTomcat(context, urlConverterTracker);
         catalina.configure(configuration);
 
         return new TomcatServletContainer(catalina, context);
diff --git a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/support/EquinoxBundleFileResolver.java b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/support/EquinoxBundleFileResolver.java
index 2911ab5..0996d8e 100644
--- a/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/support/EquinoxBundleFileResolver.java
+++ b/org.eclipse.gemini.web.tomcat/src/main/java/org/eclipse/gemini/web/tomcat/internal/support/EquinoxBundleFileResolver.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2009, 2015 VMware Inc.
+ * Copyright (c) 2009, 2017 VMware Inc.
  *
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
@@ -59,7 +59,7 @@
         try {
             EquinoxBundleFileResolver.class.getClassLoader().loadClass(EquinoxBundle.class.getName());
             return true;
-        } catch (Exception | LinkageError _) {
+        } catch (Exception | LinkageError e) {
             return false;
         }
     }
diff --git a/org.eclipse.gemini.web.tomcat/src/test/java/org/eclipse/gemini/web/tomcat/internal/BundleDependenciesJarScannerTests.java b/org.eclipse.gemini.web.tomcat/src/test/java/org/eclipse/gemini/web/tomcat/internal/BundleDependenciesJarScannerTests.java
index 9344832..376298c 100644
--- a/org.eclipse.gemini.web.tomcat/src/test/java/org/eclipse/gemini/web/tomcat/internal/BundleDependenciesJarScannerTests.java
+++ b/org.eclipse.gemini.web.tomcat/src/test/java/org/eclipse/gemini/web/tomcat/internal/BundleDependenciesJarScannerTests.java
@@ -41,10 +41,13 @@
 import org.eclipse.gemini.web.tomcat.internal.support.BundleDependencyDeterminer;
 import org.eclipse.gemini.web.tomcat.internal.support.BundleFileResolver;
 import org.eclipse.gemini.web.tomcat.spi.ClassLoaderCustomizer;
+import org.eclipse.osgi.service.urlconversion.URLConverter;
 import org.eclipse.virgo.test.stubs.framework.StubBundle;
 import org.eclipse.virgo.test.stubs.framework.StubBundleContext;
+import org.eclipse.virgo.test.stubs.framework.StubFilter;
 import org.junit.Test;
 import org.osgi.framework.Bundle;
+import org.osgi.util.tracker.ServiceTracker;
 
 public class BundleDependenciesJarScannerTests {
 
@@ -54,8 +57,11 @@
 
     private final StubBundleContext bundleContext = new StubBundleContext();
 
+    private ServiceTracker<URLConverter, URLConverter> converter =
+            new ServiceTracker<>(this.bundleContext, createMock(StubFilter.class), null);
+
     private final BundleDependenciesJarScanner scanner = new BundleDependenciesJarScanner(this.dependencyDeterminer, this.bundleFileResolver,
-        this.bundleContext);
+        this.bundleContext, this.converter);
 
     private final Bundle bundle = new StubBundle();
 
diff --git a/org.eclipse.gemini.web.tomcat/src/test/java/org/eclipse/gemini/web/tomcat/internal/OsgiAwareEmbeddedTomcatTests.java b/org.eclipse.gemini.web.tomcat/src/test/java/org/eclipse/gemini/web/tomcat/internal/OsgiAwareEmbeddedTomcatTests.java
index 6309216..5d4406f 100755
--- a/org.eclipse.gemini.web.tomcat/src/test/java/org/eclipse/gemini/web/tomcat/internal/OsgiAwareEmbeddedTomcatTests.java
+++ b/org.eclipse.gemini.web.tomcat/src/test/java/org/eclipse/gemini/web/tomcat/internal/OsgiAwareEmbeddedTomcatTests.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2011, 2014 SAP AG
+ * Copyright (c) 2011, 2017 SAP AG
  *
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
@@ -29,6 +29,7 @@
 import javax.naming.spi.ObjectFactoryBuilder;
 
 import org.apache.catalina.LifecycleException;
+import org.eclipse.osgi.service.urlconversion.URLConverter;
 import org.eclipse.virgo.test.stubs.framework.StubBundleContext;
 import org.eclipse.virgo.test.stubs.framework.StubFilter;
 import org.eclipse.virgo.test.stubs.framework.StubServiceRegistration;
@@ -36,6 +37,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.osgi.framework.Constants;
+import org.osgi.util.tracker.ServiceTracker;
 
 public class OsgiAwareEmbeddedTomcatTests {
 
@@ -43,11 +45,14 @@
 
     private StubFilter filter;
 
+    private ServiceTracker<URLConverter, URLConverter> converter;
+
     @Before
     public void setUp() {
         this.bundleContext = new StubBundleContext();
         this.filter = createMock(StubFilter.class);
         this.bundleContext.addFilter("(objectClass=org.eclipse.gemini.web.tomcat.spi.JarScannerCustomizer)", this.filter);
+        this.converter = new ServiceTracker<>(this.bundleContext, createMock(StubFilter.class), null);
     }
 
     @After
@@ -116,7 +121,7 @@
     }
 
     private OsgiAwareEmbeddedTomcat createTomcat() {
-        return new OsgiAwareEmbeddedTomcat(this.bundleContext);
+        return new OsgiAwareEmbeddedTomcat(this.bundleContext, this.converter);
     }
 
     private void initTomcat() {
diff --git a/org.eclipse.gemini.web.tomcat/src/test/java/org/eclipse/gemini/web/tomcat/internal/TomcatServletContainerFactoryTests.java b/org.eclipse.gemini.web.tomcat/src/test/java/org/eclipse/gemini/web/tomcat/internal/TomcatServletContainerFactoryTests.java
index 78bb80c..1548074 100644
--- a/org.eclipse.gemini.web.tomcat/src/test/java/org/eclipse/gemini/web/tomcat/internal/TomcatServletContainerFactoryTests.java
+++ b/org.eclipse.gemini.web.tomcat/src/test/java/org/eclipse/gemini/web/tomcat/internal/TomcatServletContainerFactoryTests.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2009, 2015 VMware Inc.
+ * Copyright (c) 2009, 2017 VMware Inc.
  *
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
@@ -26,17 +26,23 @@
 import java.nio.file.Paths;
 
 import org.eclipse.gemini.web.core.spi.ServletContainerException;
+import org.eclipse.osgi.service.urlconversion.URLConverter;
+import org.eclipse.virgo.test.stubs.framework.StubFilter;
 import org.junit.Before;
 import org.junit.Test;
 import org.osgi.framework.BundleContext;
+import org.osgi.util.tracker.ServiceTracker;
 
 public class TomcatServletContainerFactoryTests {
 
     private BundleContext bundleContext;
 
+    private ServiceTracker<URLConverter, URLConverter> converter;
+
     @Before
     public void setUp() throws Exception {
         this.bundleContext = createMock(BundleContext.class);
+        this.converter = new ServiceTracker<>(this.bundleContext, createMock(StubFilter.class), null);
         expect(this.bundleContext.getProperty(TomcatConfigLocator.CONFIG_PATH_FRAMEWORK_PROPERTY)).andReturn(null);
         expect(this.bundleContext.getProperty(OsgiAwareEmbeddedTomcat.USE_NAMING)).andReturn(null);
         expect(this.bundleContext.getProperty(BundleDependenciesJarScanFilter.SCANNER_SKIP_BUNDLES_PROPERTY_NAME)).andReturn(null);
@@ -49,7 +55,8 @@
     public void testCreateContainerWithConfigFile() throws Exception {
         TomcatServletContainerFactory factory = new TomcatServletContainerFactory();
         replay(this.bundleContext);
-        TomcatServletContainer container = factory.createContainer(Files.newInputStream(Paths.get("src/test/resources/server.xml")), this.bundleContext);
+        TomcatServletContainer container = factory.createContainer(
+                Files.newInputStream(Paths.get("src/test/resources/server.xml")), this.bundleContext, this.converter);
         assertNotNull(container);
         verify(this.bundleContext);
     }
@@ -58,7 +65,8 @@
     public void testCreateContainerWithInvalidConfigFile() throws Exception {
         TomcatServletContainerFactory factory = new TomcatServletContainerFactory();
         replay(this.bundleContext);
-        TomcatServletContainer container = factory.createContainer(Files.newInputStream(Paths.get("src/test/resources/invalid-server.xml")), this.bundleContext);
+        TomcatServletContainer container = factory.createContainer(
+                Files.newInputStream(Paths.get("src/test/resources/invalid-server.xml")), this.bundleContext, this.converter);
         assertNotNull(container);
         verify(this.bundleContext);
     }
diff --git a/org.eclipse.gemini.web.tomcat/src/test/java/org/eclipse/gemini/web/tomcat/internal/TomcatServletContainerTests.java b/org.eclipse.gemini.web.tomcat/src/test/java/org/eclipse/gemini/web/tomcat/internal/TomcatServletContainerTests.java
index 07e9b5e..4eacede 100644
--- a/org.eclipse.gemini.web.tomcat/src/test/java/org/eclipse/gemini/web/tomcat/internal/TomcatServletContainerTests.java
+++ b/org.eclipse.gemini.web.tomcat/src/test/java/org/eclipse/gemini/web/tomcat/internal/TomcatServletContainerTests.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2015 SAP SE
+ * Copyright (c) 2012, 2017 SAP SE
  *
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
@@ -37,12 +37,15 @@
 import org.eclipse.gemini.web.core.spi.ServletContainerException;
 import org.eclipse.gemini.web.core.spi.WebApplicationHandle;
 import org.eclipse.gemini.web.tomcat.internal.loader.BundleWebappLoader;
+import org.eclipse.osgi.service.urlconversion.URLConverter;
+import org.eclipse.virgo.test.stubs.framework.StubFilter;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Filter;
+import org.osgi.util.tracker.ServiceTracker;
 
 public class TomcatServletContainerTests {
 
@@ -72,6 +75,8 @@
 
     private WebApplicationHandle webApplicationHandle;
 
+    private ServiceTracker<URLConverter, URLConverter> converter;
+
     @Before
     public void setUp() throws Exception {
         this.bundleContext = createMock(BundleContext.class);
@@ -83,6 +88,7 @@
         this.host = createMock(Host.class);
         this.servletContext = createMock(ServletContext.class);
         this.webApplicationHandle = createMock(WebApplicationHandle.class);
+        this.converter = new ServiceTracker<>(this.bundleContext, createMock(StubFilter.class), null);
 
         expect(this.bundleContext.createFilter(FILTER_1)).andReturn(this.filter);
         expect(this.bundleContext.createFilter(FILTER_2)).andReturn(this.filter);
@@ -195,7 +201,7 @@
     private TomcatServletContainer createTomcatServletContainer() {
         replay(this.bundleContext, this.bundle, this.filter, this.server, this.service, this.engine, this.host, this.servletContext,
             this.webApplicationHandle);
-        OsgiAwareEmbeddedTomcat osgiAwareEmbeddedTomcat = new OsgiAwareEmbeddedTomcat(this.bundleContext);
+        OsgiAwareEmbeddedTomcat osgiAwareEmbeddedTomcat = new OsgiAwareEmbeddedTomcat(this.bundleContext, this.converter);
         osgiAwareEmbeddedTomcat.setServer(this.server);
         return new TomcatServletContainer(osgiAwareEmbeddedTomcat, this.bundleContext);
     }