Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.paremus.dosgi.net.promise.PromiseFactory Maven / Gradle / Ivy
Go to download
An implementation of OSGi Remote Service Admin, enhanced to
provide support for OSGi Promises and streaming using OSGi
PushStreams.
/*-
* #%L
* com.paremus.dosgi.net
* %%
* Copyright (C) 2016 - 2019 Paremus Ltd
* %%
* Licensed under the Fair Source License, Version 0.9 (the "License");
*
* See the NOTICE.txt file distributed with this work for additional
* information regarding copyright ownership. You may not use this file
* except in compliance with the License. For usage restrictions see the
* LICENSE.txt file distributed with this work
* #L%
*/
package com.paremus.dosgi.net.promise;
import static java.util.Arrays.stream;
import static java.util.stream.Stream.concat;
import static java.util.stream.Stream.of;
import static org.osgi.framework.namespace.PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE;
import static org.osgi.framework.namespace.PackageNamespace.PACKAGE_NAMESPACE;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.Function;
import java.util.function.Supplier;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.Version;
import org.osgi.framework.wiring.BundleRevision;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.netty.util.Timer;
import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.EventExecutorGroup;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.ImmediateEventExecutor;
import io.netty.util.concurrent.Promise;
/**
* This factory/helper is designed to abstract away the complexities of dealing
* with Promises. RSA has to transparently cope with a great many class spaces,
* so we don't ever use the Promise API directly. Instead we use Netty promises
* internally, and use this helper to convert between types and class spaces.
*
*
* Note that this type must not ever import org.osgi.util.promise or
* org.osgi.util.function!
*
*/
public class PromiseFactory {
private static final Logger LOG = LoggerFactory.getLogger(PromiseFactory.class.getName());
private static final Version V_1 = Version.parseVersion("1.0.0");
private static final Version V_1_1 = Version.parseVersion("1.1.0");
private static final Version V_1_2 = Version.parseVersion("1.2.0");
private static final Version V_2 = Version.parseVersion("2.0.0");
private static final String PROMISE_PKG = "org.osgi.util.promise";
private static final String PROMISE_TYPE = PROMISE_PKG + ".Promise";
public static Function> toNettyFutureAdapter(Class> type) {
Function> toReturn = null;
Class> promise = locatePromiseInterface(type);
if (promise != null) {
Version promiseVersion = getVersion(promise);
if (promiseVersion.compareTo(V_1) < 0 || promiseVersion.compareTo(V_2) >= 0) {
LOG.warn("The promise type from bundle {} with version {} may not be usable with this RSA",
FrameworkUtil.getBundle(promise), promiseVersion);
}
try {
toReturn = new ToNettyFuture(promise);
} catch (Exception nsme) {
LOG.warn("There was an error trying to integrate with the Promise type from {}",
FrameworkUtil.getBundle(promise), nsme);
}
}
return toReturn;
}
private static Class> locatePromiseInterface(Class> type) {
Class> result = null;
do {
result = concat(of(type), stream(type.getInterfaces())).filter(c -> PROMISE_TYPE.equals(c.getName()))
.findFirst().orElse(null);
} while (result == null && (type = type.getSuperclass()) != null);
return result;
}
public static boolean isPromise(Class> type) {
return locatePromiseInterface(type) != null;
}
private static Version getVersion(Class> promise) {
Version result;
Bundle b = FrameworkUtil.getBundle(promise);
if (b == null) {
LOG.warn("Unable to determine the version of the Promise as it is not from OSGi");
result = Version.emptyVersion;
} else {
BundleRevision revision = b.adapt(BundleRevision.class);
if (revision == null) {
LOG.warn("Unable to determine the version of the Promise from bundle {} as it is uninstalled", b);
result = Version.emptyVersion;
} else {
result = revision.getCapabilities(PACKAGE_NAMESPACE).stream()
.map(bc -> bc.getAttributes())
.filter(m -> PROMISE_PKG.equals(m.get(PACKAGE_NAMESPACE)))
.map(m -> (Version) m.get(CAPABILITY_VERSION_ATTRIBUTE))
.findFirst().orElse(Version.emptyVersion);
}
}
return result;
}
private static class ToNettyFuture implements Function> {
private final Method onResolve;
private final Method isDone;
private final Method getValue;
private final Method getFailure;
public ToNettyFuture(Class> promise) throws NoSuchMethodException {
this.onResolve = promise.getMethod("onResolve", Runnable.class);
this.isDone = promise.getMethod("isDone");
this.getValue = promise.getMethod("getValue");
this.getFailure = promise.getMethod("getFailure");
}
@SuppressWarnings("unchecked")
@Override
public Future apply(Object t) {
Future toReturn;
if (t instanceof Future) {
toReturn = (Future) t;
} else {
try {
if ((Boolean) isDone.invoke(t)) {
toReturn = fastPath(t);
} else {
toReturn = chain(t);
}
} catch (InvocationTargetException ite) {
toReturn = ImmediateEventExecutor.INSTANCE.newFailedFuture(ite.getTargetException());
} catch (Exception e) {
toReturn = ImmediateEventExecutor.INSTANCE.newFailedFuture(e);
}
}
return toReturn;
}
private Future fastPath(Object t) {
Future toReturn;
try {
Object o = getFailure.invoke(t);
if (o != null) {
toReturn = ImmediateEventExecutor.INSTANCE.newFailedFuture((Throwable) o);
} else {
toReturn = ImmediateEventExecutor.INSTANCE.newSucceededFuture(getValue.invoke(t));
}
} catch (InvocationTargetException ite) {
toReturn = ImmediateEventExecutor.INSTANCE.newFailedFuture(ite.getTargetException());
} catch (Exception e) {
toReturn = ImmediateEventExecutor.INSTANCE.newFailedFuture(e);
}
return toReturn;
}
private Future chain(Object t) throws IllegalAccessException, InvocationTargetException {
Promise p = ImmediateEventExecutor.INSTANCE.newPromise();
onResolve.invoke(t, (Runnable) () -> {
try {
Object o = getFailure.invoke(t);
if (o != null) {
p.setFailure((Throwable) o);
} else {
p.setSuccess(getValue.invoke(t));
}
} catch (InvocationTargetException ite) {
p.setFailure(ite.getTargetException());
} catch (Exception e) {
p.setFailure(e);
}
});
return p;
}
}
public static Function> nettyPromiseCreator(Class> promise,
Timer timer) {
Function> toReturn = null;
if(promise != null) {
Version promiseVersion = getVersion(promise);
if (promiseVersion.compareTo(V_1) >= 0 && promiseVersion.compareTo(V_1_2) < 0) {
try {
toReturn = new CustomPromiseFactory(promise, promiseVersion, timer);
} catch (Exception nsme) {
LOG.warn("An error occurred trying to create high-performance promise integration for promises from bundle {}",
FrameworkUtil.getBundle(promise), nsme);
}
} else if (promiseVersion.compareTo(V_1) < 0 || promiseVersion.compareTo(V_2) >= 0) {
LOG.warn("The Promises from bundle {} are at an incompatible version {} and so integration is not possible",
FrameworkUtil.getBundle(promise), promiseVersion);
}
}
if (toReturn == null) {
toReturn = e -> e.newPromise();
}
return toReturn;
}
private static class CustomPromiseFactory implements Function> {
private static final String CUSTOM_IMPL_NAME_V1 = "com.paremus.dosgi.net.promise.FuturePromise_v1";
private static final String CUSTOM_RESOURCE_NAME_V1 = "com/paremus/dosgi/net/promise/FuturePromise_v1.class";
private static final String CUSTOM_IMPL_NAME_V1_1 = "com.paremus.dosgi.net.promise.FuturePromise_v1_1";
private static final String CUSTOM_RESOURCE_NAME_V1_1 = "com/paremus/dosgi/net/promise/FuturePromise_v1_1.class";
private final Constructor> promiseConstructor;
private final Timer timer;
public CustomPromiseFactory(Class> promiseClass, Version promiseVersion,
Timer timer) throws NoSuchMethodException, SecurityException, ClassNotFoundException {
this.timer = timer;
ClassLoader customImplLoader = getCustomImplLoader(promiseClass);
promiseConstructor = customImplLoader.loadClass(V_1_1.compareTo(promiseVersion) > 0 ?
CUSTOM_IMPL_NAME_V1 : CUSTOM_IMPL_NAME_V1_1)
.getDeclaredConstructor(EventExecutor.class, Timer.class);
promiseConstructor.setAccessible(true);
}
private ClassLoader getCustomImplLoader(Class> promiseClass) {
ClassLoader factoryLoader = PromiseFactory.class.getClassLoader();
ClassLoader promiseLoader = promiseClass.getClassLoader();
ClassLoader customImplLoader = new ClassLoader() {
@Override
protected Class> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if(name.startsWith("io.netty.util")) {
return factoryLoader.loadClass(name);
} else if (name.startsWith("org.osgi.util")) {
return promiseLoader.loadClass(name);
}
String resource = null;
if(CUSTOM_IMPL_NAME_V1.equals(name)) {
resource = CUSTOM_RESOURCE_NAME_V1;
} else if (CUSTOM_IMPL_NAME_V1_1.equals(name)) {
resource = CUSTOM_RESOURCE_NAME_V1_1;
}
if(resource != null) {
Class> cls = findLoadedClass(name);
if(cls == null) {
cls = doLoad(name, resource);
if(resolve) {
resolveClass(cls);
}
}
return cls;
}
return super.loadClass(name, resolve);
}
private Class> doLoad(String name, String resource) throws ClassNotFoundException {
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream is = factoryLoader.getResourceAsStream(resource)) {
byte[] b = new byte[4096];
int r;
while((r = is.read(b)) > -1) {
baos.write(b, 0, r);
}
return defineClass(name, baos.toByteArray(), 0, baos.size(),
PromiseFactory.class.getProtectionDomain());
} catch (IOException ioe) {
LOG.error("Unable to load the type {}", name, ioe);
throw new ClassNotFoundException(name, ioe);
}
}
};
return customImplLoader;
}
@SuppressWarnings("unchecked")
@Override
public Promise apply(EventExecutor executor) {
try {
return (Promise) promiseConstructor.newInstance(executor, timer);
} catch (InvocationTargetException e) {
throw new RuntimeException(e.getTargetException());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public static Function, Object> fromNettyFutureAdapter(Class> promise,
EventExecutorGroup workers) {
Function, Object> toReturn = null;
if (promise != null) {
try {
Function, Object> fallback = new FromNettyPromiseDefaultImpl(promise, workers);
toReturn = f -> {
Object o;
if(promise.isInstance(f)) {
o = f;
} else {
o = fallback.apply(f);
}
return o;
};
} catch (Exception nsme) {
LOG.warn("There was an error trying to integrate with the Promise type from {}",
FrameworkUtil.getBundle(promise), nsme);
}
}
return toReturn;
}
private static class FromNettyPromiseDefaultImpl implements Function, Object> {
private static final String DEFAULT_FACTORY_NAME = "org.osgi.util.promise.Deferred";
private final Supplier newDeferred;
private final Function resolvedPromise;
private final Function failedPromise;
private final Method factoryGetPromise;
private final Method factoryResolve;
private final Method factoryFail;
public FromNettyPromiseDefaultImpl(Class> promiseClass, EventExecutorGroup workers)
throws NoSuchMethodException, SecurityException, ClassNotFoundException {
ClassLoader loader = promiseClass.getClassLoader();
Class> factory = loader.loadClass(DEFAULT_FACTORY_NAME);
factoryGetPromise = factory.getMethod("getPromise");
factoryResolve = factory.getMethod("resolve", Object.class);
factoryFail = factory.getMethod("fail", Throwable.class);
Supplier newInstance;
Function resolvedInstance;
Function failedInstance;
try {
Class> promiseFactoryClass = loader.loadClass("org.osgi.util.promise.PromiseFactory");
Constructor> c = promiseFactoryClass.getConstructor(Executor.class, ScheduledExecutorService.class);
// Attempt to use different threads for resolve callbacks and timing
Object promiseExecutors = c.newInstance(workers, workers);
Method makeDeferred = promiseFactoryClass.getMethod("deferred");
Method makeResolved = promiseFactoryClass.getMethod("resolved", Object.class);
Method makeFailed = promiseFactoryClass.getMethod("failed", Throwable.class);
newInstance = () -> {
try {
return makeDeferred.invoke(promiseExecutors);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
};
resolvedInstance = o -> {
try {
return makeResolved.invoke(promiseExecutors, o);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
};
failedInstance = t -> {
try {
return makeFailed.invoke(promiseExecutors, t);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
};
} catch (Exception ex) {
Constructor> c = factory.getConstructor();
newInstance = () -> {
try {
return c.newInstance();
} catch (Exception ex2) {
throw new RuntimeException(ex2);
}
};
resolvedInstance = o -> {
try {
return factoryResolve.invoke(c.newInstance(), o);
} catch (Exception ex2) {
throw new RuntimeException(ex2);
}
};
failedInstance = t -> {
try {
return factoryFail.invoke(c.newInstance(), t);
} catch (Exception ex2) {
throw new RuntimeException(ex2);
}
};
}
newDeferred = newInstance;
resolvedPromise = resolvedInstance;
failedPromise = failedInstance;
}
@Override
public Object apply(Future> f) {
Object toReturn;
try {
// This check allows a thread switch to be skipped sometimes
// A single isDone is used to avoid racing the promise
if(f.isDone()) {
if(f.isSuccess()) {
toReturn = resolvedPromise.apply(f.getNow());
} else {
toReturn = failedPromise.apply(f.cause());
}
} else {
Object deferred = newDeferred.get();
f.addListener(x -> {
try {
if(f.isSuccess()) {
factoryResolve.invoke(deferred, f.getNow());
} else {
factoryFail.invoke(deferred, f.cause());
}
} catch (Exception e) {
factoryFail.invoke(deferred, e);
}
});
toReturn = factoryGetPromise.invoke(deferred);
}
} catch (InvocationTargetException e) {
throw new RuntimeException(e.getTargetException());
} catch (Exception e) {
throw new RuntimeException(e);
}
return toReturn;
}
}
}