/* * Copyright 2013 The Apache Software Foundation. * * Licensed 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.framework; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.InetAddress; import java.net.URL; import java.net.URLClassLoader; import java.net.URLConnection; import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; import java.util.jar.JarOutputStream; import java.util.jar.Manifest; import java.util.zip.ZipEntry; import junit.framework.TestCase; import org.osgi.framework.Bundle; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.Constants; import org.osgi.framework.ServiceRegistration; import org.osgi.framework.launch.Framework; import org.osgi.service.url.URLConstants; import org.osgi.service.url.URLStreamHandlerService; import org.osgi.service.url.URLStreamHandlerSetter; /** * * @author pauls */ public class URLHandlersTest extends TestCase { public void testURLHandlers() throws Exception { String mf = "Bundle-SymbolicName: url.test\n" + "Bundle-Version: 1.0.0\n" + "Bundle-ManifestVersion: 2\n" + "Import-Package: org.osgi.framework,org.osgi.service.url\n" + "Manifest-Version: 1.0\n" + Constants.BUNDLE_ACTIVATOR + ": " + TestURLHandlersActivator.class.getName() + "\n\n"; File bundleFile = createBundle(mf, TestURLHandlersActivator.class); Framework f = createFramework(); f.init(); f.start(); try { final Bundle bundle = f.getBundleContext().installBundle(bundleFile.toURI().toString()); bundle.start(); } finally { try { f.stop(); } catch (Throwable t) { } } } public void testURLHandlersWithClassLoaderIsolation() throws Exception { DelegatingClassLoader cl1 = new DelegatingClassLoader(this.getClass().getClassLoader()); DelegatingClassLoader cl2 = new DelegatingClassLoader(this.getClass().getClassLoader()); Framework f = createFramework(); f.init(); f.start(); String mf = "Bundle-SymbolicName: url.test\n" + "Bundle-Version: 1.0.0\n" + "Bundle-ManifestVersion: 2\n" + "Import-Package: org.osgi.framework\n" + "Manifest-Version: 1.0\n" + Constants.BUNDLE_ACTIVATOR + ": " + TestURLHandlersActivator.class.getName(); File bundleFile = createBundle(mf, TestURLHandlersActivator.class); final Bundle bundle = f.getBundleContext().installBundle(bundleFile.toURI().toString()); bundle.start(); Class clazz1 = cl1.loadClass(URLHandlersTest.class.getName()); clazz1.getMethod("testURLHandlers").invoke(clazz1.newInstance()); bundle.stop(); bundle.start(); Class clazz2 = cl2.loadClass(URLHandlersTest.class.getName()); clazz2.getMethod("testURLHandlers").invoke(clazz2.newInstance()); bundle.stop(); bundle.start(); f.stop(); } public static class DelegatingClassLoader extends ClassLoader { private final Object m_lock = new Object(); private final ClassLoader m_source; public DelegatingClassLoader(ClassLoader source) { m_source = source; } @Override public Class<?> loadClass(String name) throws ClassNotFoundException { synchronized (m_lock) { Class<?> result = findLoadedClass(name); if (result != null) { return result; } } if (!name.startsWith("org.apache.felix") && !name.startsWith("org.osgi.")) { return m_source.loadClass(name); } byte[] buffer = new byte[8 * 1024]; try { InputStream is = m_source.getResourceAsStream(name.replace('.', '/') + ".class"); ByteArrayOutputStream os = new ByteArrayOutputStream(); for (int i = is.read(buffer); i != -1; i = is.read(buffer)) { os.write(buffer, 0, i); } is.close(); os.close(); buffer = os.toByteArray(); } catch (Exception ex) { throw new ClassNotFoundException("Unable to load class: " + name + " with cl: " + System.identityHashCode(this), ex); } return super.defineClass(name, buffer, 0, buffer.length, null); } } public static class TestURLHandlersActivator implements BundleActivator, URLStreamHandlerService { private volatile ServiceRegistration m_reg = null; public URLConnection openConnection(URL u) throws IOException { return null;//throw new UnsupportedOperationException("Not supported yet."); } public void parseURL(URLStreamHandlerSetter realHandler, URL u, String spec, int start, int limit) { realHandler.setURL(u, spec, spec, start, spec, spec, spec, spec, spec); //throw new UnsupportedOperationException("Not supported yet."); } public String toExternalForm(URL u) { return u.toString();//throw new UnsupportedOperationException("Not supported yet."); } public boolean equals(URL u1, URL u2) { throw new UnsupportedOperationException("Not supported yet."); } public int getDefaultPort() { throw new UnsupportedOperationException("Not supported yet."); } public InetAddress getHostAddress(URL u) { throw new UnsupportedOperationException("Not supported yet."); } public int hashCode(URL u) { throw new UnsupportedOperationException("Not supported yet."); } public boolean hostsEqual(URL u1, URL u2) { throw new UnsupportedOperationException("Not supported yet."); } public boolean sameFile(URL u1, URL u2) { throw new UnsupportedOperationException("Not supported yet."); } public void start(final BundleContext context) throws Exception { try { new URL("test" + System.identityHashCode(TestURLHandlersActivator.this) + ":").openConnection(); throw new Exception("Unexpected url resolve"); } catch (Exception ex) { // pass } Hashtable props = new Hashtable<String, String>(); props.put(URLConstants.URL_HANDLER_PROTOCOL, "test" + System.identityHashCode(TestURLHandlersActivator.this)); ServiceRegistration reg = context.registerService(URLStreamHandlerService.class, this, props); new URL("test" + System.identityHashCode(TestURLHandlersActivator.this) + ":").openConnection(); reg.unregister(); try { new URL("test" + System.identityHashCode(TestURLHandlersActivator.this) + ":").openConnection(); throw new Exception("Unexpected url resolve"); } catch (Exception ex) { // pass } Bundle bundle2 = null; if (context.getBundle().getSymbolicName().equals("url.test")) { String mf = "Bundle-SymbolicName: url.test2\n" + "Bundle-Version: 1.0.0\n" + "Bundle-ManifestVersion: 2\n" + "Import-Package: org.osgi.framework,org.osgi.service.url\n" + "Manifest-Version: 1.0\n" + Constants.BUNDLE_ACTIVATOR + ": " + TestURLHandlersActivator.class.getName() + "\n\n"; File bundleFile = createBundle(mf, TestURLHandlersActivator.class); bundle2 = context.installBundle(bundleFile.toURI().toURL().toString()); } if (bundle2 != null) { try { new URL("test" + System.identityHashCode(bundle2) + ":").openConnection(); throw new Exception("Unexpected url2 resolve"); } catch (Exception ex) { } bundle2.start(); new URL("test" + System.identityHashCode(bundle2) + ":").openConnection(); bundle2.stop(); try { new URL("test" + System.identityHashCode(bundle2) + ":").openConnection(); throw new Exception("Unexpected url2 resolve"); } catch (Exception ex) { } } else { try { new URL("test" + System.identityHashCode(context.getBundle()) + ":").openConnection(); throw new Exception("Unexpected url2 resolve"); } catch (Exception ex) { } props = new Hashtable(); props.put(URLConstants.URL_HANDLER_PROTOCOL, "test" + System.identityHashCode(context.getBundle())); m_reg = context.registerService(URLStreamHandlerService.class, this, props); new URL("test" + System.identityHashCode(context.getBundle()) + ":").openConnection(); } } private static File createBundle(String manifest, Class... classes) throws IOException { File f = File.createTempFile("felix-bundle", ".jar"); f.deleteOnExit(); Manifest mf = new Manifest(new ByteArrayInputStream(manifest.getBytes("utf-8"))); JarOutputStream os = new JarOutputStream(new FileOutputStream(f), mf); for (Class clazz : classes) { String path = clazz.getName().replace('.', '/') + ".class"; os.putNextEntry(new ZipEntry(path)); InputStream is = clazz.getClassLoader().getResourceAsStream(path); byte[] buffer = new byte[8 * 1024]; for (int i = is.read(buffer); i != -1; i = is.read(buffer)) { os.write(buffer, 0, i); } is.close(); os.closeEntry(); } os.close(); return f; } public void stop(BundleContext context) throws Exception { if (m_reg != null) { m_reg.unregister(); } } } private static File createBundle(String manifest, Class... classes) throws IOException { File f = File.createTempFile("felix-bundle", ".jar"); f.deleteOnExit(); Manifest mf = new Manifest(new ByteArrayInputStream(manifest.getBytes("utf-8"))); JarOutputStream os = new JarOutputStream(new FileOutputStream(f), mf); for (Class clazz : classes) { String path = clazz.getName().replace('.', '/') + ".class"; os.putNextEntry(new ZipEntry(path)); InputStream is = clazz.getClassLoader().getResourceAsStream(path); byte[] buffer = new byte[8 * 1024]; for (int i = is.read(buffer); i != -1; i = is.read(buffer)) { os.write(buffer, 0, i); } is.close(); os.closeEntry(); } os.close(); return f; } private static Felix createFramework() throws Exception { Map params = new HashMap(); params.put(Constants.FRAMEWORK_SYSTEMPACKAGES, "org.osgi.framework; version=1.4.0," + "org.osgi.service.packageadmin; version=1.2.0," + "org.osgi.service.startlevel; version=1.1.0," + "org.osgi.util.tracker; version=1.3.3," + "org.osgi.service.url; version=1.0.0"); File cacheDir = File.createTempFile("felix-cache", ".dir"); if (!cacheDir.delete() || !cacheDir.mkdirs()) { fail("Unable to set-up cache dir"); } String cache = cacheDir.getPath(); params.put("felix.cache.profiledir", cache); params.put("felix.cache.dir", cache); params.put(Constants.FRAMEWORK_STORAGE, cache); return new Felix(params); } }