/* * #%L * Gravia :: Runtime :: OSGi * %% * Copyright (C) 2013 - 2014 JBoss by Red Hat * %% * 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. * #L% */ package org.jboss.gravia.runtime.osgi.internal; import static org.jboss.gravia.runtime.spi.RuntimeLogger.LOGGER; import java.io.IOException; import java.net.URL; import java.net.URLConnection; import java.net.URLStreamHandler; import java.util.ArrayList; import java.util.Collections; import java.util.Dictionary; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.List; import java.util.Map; import org.jboss.gravia.Constants; import org.jboss.gravia.resource.Attachable; import org.jboss.gravia.resource.DefaultResourceBuilder; import org.jboss.gravia.resource.DictionaryResourceBuilder; import org.jboss.gravia.resource.Resource; import org.jboss.gravia.resource.ResourceBuilder; import org.jboss.gravia.runtime.Module; import org.jboss.gravia.runtime.ModuleContext; import org.jboss.gravia.runtime.ModuleException; import org.jboss.gravia.runtime.Runtime; import org.jboss.gravia.runtime.ServiceReference; import org.jboss.gravia.runtime.ServiceRegistration; import org.jboss.gravia.runtime.spi.AbstractModule; import org.jboss.gravia.runtime.spi.AbstractRuntime; import org.jboss.gravia.runtime.spi.ModuleEntriesProvider; import org.jboss.gravia.runtime.spi.PropertiesProvider; import org.jboss.gravia.runtime.spi.ThreadResourceAssociation; import org.jboss.gravia.runtime.spi.URLStreamHandlerTracker; import org.jboss.gravia.utils.IllegalArgumentAssertion; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleEvent; import org.osgi.framework.BundleListener; import org.osgi.framework.SynchronousBundleListener; import org.osgi.framework.wiring.BundleWiring; import org.osgi.service.url.AbstractURLStreamHandlerService; import org.osgi.service.url.URLStreamHandlerService; /** * The OSGi {@link Runtime} * * @author thomas.diesler@jboss.com * @since 27-Sep-2013 */ public final class OSGiRuntime extends AbstractRuntime { private final BundleContext context; private final BundleListener installListener; private final URLStreamHandlerTracker streamHandlerTracker; private final Map<Long, Module> uninstalled = new HashMap<Long, Module>(); private final Map<Long, Module> unresolved = new HashMap<Long, Module>(); public OSGiRuntime(BundleContext context, PropertiesProvider propertiesProvider) { super(propertiesProvider); IllegalArgumentAssertion.assertNotNull(context, "context"); this.context = context; // Install system module Bundle sysbundle = context.getBundle(0); try { Resource resource = new DefaultResourceBuilder().addIdentityCapability(getSystemIdentity()).getResource(); BundleWiring wiring = sysbundle.adapt(BundleWiring.class); installModule(wiring.getClassLoader(), resource, sysbundle.getHeaders(), null); } catch (ModuleException ex) { throw new IllegalStateException("Cannot install system module", ex); } // Create the {@link URLStreamHandlerTracker} streamHandlerTracker = createURLStreamHandlerTracker(getModuleContext()); // Create the bundle resolve/uninstall listener installListener = createBundleInstallListener(); } private URLStreamHandlerTracker createURLStreamHandlerTracker(ModuleContext moduleContext) { URLStreamHandlerTracker tracker = new URLStreamHandlerTracker(moduleContext) { private Map<ServiceReference<?>, ServiceRegistration<?>> registrations = new HashMap<>(); @Override protected void addingHandler(String protocol, ServiceReference<URLStreamHandler> sref, final URLStreamHandler handler) { URLStreamHandlerService service = new AbstractURLStreamHandlerService() { @Override public URLConnection openConnection(URL url) throws IOException { return new URL(null, url.toExternalForm(), handler).openConnection(); } }; Dictionary<String, Object> props = new Hashtable<>(); for (String key : sref.getPropertyKeys()) { if (!key.equals(Constants.SERVICE_ID)) { props.put(key, sref.getProperty(key)); } } ServiceRegistration<URLStreamHandlerService> sreg = getModuleContext().registerService(URLStreamHandlerService.class, service, props); registrations.put(sref, sreg); } @Override protected void removingHandler(String protocol, ServiceReference<URLStreamHandler> sref, URLStreamHandler handler) { ServiceRegistration<?> sreg = registrations.remove(sref); if (sreg != null) { sreg.unregister(); } } }; return tracker; } private BundleListener createBundleInstallListener() { return new SynchronousBundleListener() { @Override public void bundleChanged(BundleEvent event) { int eventType = event.getType(); Bundle bundle = event.getBundle(); if (!isFragment(bundle)) { if (eventType == BundleEvent.RESOLVED) { installModule(bundle); } else if (eventType == BundleEvent.UNRESOLVED) { Module module = getModule(bundle); if (module != null) { synchronized (unresolved) { unresolved.put(bundle.getBundleId(), module); uninstallModule(module); } } } else if (eventType == BundleEvent.UNINSTALLED) { Module module = getModule(bundle); if (module != null) { synchronized (unresolved) { unresolved.remove(bundle.getBundleId()); } synchronized (uninstalled) { uninstalled.put(bundle.getBundleId(), module); uninstallModule(module); } } } } } }; } private boolean isFragment(Bundle bundle) { String host = bundle.getHeaders().get("Fragment-Host"); return host != null; } Module getModuleInternal(long id, boolean all) { Module module = getModule(id); if (all && module == null) { synchronized (unresolved) { module = unresolved.get(id); } } if (all && module == null) { synchronized (uninstalled) { module = uninstalled.get(id); } } return module; } Module getModule(Bundle bundle) { Module module = super.getModule(bundle.getBundleId()); if (module == null && bundle.getState() == Bundle.UNINSTALLED) { synchronized (uninstalled) { module = uninstalled.get(bundle.getBundleId()); // Cleanup uninstalled modules for which there is no bundle any more for (Long bundleId : new HashSet<Long>(uninstalled.keySet())) { if (context.getBundle(bundleId) == null) { uninstalled.remove(bundleId); } } } } return module; } @Override public void init() { // Register the Runtime service context.registerService(Runtime.class, this, null); // Start tracking the URLStreamHandler services streamHandlerTracker.open(); // Setup the bundle tracker context.addBundleListener(installListener); // Install already existing bundles for (Bundle bundle : context.getBundles()) { installModule(bundle); } } BundleContext getSystemContext() { return context.getBundle(0).getBundleContext(); } @Override protected AbstractModule createModule(ClassLoader classLoader, Resource resource, Dictionary<String, String> headers, Attachable context) { return new ModuleAdaptor(this, classLoader, resource, headers); } @Override protected ModuleEntriesProvider getDefaultEntriesProvider(Module module, Attachable context) { return new OSGiModuleEntriesProvider(module); } @Override protected void uninstallModule(Module module) { super.uninstallModule(module); } private Module installModule(Bundle bundle) { Module module = getModule(bundle.getBundleId()); if (module != null) return module; BundleWiring wiring = bundle.adapt(BundleWiring.class); ClassLoader classLoader = wiring != null ? wiring.getClassLoader() : null; if (classLoader == null) return null; Resource resource = ThreadResourceAssociation.getResource(); Dictionary<String, String> headers = bundle.getHeaders(); if (resource == null) { ResourceBuilder builder = new DictionaryResourceBuilder().load(headers); if (builder.isValid() == false) { String symbolicName = bundle.getSymbolicName(); String version = bundle.getVersion().toString(); builder.addIdentityCapability(symbolicName, version); } resource = builder.getResource(); } try { module = installModule(classLoader, resource, headers); } catch (ModuleException ex) { LOGGER.error("Cannot install module from: " + bundle, ex); } return module; } @Override public Runtime shutdown() { throw new UnsupportedOperationException(); } private class OSGiModuleEntriesProvider implements ModuleEntriesProvider { private final Module module; OSGiModuleEntriesProvider(Module module) { this.module = module; } @Override public List<String> getEntryPaths(String path) { Bundle bundle = context.getBundle(module.getModuleId()); Enumeration<String> paths = bundle.getEntryPaths(path); List<String> result = new ArrayList<String>(); if (paths != null) { while (paths.hasMoreElements()) { String element = paths.nextElement(); result.add(element); } } return Collections.unmodifiableList(result); } @Override public URL getEntry(String path) { Bundle bundle = context.getBundle(module.getModuleId()); return bundle.getEntry(path); } @Override public List<URL> findEntries(String path, String filePattern, boolean recurse) { Bundle bundle = context.getBundle(module.getModuleId()); Enumeration<URL> paths = bundle.findEntries(path, filePattern, recurse); List<URL> result = new ArrayList<URL>(); if (paths != null) { while (paths.hasMoreElements()) { URL element = paths.nextElement(); result.add(element); } } return Collections.unmodifiableList(result); } } }