Bug 252303 Uniqueness of bundleentry URLs with multiple frameworks in the same VM
diff --git a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/ClassLoadingBundleTests.java b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/ClassLoadingBundleTests.java
index e2b8b3e..ee428b0 100644
--- a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/ClassLoadingBundleTests.java
+++ b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/bundles/ClassLoadingBundleTests.java
@@ -11,7 +11,7 @@
 package org.eclipse.osgi.tests.bundles;
 
 import java.io.*;
-import java.net.URL;
+import java.net.*;
 import java.util.ArrayList;
 import java.util.Enumeration;
 import junit.framework.Test;
@@ -729,7 +729,7 @@
 		assertEquals("manifest number", 1, manifestURLs.size()); //$NON-NLS-1$
 		URL manifest = (URL) manifestURLs.get(0);
 		int dotIndex = manifest.getHost().indexOf('.');
-		long bundleId = dotIndex >= 0 && dotIndex < manifest.getHost().length() - 1 ? Long.parseLong(manifest.getHost().substring(dotIndex + 1)) : Long.parseLong(manifest.getHost());
+		long bundleId = dotIndex >= 0 && dotIndex < manifest.getHost().length() - 1 ? Long.parseLong(manifest.getHost().substring(0, dotIndex)) : Long.parseLong(manifest.getHost());
 		assertEquals("host id", test.getBundleId(), bundleId); //$NON-NLS-1$
 	}
 
@@ -801,6 +801,81 @@
 		assertEquals("root resource", "root classpath", readURL(entryCopy)); //$NON-NLS-1$ //$NON-NLS-2$
 	}
 
+	public void testURLExternalFormat03() throws BundleException {
+		Bundle test = installer.installBundle("test"); //$NON-NLS-1$
+		// test the external format of bundle resource URLs
+		URL entry = test.getEntry("data/resource1"); //$NON-NLS-1$
+		assertNotNull("entry", entry); //$NON-NLS-1$
+		URI uri1 = null;
+		URI uri2 = null;
+		URI uri3 = null;
+		try {
+			uri1 = new URI(entry.getProtocol(), null, entry.getHost(), entry.getPort(), entry.getPath(), null, entry.getQuery());
+			uri2 = new URI(entry.getProtocol(), entry.getHost(), entry.getPath(), entry.getQuery());
+			uri3 = new URI(entry.toExternalForm());
+		} catch (URISyntaxException e) {
+			fail("Unexpected URI exception", e); //$NON-NLS-1$
+		}
+		URL url1 = null;
+		URL url2 = null;
+		URL url3 = null;
+		URL url4 = null;
+		try {
+			url1 = uri1.toURL();
+			url2 = uri2.toURL();
+			url3 = uri3.toURL();
+			url4 = new URL(uri1.toString());
+		} catch (MalformedURLException e) {
+			fail("Unexpected URL exception", e); //$NON-NLS-1$
+		}
+		checkURL("root classpath", entry, url1); //$NON-NLS-1$
+		checkURL("root classpath", entry, url2); //$NON-NLS-1$
+		checkURL("root classpath", entry, url3); //$NON-NLS-1$
+		checkURL("root classpath", entry, url4); //$NON-NLS-1$
+	}
+
+	public void testURLExternalFormat04() throws BundleException {
+		Bundle test = installer.installBundle("test"); //$NON-NLS-1$
+		// test the external format of bundle resource URLs
+		URL entry = test.getResource("data/resource1"); //$NON-NLS-1$
+		assertNotNull("entry", entry); //$NON-NLS-1$
+		URI uri1 = null;
+		URI uri2 = null;
+		URI uri3 = null;
+		try {
+			uri1 = new URI(entry.getProtocol(), null, entry.getHost(), entry.getPort(), entry.getPath(), null, entry.getQuery());
+			uri2 = new URI(entry.getProtocol(), entry.getHost(), entry.getPath(), entry.getQuery());
+			uri3 = new URI(entry.toExternalForm());
+		} catch (URISyntaxException e) {
+			fail("Unexpected URI exception", e); //$NON-NLS-1$
+		}
+		URL url1 = null;
+		URL url2 = null;
+		URL url3 = null;
+		URL url4 = null;
+		try {
+			url1 = uri1.toURL();
+			url2 = uri2.toURL();
+			url3 = uri3.toURL();
+			url4 = new URL(uri1.toString());
+		} catch (MalformedURLException e) {
+			fail("Unexpected URL exception", e); //$NON-NLS-1$
+		}
+		checkURL("root classpath", entry, url1); //$NON-NLS-1$
+		checkURL("root classpath", entry, url2); //$NON-NLS-1$
+		checkURL("root classpath", entry, url3); //$NON-NLS-1$
+		checkURL("root classpath", entry, url4); //$NON-NLS-1$
+	}
+
+	private void checkURL(String content, URL orig, URL copy) {
+		assertEquals("external format", orig.toExternalForm(), copy.toExternalForm()); //$NON-NLS-1$
+		assertEquals(content, content, readURL(copy));
+	}
+
+	public void testURI() throws URISyntaxException {
+		new URI("bundleentry", "1", "/test", null);
+	}
+
 	public void testMultipleExportFragments01() throws Exception {
 		Bundle host = installer.installBundle("host.multiple.exports"); //$NON-NLS-1$
 		Bundle frag = installer.installBundle("frag.multiple.exports"); //$NON-NLS-1$
@@ -1145,25 +1220,29 @@
 	}
 
 	public void testResolveURLRelativeBundleResourceWithPort() throws Exception {
-		URL directory = new URL("bundleresource://82:1/dictionaries/");
+		URL directory = new URL("bundleresource://82:1/dictionaries/"); //$NON-NLS-1$
 		assertEquals(1, directory.getPort());
 
-		URL resource = new URL(directory, "en_GB.dictionary");
+		URL resource = new URL(directory, "en_GB.dictionary"); //$NON-NLS-1$
 		assertEquals(1, resource.getPort());
 	}
 
-	private String readURL(URL url) throws IOException {
+	private String readURL(URL url) {
 		StringBuffer sb = new StringBuffer();
-		BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
 		try {
-			for (String line = reader.readLine(); line != null;) {
-				sb.append(line);
-				line = reader.readLine();
-				if (line != null)
-					sb.append('\n');
+			BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
+			try {
+				for (String line = reader.readLine(); line != null;) {
+					sb.append(line);
+					line = reader.readLine();
+					if (line != null)
+						sb.append('\n');
+				}
+			} finally {
+				reader.close();
 			}
-		} finally {
-			reader.close();
+		} catch (IOException e) {
+			fail("Unexpected exception reading url: " + url.toExternalForm(), e); //$NON-NLS-1$
 		}
 		return sb.toString();
 	}
diff --git a/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/baseadaptor/BaseData.java b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/baseadaptor/BaseData.java
index 4e3b03e..a5fb914 100644
--- a/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/baseadaptor/BaseData.java
+++ b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/baseadaptor/BaseData.java
@@ -25,6 +25,7 @@
 import org.eclipse.osgi.baseadaptor.loader.BaseClassLoader;
 import org.eclipse.osgi.framework.adaptor.*;
 import org.eclipse.osgi.framework.debug.Debug;
+import org.eclipse.osgi.framework.internal.core.BundleResourceHandler;
 import org.eclipse.osgi.framework.internal.core.Constants;
 import org.eclipse.osgi.framework.internal.protocol.bundleentry.Handler;
 import org.eclipse.osgi.framework.log.FrameworkLogEntry;
@@ -113,7 +114,7 @@
 			path = '/' + path;
 		try {
 			//use the constant string for the protocol to prevent duplication
-			return new URL(Constants.OSGI_ENTRY_URL_PROTOCOL, Integer.toString(adaptor.hashCode()) + '.' + Long.toString(id), 0, path, new Handler(entry, adaptor));
+			return new URL(Constants.OSGI_ENTRY_URL_PROTOCOL, Long.toString(id) + BundleResourceHandler.BID_FWKID_SEPARATOR + Integer.toString(adaptor.hashCode()), 0, path, new Handler(entry, adaptor));
 		} catch (MalformedURLException e) {
 			return null;
 		}
diff --git a/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/baseadaptor/bundlefile/BundleFile.java b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/baseadaptor/bundlefile/BundleFile.java
index 224fe8a..8222354 100644
--- a/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/baseadaptor/bundlefile/BundleFile.java
+++ b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/baseadaptor/bundlefile/BundleFile.java
@@ -19,8 +19,7 @@
 import java.util.ArrayList;
 import java.util.Enumeration;
 import org.eclipse.osgi.baseadaptor.BaseData;
-import org.eclipse.osgi.framework.internal.core.Constants;
-import org.eclipse.osgi.framework.internal.core.FrameworkProperties;
+import org.eclipse.osgi.framework.internal.core.*;
 import org.eclipse.osgi.framework.internal.protocol.bundleresource.Handler;
 import org.eclipse.osgi.framework.util.SecureAction;
 import org.eclipse.osgi.util.ManifestElement;
@@ -158,7 +157,7 @@
 			path = '/' + path;
 		try {
 			//use the constant string for the protocol to prevent duplication
-			return secureAction.getURL(Constants.OSGI_RESOURCE_URL_PROTOCOL, Integer.toString(hostData.getAdaptor().hashCode()) + '.' + Long.toString(hostBundleID), index, path, new Handler(bundleEntry, hostData == null ? null : hostData.getAdaptor()));
+			return secureAction.getURL(Constants.OSGI_RESOURCE_URL_PROTOCOL, Long.toString(hostBundleID) + BundleResourceHandler.BID_FWKID_SEPARATOR + Integer.toString(hostData.getAdaptor().hashCode()), index, path, new Handler(bundleEntry, hostData == null ? null : hostData.getAdaptor()));
 		} catch (MalformedURLException e) {
 			return null;
 		}
diff --git a/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/core/BundleResourceHandler.java b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/core/BundleResourceHandler.java
index ae941ae..71191c0 100644
--- a/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/core/BundleResourceHandler.java
+++ b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/framework/internal/core/BundleResourceHandler.java
@@ -30,6 +30,7 @@
 public abstract class BundleResourceHandler extends URLStreamHandler implements ProtocolActivator {
 	public static final String SECURITY_CHECKED = "SECURITY_CHECKED"; //$NON-NLS-1$
 	public static final String SECURITY_UNCHECKED = "SECURITY_UNCHECKED"; //$NON-NLS-1$
+	public static final String BID_FWKID_SEPARATOR = ".fwk"; //$NON-NLS-1$
 	private BaseAdaptor adaptor;
 	protected BundleEntry bundleEntry;
 
@@ -128,7 +129,7 @@
 			authorized = SECURITY_CHECKED;
 		// Always force the use of the hash from the adaptor
 		if (adaptor != null)
-			host = Integer.toString(adaptor.hashCode()) + '.' + Long.toString(bundleId);
+			host = Long.toString(bundleId) + BID_FWKID_SEPARATOR + Integer.toString(adaptor.hashCode());
 		// Setting the authority portion of the URL to SECURITY_ATHORIZED
 		// ensures that this URL was created by using this parseURL
 		// method.  The openConnection method will only open URLs
@@ -298,6 +299,6 @@
 
 	private long getBundleID(String host) {
 		int dotIndex = host.indexOf('.');
-		return (dotIndex >= 0 && dotIndex < host.length() - 1) ? Long.parseLong(host.substring(dotIndex + 1)) : Long.parseLong(host);
+		return (dotIndex >= 0 && dotIndex < host.length() - 1) ? Long.parseLong(host.substring(0, dotIndex)) : Long.parseLong(host);
 	}
 }