/* * (C) Copyright 2006-2014 Nuxeo SA (http://nuxeo.com/) and others. * * 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. * * Contributors: * Nuxeo - initial API and implementation * */ package org.nuxeo.osgi; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.Dictionary; import java.util.Enumeration; import java.util.Map; import java.util.jar.Manifest; import org.nuxeo.common.utils.ExceptionUtils; import org.nuxeo.osgi.util.CompoundEnumeration; import org.nuxeo.runtime.api.Framework; import org.osgi.framework.Bundle; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleEvent; import org.osgi.framework.BundleException; import org.osgi.framework.Constants; import org.osgi.framework.ServiceReference; import org.osgi.framework.Version; import org.osgi.service.packageadmin.PackageAdmin; /** * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> */ public class BundleImpl implements Bundle { protected final long id; protected final String symbolicName; protected final Dictionary<String, String> headers; protected final BundleContext context; protected final OSGiAdapter osgi; protected final BundleFile file; protected final ClassLoader loader; protected int state; protected long lastModified; protected BundleActivator activator; protected double startupTime; protected boolean allowHostOverride; public BundleImpl(OSGiAdapter osgi, BundleFile file, ClassLoader loader) throws BundleException { this(osgi, file, loader, false); } public BundleImpl(OSGiAdapter osgi, BundleFile file, ClassLoader loader, boolean isSystemBundle) throws BundleException { this.osgi = osgi; this.loader = loader; this.file = file; Manifest mf = file.getManifest(); if (mf == null) { headers = null; symbolicName = null; id = -1; context = null; return; } try { headers = BundleManifestReader.getHeaders(mf); } catch (BundleException e) { throw new BundleException("Invalid OSGi Manifest in file " + file + " : " + e.getMessage(), e); } symbolicName = headers.get(Constants.BUNDLE_SYMBOLICNAME); allowHostOverride = Boolean.parseBoolean(headers.get(BundleManifestReader.ALLOW_HOST_OVERRIDE)); id = isSystemBundle ? 0 : osgi.getBundleId(symbolicName); context = createContext(); state = UNINSTALLED; } public BundleFile getBundleFile() { return file; } protected final BundleContext createContext() { return new OSGiBundleContext(this); } @Override public BundleContext getBundleContext() { // ensure BundleContext is not visible in RESOLVED state - to ensure // OSGi compat. - in our component activate method. // TODO NXP-6035: disable for now the check until a better compatibility // mode is implemented. // if (state == RESOLVED) { // throw new IllegalStateException( // "You cannot use a BundleContext when in RESOLVED state. Do not use this in your component activate method!"); // } return context; } @Override public void start(int options) throws BundleException { // TODO Auto-generated method stub } @Override public void stop(int options) throws BundleException { // TODO } @Override public String getLocation() { return file.getLocation(); } @Override public URL getResource(String name) { return loader.getResource(name); } @Override public Enumeration<URL> getResources(String name) throws IOException { return loader.getResources(name); } @Override public Class<?> loadClass(String name) throws ClassNotFoundException { try { return loader.loadClass(name); } catch (NoClassDefFoundError e) { throw e; } } @Override public URL getEntry(String name) { return file.getEntry(name); } public static PackageAdmin getPackageAdmin() { BundleContext sysctx = Framework.getRuntime().getContext().getBundle().getBundleContext(); ServiceReference ref = sysctx.getServiceReference(PackageAdmin.class.getName()); return (PackageAdmin) sysctx.getService(ref); } protected static class CompoundEnumerationBuilder { protected final ArrayList<Enumeration<URL>> collected = new ArrayList<>(); public CompoundEnumerationBuilder add(Enumeration<URL> e) { collected.add(e); return this; } public Enumeration<URL> build() { return new CompoundEnumeration<>(collected.toArray(new Enumeration[collected.size()])); } } @Override public Enumeration<URL> findEntries(String path, String filePattern, boolean recurse) { Enumeration<URL> hostEntries = file.findEntries(path, filePattern, recurse); Bundle[] fragments = osgi.getRegistry().getFragments(symbolicName); if (fragments.length == 0) { return hostEntries; } CompoundEnumerationBuilder builder = new CompoundEnumerationBuilder(); if (!allowHostOverride) { builder.add(hostEntries); } for (Bundle fragment : fragments) { Enumeration<URL> fragmentEntries = fragment.findEntries(path, filePattern, recurse); builder.add(fragmentEntries); } if (allowHostOverride) { builder.add(hostEntries); } return builder.build(); } @Override public Enumeration<String> getEntryPaths(String path) { return file.getEntryPaths(path); } @Override public long getBundleId() { return id; } @Override public Dictionary<String, String> getHeaders() { return headers; } @Override public Dictionary<String, String> getHeaders(String locale) { return headers; // TODO } @Override public long getLastModified() { return lastModified; } @Override public ServiceReference[] getRegisteredServices() { // RegistrationInfo ri = // (RegistrationInfo)di.context.get("RegistrationInfo"); // TODO Auto-generated method stub return null; } @Override public ServiceReference[] getServicesInUse() { // TODO Auto-generated method stub return null; } @Override public int getState() { return state; } @Override public String getSymbolicName() { return symbolicName; } @Override public boolean hasPermission(Object permission) { return true; // TODO } protected String getActivatorClassName() { return headers == null ? null : headers.get(Constants.BUNDLE_ACTIVATOR); } public BundleActivator getActivator() throws BundleException { if (activator == null) { activator = NullActivator.INSTANCE; String className = getActivatorClassName(); if (className == null) { return activator; } try { activator = (BundleActivator) loadClass(className).newInstance(); } catch (ClassNotFoundException e) { throw new BundleException("Activator not found: " + className, e); } catch (InstantiationException e) { throw new BundleException("Activator not instantiable: " + className, e); } catch (IllegalAccessException e) { throw new BundleException("Activator not accessible: " + className, e); } } return activator; } @Override public void start() throws BundleException { try { setStarting(); getActivator().start(context); setStarted(); } catch (BundleException e) { throw new BundleException("Failed to start bundle at: " + file + " with activator: " + getActivatorClassName(), e); } catch (Exception e) { // stupid OSGi API throws Exception RuntimeException re = ExceptionUtils.runtimeException(e); throw new BundleException("Failed to start bundle at: " + file + " with activator: " + getActivatorClassName(), re); } } @Override public void stop() throws BundleException { try { setStopping(); getActivator().stop(context); setStopped(); } catch (BundleException e) { throw new BundleException("Failed to stop activator: " + getActivatorClassName(), e); } catch (Exception e) { // stupid OSGi API throws Exception RuntimeException re = ExceptionUtils.runtimeException(e); throw new BundleException("Failed to stop activator: " + getActivatorClassName(), re); } } public void shutdown() throws BundleException { try { state = STOPPING; getActivator().stop(context); lastModified = System.currentTimeMillis(); state = UNINSTALLED; } catch (BundleException e) { throw new BundleException("Failed to stop activator: " + getActivatorClassName(), e); } catch (Exception e) { // stupid OSGi API throws Exception RuntimeException re = ExceptionUtils.runtimeException(e); throw new BundleException("Failed to stop activator: " + getActivatorClassName(), re); } } @Override public void uninstall() throws BundleException { osgi.uninstall(this); try { file.close(osgi); } catch (IOException e) { throw new BundleException("Cannot close underlying file resources " + symbolicName, e); } } @Override public void update() throws BundleException { lastModified = System.currentTimeMillis(); throw new UnsupportedOperationException("Bundle.update() operations was not yet implemented"); } @Override public void update(InputStream in) throws BundleException { lastModified = System.currentTimeMillis(); throw new UnsupportedOperationException("Bundle.update() operations was not yet implemented"); } void setInstalled() { if (state == INSTALLED) { return; } lastModified = System.currentTimeMillis(); state = INSTALLED; BundleEvent event = new BundleEvent(BundleEvent.INSTALLED, this); osgi.fireBundleEvent(event); } void setUninstalled() { if (state == UNINSTALLED) { return; } lastModified = System.currentTimeMillis(); state = UNINSTALLED; BundleEvent event = new BundleEvent(BundleEvent.UNINSTALLED, this); osgi.fireBundleEvent(event); } void setResolved() { if (state == RESOLVED) { return; } state = RESOLVED; BundleEvent event = new BundleEvent(BundleEvent.RESOLVED, this); osgi.fireBundleEvent(event); } void setUnResolved() { state = INSTALLED; BundleEvent event = new BundleEvent(BundleEvent.UNRESOLVED, this); osgi.fireBundleEvent(event); } void setStarting() { if (state != RESOLVED) { return; } state = STARTING; BundleEvent event = new BundleEvent(BundleEvent.STARTING, this); osgi.fireBundleEvent(event); } void setStarted() { if (state != STARTING) { return; } state = ACTIVE; BundleEvent event = new BundleEvent(BundleEvent.STARTED, this); osgi.fireBundleEvent(event); } void setStopping() { if (state != ACTIVE) { return; } state = STOPPING; BundleEvent event = new BundleEvent(BundleEvent.STOPPING, this); osgi.fireBundleEvent(event); } void setStopped() { if (state != STOPPING) { return; } state = RESOLVED; BundleEvent event = new BundleEvent(BundleEvent.STOPPED, this); osgi.fireBundleEvent(event); } public double getStartupTime() { return startupTime; } @Override public int hashCode() { return symbolicName.hashCode(); } @Override public boolean equals(Object obj) { if (obj instanceof Bundle) { return symbolicName.equals(((Bundle) obj).getSymbolicName()); } return false; } @Override public String toString() { return symbolicName; } @Override public Map getSignerCertificates(int signersType) { throw new UnsupportedOperationException("not yet implemented"); } @Override public Version getVersion() { return Version.parseVersion(headers.get(Constants.BUNDLE_VERSION)); } }