/**
* Copyright (C) 2010-14 diirt developers. See COPYRIGHT.TXT
* All rights reserved. Use is subject to license terms. See LICENSE.TXT
*/
package org.diirt.util.config;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* A wrapper for ServiceLoader that works in OSGi as well.
* <p>
* OSGi is known to break the ServiceLoader mechanism. Unfortunately, we have
* not been able to make the standard based solution based on Ares SPI-Fly work.
* Therefore we have created this wrapper that detects whether the class has
* been loaded with OSGi. If not, uses the standard ServiceLoader. If so, uses
* introspection to access the OSGi framework and replicate the functionality.
* <p>
* Note that the implementation on OSGi will not give all the nice debugging
* information that the standard ServiceLoader implementation does. It also is
* not optimized: makes heavy use of introspection and does not use lazy
* initialization. It's essentially a hack to make things to work.
*
* @author carcassi
*/
public class ServiceLoaderOSGiWrapper {
public static <T> void load(Class<T> serviceClazz, Logger log, Consumer<T> consumer) {
log.log(Level.CONFIG, "Fetching {0}s", serviceClazz.getSimpleName());
int count = 0;
for (T service : ServiceLoaderOSGiWrapper.load(serviceClazz)) {
log.log(Level.CONFIG, "Found {0} ({1})", new Object[] {serviceClazz.getSimpleName(), service.getClass().getSimpleName()});
try {
consumer.accept(service);
count++;
} catch (RuntimeException ex) {
log.log(Level.WARNING, "Couldn't register " + serviceClazz.getSimpleName() + " (" + service.getClass().getSimpleName() + ")", ex);
}
}
log.log(Level.CONFIG, "Found {0} {1}s", new Object[] {count, serviceClazz.getSimpleName()});
}
public static <T> Iterable<T> load(Class<T> serviceClazz) {
if (isOSGi(serviceClazz)) {
return loadOSGi(serviceClazz);
} else {
return ServiceLoader.load(serviceClazz);
}
}
private static boolean isOSGi(Class<?> serviceClazz) {
try {
if (osgi.frameworkUtilClass != null && osgi.getBundle(serviceClazz) != null) {
return true;
}
} catch (Exception e) {
}
return false;
}
private static <T> List<T> loadOSGi(Class<T> serviceClazz) {
// TODO: improve logging
Object bundle = osgi.getBundle(serviceClazz);
System.out.println("Found bundle " + bundle);
osgi.start(bundle);
Object bundleContext = osgi.getBundleContext(bundle);
Object[] bundles = osgi.getBundles(bundleContext);
System.out.println("Found bundle " + bundleContext);
System.out.println("Found bundles " + bundles);
Map<Object, List<String>> bundleToServiceClassnamesMap = new HashMap<>();
List<T> providers = new ArrayList<>();
for (Object providerBundle : bundles) {
URL url = osgi.getEntry(providerBundle, "META-INF/services/" + serviceClazz.getName());
if (url != null) {
System.out.println("Found " + url + " in " + providerBundle);
List<String> classnames = readAllLines(url);
if (classnames != null) {
bundleToServiceClassnamesMap.put(providerBundle, classnames);
System.out.println("Providers " + classnames);
for (String classname : classnames) {
Class<?> providerClass = osgi.loadClass(providerBundle, classname);
if (providerClass != null) {
try {
T provider = serviceClazz.cast(providerClass.newInstance());
System.out.println("Provider instance " + provider);
providers.add(provider);
} catch (InstantiationException | IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
}
return providers;
}
private static OSGiReflection osgi = new OSGiReflection();
private static class OSGiReflection {
private final Class<?> frameworkUtilClass = loadClass("org.osgi.framework.FrameworkUtil");
private final Class<?> bundleClass = loadClass("org.osgi.framework.Bundle");
private final Class<?> bundleContextClass = loadClass("org.osgi.framework.BundleContext");
private final Method getBundleMethod = loadMethod(frameworkUtilClass, "getBundle", Class.class);
private final Method startMethod = loadMethod(bundleClass, "start");
private final Method getBundleContextMethod = loadMethod(bundleClass, "getBundleContext");
private final Method getBundlesMethod = loadMethod(bundleContextClass, "getBundles");
private final Method loadClassMethod = loadMethod(bundleClass, "loadClass", String.class);
private final Method getEntryMethod = loadMethod(bundleClass, "getEntry", String.class);
private final boolean osgiEnabled;
public OSGiReflection() {
this.osgiEnabled = false;
}
private Class<?> loadClass(String name) {
try {
return Class.forName(name);
} catch (ClassNotFoundException ex) {
return null;
}
}
private Method loadMethod(Class<?> clazz, String name, Class<?>... parameterTypes) {
if (clazz == null) {
return null;
}
try {
return clazz.getMethod(name, parameterTypes);
} catch (NoSuchMethodException | SecurityException ex) {
Logger.getLogger(ServiceLoaderOSGiWrapper.class.getName()).log(Level.SEVERE, null, ex);
return null;
}
}
private Object getBundle(Object classFromBundle) {
try {
return getBundleMethod.invoke(null, classFromBundle);
} catch (SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
Logger.getLogger(ServiceLoaderOSGiWrapper.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
private void start(Object bundle) {
try {
startMethod.invoke(bundle);
} catch (SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
Logger.getLogger(ServiceLoaderOSGiWrapper.class.getName()).log(Level.SEVERE, null, ex);
}
}
private Object getBundleContext(Object bundle) {
try {
return getBundleContextMethod.invoke(bundle);
} catch (SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
Logger.getLogger(ServiceLoaderOSGiWrapper.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
private Object[] getBundles(Object bundleContext) {
try {
return (Object[]) getBundlesMethod.invoke(bundleContext);
} catch (SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
Logger.getLogger(ServiceLoaderOSGiWrapper.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
private URL getEntry(Object bundle, String name) {
try {
return (URL) getEntryMethod.invoke(bundle, name);
} catch (SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
Logger.getLogger(ServiceLoaderOSGiWrapper.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
private Class<?> loadClass(Object bundle, String name) {
try {
return (Class<?>) loadClassMethod.invoke(bundle, name);
} catch (SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
Logger.getLogger(ServiceLoaderOSGiWrapper.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
}
private static List<String> readAllLines(URL url) {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8"))) {
List<String> result = new ArrayList<>();
for (;;) {
String line = reader.readLine();
if (line == null) {
break;
}
result.add(line);
}
return result;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}