Bug 576644 - Implement BundleReference for BundleURLConnection

Change-Id: I2a4580625fb8c95a398c55c68a10b13243616953
Signed-off-by: Hannes Wellmann <wellmann.hannes1@gmx.net>
Reviewed-on: https://git.eclipse.org/r/c/equinox/rt.equinox.framework/+/186525
Tested-by: Equinox Bot <equinox-bot@eclipse.org>
Reviewed-by: Thomas Watson <tjwatson@us.ibm.com>
diff --git a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/AutomatedTests.java b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/AutomatedTests.java
index 4e4bbfe..41532b5 100644
--- a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/AutomatedTests.java
+++ b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/AutomatedTests.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2004, 2013 IBM Corporation and others.
+ * Copyright (c) 2004, 2021 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -40,7 +40,7 @@
 		BundleContextFilterTests.class, FrameworkUtilFilterTests.class, AdminPermissionTests.class,
 		ServicePermissionTests.class, PackagePermissionTests.class,
 		org.eclipse.osgi.tests.securityadmin.AllSecurityAdminTests.class,
-		org.eclipse.osgi.tests.resource.AllTests.class })
+		org.eclipse.osgi.tests.resource.AllTests.class, org.eclipse.osgi.tests.url.AllTests.class })
 
 public class AutomatedTests {
 }
diff --git a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/url/AllTests.java b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/url/AllTests.java
new file mode 100644
index 0000000..ccffc8f
--- /dev/null
+++ b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/url/AllTests.java
@@ -0,0 +1,22 @@
+/*******************************************************************************
+ * Copyright (c) 2021 Hannes Wellmann and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Hannes Wellmann - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.tests.url;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+@RunWith(Suite.class)
+@Suite.SuiteClasses({ BundleURLConnectionTest.class })
+public class AllTests {
+}
diff --git a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/url/BundleURLConnectionTest.java b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/url/BundleURLConnectionTest.java
new file mode 100644
index 0000000..ac0ef0b
--- /dev/null
+++ b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/url/BundleURLConnectionTest.java
@@ -0,0 +1,108 @@
+/*******************************************************************************
+ * Copyright (c) 2021 Hannes Wellmann and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Hannes Wellmann - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.tests.url;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Enumeration;
+import java.util.jar.JarFile;
+import org.eclipse.core.runtime.Platform;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleReference;
+import org.osgi.framework.FrameworkUtil;
+
+public class BundleURLConnectionTest {
+
+	private static Class<?> testClass;
+	private static Bundle classBundle;
+
+	@BeforeClass
+	public static void setUpBeforeClass() {
+		testClass = BundleURLConnectionTest.class;
+		classBundle = FrameworkUtil.getBundle(testClass);
+		assertNotNull("Class is not from a OSGi-bundle", classBundle);
+	}
+
+	@AfterClass
+	public static void tearDownAfterClass() {
+		testClass = null;
+		classBundle = null;
+	}
+
+	@Test
+	public void testBundleReference_classLoaderURLConnection() throws IOException {
+		URL resource = testClass.getClassLoader().getResource(JarFile.MANIFEST_NAME);
+
+		assertBundleReferenceURLConnection(resource, classBundle);
+	}
+
+	@Test
+	public void testBundleReference_otherClassLoaderURLConnection() throws IOException {
+		String resourceName = Platform.class.getName().replace(".", "/") + ".class";
+		URL resource = testClass.getClassLoader().getResource(resourceName);
+
+		assertBundleReferenceURLConnection(resource, FrameworkUtil.getBundle(Platform.class));
+	}
+
+	@Test
+	public void testBundleReference_bundleEntryURLConnection() throws IOException {
+		URL entry = classBundle.getEntry(JarFile.MANIFEST_NAME);
+
+		assertBundleReferenceURLConnection(entry, classBundle);
+	}
+
+	@Test
+	public void testBundleReference_bundleEntriesURLConnection() throws IOException {
+		Enumeration<URL> entries = classBundle.findEntries("META-INF", null, true);
+
+		while (entries.hasMoreElements()) {
+			assertBundleReferenceURLConnection(entries.nextElement(), classBundle);
+		}
+	}
+
+	@Test
+	public void testBundleReference_bundleResourceURLConnection() throws IOException {
+		URL entry = classBundle.getResource(JarFile.MANIFEST_NAME);
+
+		assertBundleReferenceURLConnection(entry, classBundle);
+	}
+
+	@Test
+	public void testBundleReference_bundleResourcesURLConnection() throws IOException {
+		Enumeration<URL> entries = classBundle.getResources(JarFile.MANIFEST_NAME);
+
+		while (entries.hasMoreElements()) {
+			assertBundleReferenceURLConnection(entries.nextElement(), classBundle);
+		}
+	}
+
+	private static void assertBundleReferenceURLConnection(URL resource, Bundle expectedBundle) throws IOException {
+		URLConnection connection = resource.openConnection();
+
+		assertThat(connection, is(instanceOf(BundleReference.class)));
+
+		Bundle bundle = ((BundleReference) connection).getBundle();
+		assertSame(expectedBundle, bundle);
+	}
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/storage/url/BundleResourceHandler.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/storage/url/BundleResourceHandler.java
index 8ffd0f5..49cf638 100644
--- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/storage/url/BundleResourceHandler.java
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/storage/url/BundleResourceHandler.java
@@ -11,6 +11,7 @@
  * Contributors:
  *     IBM Corporation - initial API and implementation
  *     Hannes Wellmann - Bug 576643: Clean up and unify Bundle resource classes
+ *     Hannes Wellmann - Bug 576644: Implement BundleReference for BundleURLConnection
  *******************************************************************************/
 
 package org.eclipse.osgi.storage.url;
@@ -155,7 +156,7 @@
 	@Override
 	protected URLConnection openConnection(URL url) throws IOException {
 		if (bundleEntry != null) { // if the bundleEntry is not null then return quick
-			return new BundleURLConnection(url, bundleEntry);
+			return new BundleURLConnection(url, container, bundleEntry);
 		}
 		String host = url.getHost();
 		if (host == null) {
@@ -178,7 +179,7 @@
 			// No admin security check was made better check now.
 			checkAuthorization(module);
 		}
-		return new BundleURLConnection(url, findBundleEntry(url, module));
+		return new BundleURLConnection(url, container, findBundleEntry(url, module));
 	}
 
 	/**
@@ -293,7 +294,7 @@
 		return bundleId + BID_FWKID_SEPARATOR + container.hashCode();
 	}
 
-	private static long parseBundleIDFromURLHost(String host) {
+	static long parseBundleIDFromURLHost(String host) {
 		int dotIndex = host.indexOf(BID_FWKID_SEPARATOR);
 		return (dotIndex >= 0 && dotIndex < host.length() - 1) ? Long.parseLong(host.substring(0, dotIndex)) : Long.parseLong(host);
 	}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/storage/url/BundleURLConnection.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/storage/url/BundleURLConnection.java
index 9040649..bb18586 100644
--- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/storage/url/BundleURLConnection.java
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/storage/url/BundleURLConnection.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2004, 2013 IBM Corporation and others.
+ * Copyright (c) 2004, 2021 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -10,6 +10,7 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Hannes Wellmann - Bug 576644: Implement BundleReference for BundleURLConnection
  *******************************************************************************/
 
 package org.eclipse.osgi.storage.url;
@@ -18,17 +19,23 @@
 import java.io.InputStream;
 import java.net.URL;
 import java.net.URLConnection;
+import org.eclipse.osgi.container.Module;
+import org.eclipse.osgi.container.ModuleContainer;
 import org.eclipse.osgi.internal.messages.Msg;
 import org.eclipse.osgi.storage.bundlefile.BundleEntry;
 import org.eclipse.osgi.util.NLS;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleReference;
 
 /**
  * URLConnection for BundleClassLoader resources.
  */
-public class BundleURLConnection extends URLConnection {
+public class BundleURLConnection extends URLConnection implements BundleReference {
 	/** BundleEntry that the URL is associated. */
 	protected final BundleEntry bundleEntry;
 
+	private final ModuleContainer container;
+
 	/** InputStream for this URLConnection. */
 	protected InputStream in;
 
@@ -41,9 +48,10 @@
 	 * @param url  URL for this URLConnection.
 	 * @param bundleEntry  BundleEntry that the URLConnection is associated.
 	 */
-	public BundleURLConnection(URL url, BundleEntry bundleEntry) {
+	public BundleURLConnection(URL url, ModuleContainer container, BundleEntry bundleEntry) {
 		super(url);
 
+		this.container = container;
 		this.bundleEntry = bundleEntry;
 		this.in = null;
 		this.contentType = null;
@@ -134,4 +142,12 @@
 	public URL getFileURL() {
 		return bundleEntry.getFileURL();
 	}
+
+	@Override
+	public Bundle getBundle() {
+		String host = url.getHost();
+		long bundleId = BundleResourceHandler.parseBundleIDFromURLHost(host);
+		Module module = container.getModule(bundleId);
+		return module != null ? module.getBundle() : null;
+	}
 }