All Downloads are FREE. Search and download functionalities are using the official Maven repository.
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.gh.bmd.jrt.core.DefaultObjectRoutineBuilder Maven / Gradle / Ivy
/*
* 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.
*/
package com.gh.bmd.jrt.core;
import com.gh.bmd.jrt.annotation.Bind;
import com.gh.bmd.jrt.annotation.Pass;
import com.gh.bmd.jrt.annotation.Pass.PassMode;
import com.gh.bmd.jrt.annotation.ShareGroup;
import com.gh.bmd.jrt.annotation.Timeout;
import com.gh.bmd.jrt.annotation.TimeoutAction;
import com.gh.bmd.jrt.builder.ObjectRoutineBuilder;
import com.gh.bmd.jrt.builder.RoutineConfiguration;
import com.gh.bmd.jrt.builder.RoutineConfiguration.Builder;
import com.gh.bmd.jrt.builder.RoutineConfiguration.OrderType;
import com.gh.bmd.jrt.builder.RoutineConfiguration.TimeoutActionType;
import com.gh.bmd.jrt.channel.OutputChannel;
import com.gh.bmd.jrt.channel.ParameterChannel;
import com.gh.bmd.jrt.common.ClassToken;
import com.gh.bmd.jrt.common.WeakIdentityHashMap;
import com.gh.bmd.jrt.routine.Routine;
import com.gh.bmd.jrt.time.TimeDuration;
import java.lang.annotation.Annotation;
import java.lang.ref.WeakReference;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import static com.gh.bmd.jrt.builder.RoutineBuilders.getParamMode;
import static com.gh.bmd.jrt.builder.RoutineBuilders.getReturnMode;
import static com.gh.bmd.jrt.common.Reflection.boxingClass;
import static com.gh.bmd.jrt.time.TimeDuration.fromUnit;
/**
* Class implementing a builder of routines wrapping an object instance.
*
* Created by davide on 9/21/14.
*/
class DefaultObjectRoutineBuilder extends DefaultClassRoutineBuilder
implements ObjectRoutineBuilder {
private static final WeakIdentityHashMap> sMethodCache =
new WeakIdentityHashMap>();
/**
* Constructor.
*
* @param target the target object instance.
* @throws java.lang.IllegalArgumentException if a duplicate name in the annotations is
* detected.
* @throws java.lang.NullPointerException if the specified target is null.
*/
DefaultObjectRoutineBuilder(@Nonnull final Object target) {
super(target);
}
@Nullable
@SuppressWarnings("unchecked")
private static Object callRoutine(@Nonnull final Routine routine,
@Nonnull final Method method, @Nonnull final Object[] args,
@Nullable final PassMode paramMode, @Nullable final PassMode returnMode) {
final Class> returnType = method.getReturnType();
final OutputChannel outputChannel;
if (paramMode == PassMode.PARALLEL) {
final ParameterChannel parameterChannel = routine.invokeParallel();
final Class> parameterType = method.getParameterTypes()[0];
final Object arg = args[0];
if (arg == null) {
parameterChannel.pass((Iterable) null);
} else if (OutputChannel.class.isAssignableFrom(parameterType)) {
parameterChannel.pass((OutputChannel) arg);
} else if (parameterType.isArray()) {
final int length = Array.getLength(arg);
for (int i = 0; i < length; i++) {
parameterChannel.pass(Array.get(arg, i));
}
} else {
final Iterable> iterable = (Iterable>) arg;
for (final Object input : iterable) {
parameterChannel.pass(input);
}
}
outputChannel = parameterChannel.result();
} else if (paramMode == PassMode.OBJECT) {
final ParameterChannel parameterChannel = routine.invokeAsync();
final Class>[] parameterTypes = method.getParameterTypes();
final int length = args.length;
for (int i = 0; i < length; ++i) {
final Object arg = args[i];
if (OutputChannel.class.isAssignableFrom(parameterTypes[i])) {
parameterChannel.pass((OutputChannel) arg);
} else {
parameterChannel.pass(arg);
}
}
outputChannel = parameterChannel.result();
} else if (paramMode == PassMode.COLLECTION) {
final ParameterChannel parameterChannel = routine.invokeAsync();
outputChannel = parameterChannel.pass((OutputChannel) args[0]).result();
} else {
outputChannel = routine.callAsync(args);
}
if (!Void.class.equals(boxingClass(returnType))) {
if (returnMode != null) {
if (OutputChannel.class.isAssignableFrom(returnType)) {
return outputChannel;
}
if (returnType.isAssignableFrom(List.class)) {
return outputChannel.readAll();
}
if (returnType.isArray()) {
final List results = outputChannel.readAll();
final int size = results.size();
final Object array = Array.newInstance(returnType.getComponentType(), size);
for (int i = 0; i < size; ++i) {
Array.set(array, i, results.get(i));
}
return array;
}
}
return outputChannel.readNext();
}
return null;
}
@Nonnull
public TYPE buildProxy(@Nonnull final Class itf) {
if (!itf.isInterface()) {
throw new IllegalArgumentException(
"the specified class is not an interface: " + itf.getCanonicalName());
}
final InvocationHandler handler;
if (itf.isAssignableFrom(getTargetClass())) {
handler = new InterfaceInvocationHandler();
} else {
handler = new ProxyInvocationHandler();
}
final Object proxy =
Proxy.newProxyInstance(itf.getClassLoader(), new Class[]{itf}, handler);
return itf.cast(proxy);
}
@Nonnull
public TYPE buildProxy(@Nonnull final ClassToken itf) {
return itf.cast(buildProxy(itf.getRawClass()));
}
@Nonnull
@Override
public ObjectRoutineBuilder withConfiguration(
@Nullable final RoutineConfiguration configuration) {
super.withConfiguration(configuration);
return this;
}
@Nonnull
@Override
public ObjectRoutineBuilder withShareGroup(@Nullable final String group) {
super.withShareGroup(group);
return this;
}
@Nonnull
private Method getTargetMethod(@Nonnull final Method method,
@Nonnull final Class>[] targetParameterTypes) throws NoSuchMethodException {
String name = null;
Method targetMethod = null;
final Class> targetClass = getTargetClass();
final Bind annotation = method.getAnnotation(Bind.class);
if (annotation != null) {
name = annotation.value();
targetMethod = getAnnotatedMethod(name);
}
if (targetMethod == null) {
if (name == null) {
name = method.getName();
}
try {
targetMethod = targetClass.getMethod(name, targetParameterTypes);
} catch (final NoSuchMethodException ignored) {
}
if (targetMethod == null) {
targetMethod = targetClass.getDeclaredMethod(name, targetParameterTypes);
}
}
return targetMethod;
}
/**
* Invocation handler wrapping the target object instance.
*/
private class InterfaceInvocationHandler implements InvocationHandler {
private final RoutineConfiguration mConfiguration;
private final String mShareGroup;
/**
* Constructor.
*/
private InterfaceInvocationHandler() {
mShareGroup = getShareGroup();
mConfiguration = RoutineConfiguration.notNull(getConfiguration());
}
public Object invoke(final Object proxy, final Method method, final Object[] args) throws
Throwable {
final OutputChannel outputChannel =
method(mConfiguration, mShareGroup, method).callAsync(args);
final Class> returnType = method.getReturnType();
if (!Void.class.equals(boxingClass(returnType))) {
TimeDuration outputTimeout = null;
TimeoutActionType outputAction = null;
final Timeout timeoutAnnotation = method.getAnnotation(Timeout.class);
if (timeoutAnnotation != null) {
outputTimeout = fromUnit(timeoutAnnotation.value(), timeoutAnnotation.unit());
}
final TimeoutAction actionAnnotation = method.getAnnotation(TimeoutAction.class);
if (actionAnnotation != null) {
outputAction = actionAnnotation.value();
}
if (outputTimeout != null) {
outputChannel.afterMax(outputTimeout);
}
if (outputAction == TimeoutActionType.DEADLOCK) {
outputChannel.eventuallyDeadlock();
} else if (outputAction == TimeoutActionType.EXIT) {
outputChannel.eventuallyExit();
} else if (outputAction == TimeoutActionType.ABORT) {
outputChannel.eventuallyAbort();
}
return outputChannel.readNext();
}
return null;
}
}
/**
* Invocation handler adapting a different interface to the target object instance.
*/
private class ProxyInvocationHandler implements InvocationHandler {
private final RoutineConfiguration mConfiguration;
private final String mShareGroup;
/**
* Constructor.
*/
private ProxyInvocationHandler() {
mShareGroup = getShareGroup();
mConfiguration = RoutineConfiguration.notNull(getConfiguration());
}
@Nonnull
private Routine buildRoutine(@Nonnull final Method method,
@Nonnull final Method targetMethod, @Nullable final PassMode paramMode,
@Nullable final PassMode returnMode) {
String shareGroup = mShareGroup;
final RoutineConfiguration configuration = mConfiguration;
final Builder builder = RoutineConfiguration.builderFrom(configuration);
final ShareGroup shareGroupAnnotation = method.getAnnotation(ShareGroup.class);
if (shareGroupAnnotation != null) {
shareGroup = shareGroupAnnotation.value();
}
warn(configuration);
builder.withInputOrder(
(paramMode == PassMode.PARALLEL) ? OrderType.NONE : OrderType.PASSING_ORDER)
.withInputSize(Integer.MAX_VALUE)
.withInputTimeout(TimeDuration.ZERO)
.withOutputOrder((returnMode == PassMode.COLLECTION) ? OrderType.PASSING_ORDER
: OrderType.NONE)
.withOutputSize(Integer.MAX_VALUE)
.withOutputTimeout(TimeDuration.ZERO);
final Timeout timeoutAnnotation = method.getAnnotation(Timeout.class);
if (timeoutAnnotation != null) {
builder.withReadTimeout(timeoutAnnotation.value(), timeoutAnnotation.unit());
}
final TimeoutAction actionAnnotation = method.getAnnotation(TimeoutAction.class);
if (actionAnnotation != null) {
builder.onReadTimeout(actionAnnotation.value());
}
return getRoutine(builder.buildConfiguration(), shareGroup, targetMethod,
(paramMode == PassMode.COLLECTION),
(returnMode == PassMode.COLLECTION));
}
public Object invoke(final Object proxy, final Method method, final Object[] args) throws
Throwable {
final WeakReference> targetReference = getTargetReference();
if (targetReference == null) {
throw new IllegalStateException("the target reference must not be null");
}
final Object target = targetReference.get();
if (target == null) {
throw new IllegalStateException("the target object has been destroyed");
}
PassMode asyncParamMode = null;
PassMode asyncReturnMode = null;
Class> returnClass = null;
final Pass methodAnnotation = method.getAnnotation(Pass.class);
if (methodAnnotation != null) {
returnClass = methodAnnotation.value();
asyncReturnMode = getReturnMode(method);
}
final Class>[] targetParameterTypes = method.getParameterTypes();
final Annotation[][] annotations = method.getParameterAnnotations();
final int length = annotations.length;
for (int i = 0; i < length; ++i) {
final PassMode paramMode = getParamMode(method, i);
if (paramMode != null) {
asyncParamMode = paramMode;
for (final Annotation paramAnnotation : annotations[i]) {
if (paramAnnotation.annotationType() == Pass.class) {
final Pass passAnnotation = (Pass) paramAnnotation;
targetParameterTypes[i] = passAnnotation.value();
break;
}
}
}
}
Method targetMethod;
synchronized (sMethodCache) {
final Class> targetClass = getTargetClass();
final WeakIdentityHashMap> methodCache =
sMethodCache;
HashMap methodMap = methodCache.get(targetClass);
if (methodMap == null) {
methodMap = new HashMap();
methodCache.put(targetClass, methodMap);
}
targetMethod = methodMap.get(method);
if (targetMethod == null) {
try {
targetMethod = getTargetMethod(method, targetParameterTypes);
} catch (final NoSuchMethodException e) {
throw new IllegalArgumentException(e);
}
final Class> returnType = method.getReturnType();
final Class> targetReturnType = targetMethod.getReturnType();
boolean isError = false;
if (methodAnnotation == null) {
isError = !returnType.isAssignableFrom(targetReturnType);
} else {
if ((asyncReturnMode == PassMode.PARALLEL) && returnType.isArray()) {
isError = !boxingClass(returnType.getComponentType()).isAssignableFrom(
boxingClass(targetReturnType));
}
isError |= !returnClass.isAssignableFrom(targetReturnType);
}
if (isError) {
throw new IllegalArgumentException(
"the proxy method has incompatible return type: " + method);
}
}
}
final Routine routine =
buildRoutine(method, targetMethod, asyncParamMode, asyncReturnMode);
return callRoutine(routine, method, args, asyncParamMode, asyncReturnMode);
}
}
}