// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.application.container.impl; import com.google.common.collect.Lists; import com.yahoo.container.standalone.StandaloneContainerApplication; import com.yahoo.jdisc.application.OsgiFramework; import com.yahoo.jdisc.application.OsgiHeader; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleListener; import org.osgi.framework.Filter; import org.osgi.framework.FrameworkListener; import org.osgi.framework.ServiceFactory; import org.osgi.framework.ServiceListener; import org.osgi.framework.ServiceObjects; import org.osgi.framework.ServiceReference; import org.osgi.framework.ServiceRegistration; import org.osgi.framework.Version; import org.osgi.framework.wiring.BundleCapability; import org.osgi.framework.wiring.BundleRequirement; import org.osgi.framework.wiring.BundleRevision; import org.osgi.framework.wiring.BundleWire; import org.osgi.framework.wiring.BundleWiring; import org.osgi.resource.Capability; import org.osgi.resource.Requirement; import org.osgi.resource.Wire; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.URLClassLoader; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Dictionary; import java.util.Enumeration; import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.jar.Attributes; import java.util.jar.JarFile; /** * A (mock) OSGI implementation which loads classes from the system classpath * * @author Tony Vaagenes * @author ollivir */ public final class ClassLoaderOsgiFramework implements OsgiFramework { private BundleContextImpl bundleContextImpl = new BundleContextImpl(); private SystemBundleImpl systemBundleImpl = new SystemBundleImpl(); private BundleWiringImpl bundleWiringImpl = new BundleWiringImpl(); private List bundleLocations = new ArrayList<>(); private List bundleList = Lists.newArrayList(systemBundleImpl); private ClassLoader classLoader = null; private AtomicInteger nextBundleId = new AtomicInteger(1); @Override public List installBundle(String bundleLocation) { if (bundleLocation != null && ! bundleLocation.isEmpty()) { try { URL url = new URL(bundleLocation); bundleLocations.add(url); bundleList.add(new JarBundleImpl(url)); } catch (IOException e) { throw new RuntimeException(e); } } return bundles(); } private ClassLoader getClassLoader() { if (bundleLocations.isEmpty()) { return getClass().getClassLoader(); } else { if (classLoader == null) { classLoader = new URLClassLoader(bundleLocations.toArray(new URL[0]), getClass().getClassLoader()); } return classLoader; } } @Override public void startBundles(List bundles, boolean privileged) { } @Override public void refreshPackages() { } @Override public BundleContext bundleContext() { return bundleContextImpl; } @Override public List bundles() { return bundleList; } @Override public List getBundles(Bundle requestingBundle) { return bundleList; } @Override public void allowDuplicateBundles(Collection bundles) { } @Override public void start() { } @Override public void stop() { } private abstract class BundleImpl implements Bundle { @Override public int getState() { return Bundle.ACTIVE; } @Override public void start(int options) { } @Override public void start() { } @Override public void stop(int options) { } @Override public void stop() { } @Override public void update(InputStream input) { } @Override public void update() { } @Override public void uninstall() { } @Override public Dictionary getHeaders(String locale) { return getHeaders(); } @Override public String getSymbolicName() { return ClassLoaderOsgiFramework.this.getClass().getName(); } @Override public String getLocation() { return getSymbolicName(); } @Override public ServiceReference[] getRegisteredServices() { return new ServiceReference[0]; } @Override public ServiceReference[] getServicesInUse() { return getRegisteredServices(); } @Override public boolean hasPermission(Object permission) { return true; } @Override public URL getResource(String name) { return getClassLoader().getResource(name); } @Override public Class loadClass(String name) throws ClassNotFoundException { return getClassLoader().loadClass(name); } @Override public Enumeration getResources(String name) throws IOException { return getClassLoader().getResources(name); } @Override public Enumeration getEntryPaths(String path) { throw new UnsupportedOperationException(); } @Override public URL getEntry(String path) { throw new UnsupportedOperationException(); } @Override public Enumeration findEntries(String path, String filePattern, boolean recurse) { throw new UnsupportedOperationException(); } @Override public long getLastModified() { return 1L; } @Override public BundleContext getBundleContext() { throw new UnsupportedOperationException(); } @Override public Map> getSignerCertificates(int signersType) { return Collections.emptyMap(); } @Override @SuppressWarnings("unchecked") public T adapt(Class clazz) { if (clazz.equals(BundleRevision.class)) { return (T) new BundleRevisionImpl(); } else if (clazz.equals(BundleWiring.class)) { return (T) new BundleWiringImpl(); } else { return null; } } @Override public File getDataFile(String filename) { return null; } @Override public int compareTo(Bundle o) { return Long.compare(getBundleId(), o.getBundleId()); } } private class BundleRevisionImpl implements BundleRevision { @Override public String getSymbolicName() { return this.getClass().getName(); } @Override public List getDeclaredRequirements(String p1) { throw new UnsupportedOperationException(); } @Override public Version getVersion() { return Version.emptyVersion; } @Override public BundleWiring getWiring() { return bundleWiringImpl; } @Override public List getDeclaredCapabilities(String p1) { throw new UnsupportedOperationException(); } @Override public int getTypes() { return 0; } @Override public Bundle getBundle() { throw new UnsupportedOperationException(); } @Override public List getCapabilities(String p1) { throw new UnsupportedOperationException(); } @Override public List getRequirements(String p1) { throw new UnsupportedOperationException(); } } private class BundleWiringImpl implements BundleWiring { @Override public List findEntries(String p1, String p2, int p3) { throw new UnsupportedOperationException(); } @Override public List getRequiredResourceWires(String p1) { throw new UnsupportedOperationException(); } @Override public List getResourceCapabilities(String p1) { throw new UnsupportedOperationException(); } @Override public boolean isCurrent() { throw new UnsupportedOperationException(); } @Override public List getRequiredWires(String p1) { throw new UnsupportedOperationException(); } @Override public List getCapabilities(String p1) { throw new UnsupportedOperationException(); } @Override public List getProvidedResourceWires(String p1) { throw new UnsupportedOperationException(); } @Override public List getProvidedWires(String p1) { throw new UnsupportedOperationException(); } @Override public BundleRevision getRevision() { throw new UnsupportedOperationException(); } @Override public List getResourceRequirements(String p1) { throw new UnsupportedOperationException(); } @Override public boolean isInUse() { throw new UnsupportedOperationException(); } @Override public Collection listResources(String p1, String p2, int p3) { throw new UnsupportedOperationException(); } @Override public ClassLoader getClassLoader() { return ClassLoaderOsgiFramework.this.getClassLoader(); } @Override public List getRequirements(String p1) { throw new UnsupportedOperationException(); } @Override public BundleRevision getResource() { throw new UnsupportedOperationException(); } @Override public Bundle getBundle() { throw new UnsupportedOperationException(); } } private class SystemBundleImpl extends BundleImpl { @Override public long getBundleId() { return 0L; } @Override public Version getVersion() { return Version.emptyVersion; } @Override public Dictionary getHeaders() { Hashtable ret = new Hashtable<>(); ret.put(OsgiHeader.APPLICATION, StandaloneContainerApplication.class.getName()); return ret; } } private class JarBundleImpl extends BundleImpl { private final long bundleId; private final Dictionary headers; JarBundleImpl(URL location) throws IOException { this.bundleId = (long) nextBundleId.getAndIncrement(); this.headers = retrieveHeaders(location); } @Override public long getBundleId() { return bundleId; } @Override public Dictionary getHeaders() { return headers; } @Override public String getSymbolicName() { return headers.get("Bundle-SymbolicName"); } @Override public Version getVersion() { return Version.parseVersion(headers.get("Bundle-Version")); } private Dictionary retrieveHeaders(URL location) throws IOException { try (JarFile jarFile = new JarFile(location.getFile())) { Attributes attributes = jarFile.getManifest().getMainAttributes(); Hashtable ret = new Hashtable<>(); attributes.forEach((k, v) -> ret.put(k.toString(), v.toString())); return ret; } } } private class BundleContextImpl implements BundleContext { @Override public String getProperty(String key) { return null; } @Override public Bundle getBundle() { return systemBundleImpl; } @Override public Bundle installBundle(String location, InputStream input) { throw new UnsupportedOperationException(); } @Override public Bundle installBundle(String location) { throw new UnsupportedOperationException(); } @Override public Bundle getBundle(long id) { return systemBundleImpl; } @Override public Bundle[] getBundles() { return new Bundle[] { systemBundleImpl }; } @Override public Bundle getBundle(String location) { return systemBundleImpl; } @Override public void addServiceListener(ServiceListener listener, String filter) { } @Override public void addServiceListener(ServiceListener listener) { } @Override public void removeServiceListener(ServiceListener listener) { } @Override public void addBundleListener(BundleListener listener) { } @Override public void removeBundleListener(BundleListener listener) { } @Override public void addFrameworkListener(FrameworkListener listener) { } @Override public void removeFrameworkListener(FrameworkListener listener) { } @Override public ServiceRegistration registerService(String[] classes, Object service, Dictionary properties) { throw new UnsupportedOperationException(); } @Override public ServiceRegistration registerService(String clazz, Object service, Dictionary properties) { return null; } @Override public ServiceRegistration registerService(Class clazz, S service, Dictionary properties) { throw new UnsupportedOperationException(); } @Override public ServiceReference[] getServiceReferences(String clazz, String filter) { throw new UnsupportedOperationException(); } @Override public ServiceReference[] getAllServiceReferences(String clazz, String filter) { throw new UnsupportedOperationException(); } @Override public ServiceReference getServiceReference(String clazz) { throw new UnsupportedOperationException(); } @Override public ServiceReference getServiceReference(Class clazz) { throw new UnsupportedOperationException(); } @Override public Collection> getServiceReferences(Class clazz, String filter) { return new ArrayList<>(); } @Override public S getService(ServiceReference reference) { throw new UnsupportedOperationException(); } @Override public boolean ungetService(ServiceReference reference) { throw new UnsupportedOperationException(); } @Override public File getDataFile(String filename) { throw new UnsupportedOperationException(); } @Override public Filter createFilter(String filter) { throw new UnsupportedOperationException(); } @Override public ServiceRegistration registerService(Class aClass, ServiceFactory serviceFactory, Dictionary dictionary) { throw new UnsupportedOperationException(); } @Override public ServiceObjects getServiceObjects(ServiceReference serviceReference) { throw new UnsupportedOperationException(); } } }