Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * 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.routine;
 
 
 import java.util.List;
 
 
 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.

 
         implements ObjectRoutineBuilder {
 
     private static final WeakIdentityHashMap<ObjectHashMap<MethodMethod>> sMethodCache =
             new WeakIdentityHashMap<ObjectHashMap<MethodMethod>>();

    
Constructor.

Parameters:
target the target object instance.
Throws:
java.lang.IllegalArgumentException if a duplicate name in the annotations is detected.
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<ObjectObjectroutine,
             @Nonnull final Method method, @Nonnull final Object[] args,
             @Nullable final PassingMode paramMode,
             @Nullable final PassingMode returnMode) { //TODO returnType
 
         final Class<?> returnType = method.getReturnType();
         final OutputChannel<ObjectoutputChannel;
 
         if (paramMode == .) {
 
             final ParameterChannel<ObjectObjectparameterChannel = routine.invokeParallel();
             final Class<?> parameterType = method.getParameterTypes()[0];
             final Object arg = args[0];
 
             if (arg == null) {
 
                 parameterChannel.pass((Iterable<Object>) null);
 
             } else if (OutputChannel.class.isAssignableFrom(parameterType)) {
 
                 parameterChannel.pass((OutputChannel<Object>) arg);
 
             } else if (parameterType.isArray()) {
 
                 final int length = Array.getLength(arg);
 
                 for (int i = 0; i < lengthi++) {
                    parameterChannel.pass(Array.get(argi));
                }
            } else {
                final Iterable<?> iterable = (Iterable<?>) arg;
                for (final Object input : iterable) {
                    parameterChannel.pass(input);
                }
            }
            outputChannel = parameterChannel.result();
        } else if (paramMode == .) {
            final ParameterChannel<ObjectObjectparameterChannel = 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<Object>) arg);
                } else {
                    parameterChannel.pass(arg);
                }
            }
            outputChannel = parameterChannel.result();
        } else if (paramMode == .) {
            final ParameterChannel<ObjectObjectparameterChannel = routine.invokeAsync();
            outputChannel = parameterChannel.pass((OutputChannel<Object>) 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<Objectresults = outputChannel.readAll();
                    final int size = results.size();
                    final Object array = Array.newInstance(returnType.getComponentType(), size);
                    for (int i = 0; i < size; ++i) {
                        Array.set(arrayiresults.get(i));
                    }
                    return array;
                }
            }
            return outputChannel.readNext();
        }
        return null;
    }
    @Nonnull
    private static PassingMode getParamMode(@Nonnull final Method method,
            @Nonnull final Pass passAnnotation, @Nonnull final Class<?> parameterType,
            final int length) {
        PassingMode passingMode = passAnnotation.mode();
        final Class<?> paramClass = passAnnotation.value();
        final boolean isArray = parameterType.isArray();
        if (passingMode == .) {
            if (OutputChannel.class.isAssignableFrom(parameterType)) {
                if ((length == 1) && (paramClass.isArray() || paramClass.isAssignableFrom(
                        List.class))) {
                    passingMode = .;
                } else {
                    passingMode = .;
                }
            } else if (isArray || Iterable.class.isAssignableFrom(parameterType)) {
                if (isArray && !boxingClass(paramClass).isAssignableFrom(
                        boxingClass(parameterType.getComponentType()))) {
                    throw new IllegalArgumentException(
                            "[" + method + "] the async input array with passing mode "
                                    + . + " does not match the bound type: "
                                    + paramClass.getCanonicalName());
                }
                if (length > 1) {
                    throw new IllegalArgumentException(
                            "[" + method + "] an async input with passing mode "
                                    + .
                                    + " cannot be applied to a method taking " + length +
                                    " input parameters");
                }
                passingMode = .;
            } else {
                throw new IllegalArgumentException("[" + method + "] cannot automatically choose a "
                                                           + "passing mode for an output of type: "
                                                           + parameterType.getCanonicalName());
            }
        } else if (passingMode == .) {
            if (!OutputChannel.class.isAssignableFrom(parameterType)) {
                throw new IllegalArgumentException(
                        "[" + method + "] an async input with passing mode " + .
                                + " must extends an " + OutputChannel.class.getCanonicalName());
            }
        } else if (passingMode == .) {
            if (!OutputChannel.class.isAssignableFrom(parameterType)) {
                throw new IllegalArgumentException(
                        "[" + method + "] an async input with passing mode "
                                + . + " must extends an " + OutputChannel.class
                                .getCanonicalName());
            }
            if (!paramClass.isArray() && !paramClass.isAssignableFrom(List.class)) {
                throw new IllegalArgumentException(
                        "[" + method + "] an async input with passing mode "
                                + .
                                + " must be bound to an array or a superclass of "
                                + List.class.getCanonicalName());
            }
            if (length > 1) {
                throw new IllegalArgumentException(
                        "[" + method + "] an async input with passing mode "
                                + . +
                                " cannot be applied to a method taking " + length
                                + " input parameters");
            }
        } else { // PassingMode.PARALLEL
            if (!isArray && !Iterable.class.isAssignableFrom(parameterType)) {
                throw new IllegalArgumentException(
                        "[" + method + "] an async input with passing mode " + .
                                + " must be an array or implement an "
                                + Iterable.class.getCanonicalName());
            }
            if (isArray && !boxingClass(paramClass).isAssignableFrom(
                    boxingClass(parameterType.getComponentType()))) {
                throw new IllegalArgumentException(
                        "[" + method + "] the async input array with passing mode "
                                + . + " does not match the bound type: "
                                + paramClass.getCanonicalName());
            }
            if (length > 1) {
                throw new IllegalArgumentException(
                        "[" + method + "] an async input with passing mode " + .
                                + " cannot be applied to a method taking " + length
                                + " input parameters");
            }
        }
        return passingMode;
    }
    @Nonnull
    private static PassingMode getReturnMode(@Nonnull final Method method,
            @Nonnull final Pass annotation, @Nonnull final Class<?> returnType) {
        PassingMode passingMode = annotation.mode();
        if (passingMode == .) {
            if (returnType.isArray() || returnType.isAssignableFrom(List.class)) {
                final Class<?> returnClass = annotation.value();
                if (returnType.isArray() && !boxingClass(
                        returnType.getComponentType()).isAssignableFrom(boxingClass(returnClass))) {
                    throw new IllegalArgumentException(
                            "[" + method + "] the async output array with passing mode "
                                    + . + " does not match the bound type: "
                                    + returnClass.getCanonicalName());
                }
                passingMode = .;
            } else if (returnType.isAssignableFrom(OutputChannel.class)) {
                final Class<?> returnClass = annotation.value();
                if (returnClass.isArray() || Iterable.class.isAssignableFrom(returnClass)) {
                    passingMode = .;
                } else {
                    passingMode = .;
                }
            } else {
                throw new IllegalArgumentException("[" + method + "] cannot automatically choose a "
                                                           + "passing mode for an input of type: "
                                                           + returnType.getCanonicalName());
            }
        } else if (passingMode == .) {
            if (!returnType.isAssignableFrom(OutputChannel.class)) {
                final String channelClassName = OutputChannel.class.getCanonicalName();
                throw new IllegalArgumentException(
                        "[" + method + "] an async output with passing mode " + .
                                + " must be a superclass of " + channelClassName);
            }
        } else if (passingMode == .) {
            if (!returnType.isAssignableFrom(OutputChannel.class)) {
                final String channelClassName = OutputChannel.class.getCanonicalName();
                throw new IllegalArgumentException(
                        "[" + method + "] an async output with passing mode " + .
                                + " must be a superclass of " + channelClassName);
            }
            final Class<?> returnClass = annotation.value();
            if (!returnClass.isArray() && !Iterable.class.isAssignableFrom(returnClass)) {
                throw new IllegalArgumentException(
                        "[" + method + "] an async output with passing mode "
                                + .
                                + " must be bound to an array or a type implementing an "
                                + Iterable.class.getCanonicalName());
            }
        } else { // PassingMode.PARALLEL
            if (!returnType.isArray() && !returnType.isAssignableFrom(List.class)) {
                throw new IllegalArgumentException(
                        "[" + method + "] an async output with passing mode " + .
                                + " must be an array or a superclass " +
                                "of " + List.class.getCanonicalName());
            }
            final Class<?> returnClass = annotation.value();
            if (returnType.isArray() && !boxingClass(
                    returnType.getComponentType()).isAssignableFrom(boxingClass(returnClass))) {
                throw new IllegalArgumentException(
                        "[" + method + "] the async output array with passing mode "
                                + . + " does not match the bound type: "
                                + returnClass.getCanonicalName());
            }
        }
        return passingMode;
    }
    @Nonnull
    public <TYPE> TYPE buildProxy(@Nonnull final Class<TYPE> 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 ObjectInvocationHandler();
        } else {
            handler = new InterfaceInvocationHandler();
        }
        final Object proxy =
                Proxy.newProxyInstance(itf.getClassLoader(), new Class[]{itf}, handler);
        return itf.cast(proxy);
    }
    @Nonnull
    public <TYPE> TYPE buildProxy(@Nonnull final ClassToken<TYPE> itf) {
        return itf.cast(buildProxy(itf.getRawClass()));
    }
    @Nonnull
    @Override
            @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<?>[] targetParameterTypesthrows NoSuchMethodException {
        final Class<?> targetClass = getTargetClass();
        final Bind annotation = method.getAnnotation(Bind.class);
        String name = null;
        Method targetMethod = null;
        if (annotation != null) {
            name = annotation.value();
            targetMethod = getAnnotatedMethod(name);
        }
        if (targetMethod == null) {
            if (name == null) {
                name = method.getName();
            }
            try {
                targetMethod = targetClass.getMethod(nametargetParameterTypes);
            } catch (final NoSuchMethodException ignored) {
            }
            if (targetMethod == null) {
                targetMethod = targetClass.getDeclaredMethod(nametargetParameterTypes);
            }
        }
        return targetMethod;
    }

    
Invocation handler adapting a different interface to the target object instance.
    private class InterfaceInvocationHandler implements InvocationHandler {
        private final RoutineConfiguration mConfiguration;
        private final String mShareGroup;

        
Constructor.
        private InterfaceInvocationHandler() {
             = getShareGroup();
             = RoutineConfiguration.notNull(getConfiguration());
        }
        public Object invoke(final Object proxyfinal Method methodfinal Object[] argsthrows
                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");
            }
            final Class<?> returnType = method.getReturnType();
            final Class<?>[] targetParameterTypes = method.getParameterTypes();
            PassingMode asyncParamMode = null;
            PassingMode asyncReturnMode = null;
            Class<?> returnClass = null;
            final Pass methodAnnotation = method.getAnnotation(Pass.class);
            if (methodAnnotation != null) {
                returnClass = methodAnnotation.value();
                asyncReturnMode = getReturnMode(methodmethodAnnotationreturnType);
            }
            final Annotation[][] annotations = method.getParameterAnnotations();
            final int length = annotations.length;
            for (int i = 0; i < length; ++i) {
                final Annotation[] paramAnnotations = annotations[i];
                for (final Annotation paramAnnotation : paramAnnotations) {
                    if (paramAnnotation.annotationType() != Pass.class) {
                        continue;
                    }
                    final Pass passAnnotation = (PassparamAnnotation;
                    asyncParamMode =
                            getParamMode(methodpassAnnotationtargetParameterTypes[i], length);
                    targetParameterTypes[i] = passAnnotation.value();
                }
            }
            Method targetMethod;
            synchronized () {
                final WeakIdentityHashMap<ObjectHashMap<MethodMethod>> methodCache =
                        ;
                HashMap<MethodMethodmethodMap = methodCache.get(target);
                if (methodMap == null) {
                    methodMap = new HashMap<MethodMethod>();
                    methodCache.put(targetmethodMap);
                }
                targetMethod = methodMap.get(method);
                if (targetMethod == null) {
                    try {
                        targetMethod = getTargetMethod(methodtargetParameterTypes);
                    } catch (final NoSuchMethodException e) {
                        throw new IllegalArgumentException(e);
                    }
                    final Class<?> targetReturnType = targetMethod.getReturnType();
                    boolean isError = false;
                    if (methodAnnotation == null) {
                        isError = !returnType.isAssignableFrom(targetReturnType);
                    } else {
                        if ((asyncReturnMode == .) && returnType.isArray()) {
                            isError = !boxingClass(returnType.getComponentType()).isAssignableFrom(
                                    boxingClass(targetReturnType));
                        }
                        isError |= !returnClass.isAssignableFrom(targetReturnType);
                    }
                    if (isError) {
                        throw new IllegalArgumentException(
                                "the bound method has incompatible return type: " + targetMethod);
                    }
                }
            }
            final Routine<ObjectObjectroutine =
                    buildRoutine(methodtargetMethodasyncParamModeasyncReturnMode);
            return callRoutine(routinemethodargsasyncParamModeasyncReturnMode);
        }
        @Nonnull
        private Routine<ObjectObjectbuildRoutine(@Nonnull final Method method,
                @Nonnull final Method targetMethod, @Nullable final PassingMode paramMode,
                @Nullable final PassingMode returnMode) {
            String shareGroup = ;
            final RoutineConfiguration configuration = ;
            final Builder builder = RoutineConfiguration.builderFrom(configuration);
            final Share shareAnnotation = method.getAnnotation(Share.class);
            if (shareAnnotation != null) {
                final String annotationShareGroup = shareAnnotation.value();
                if (!..equals(annotationShareGroup)) {
                    shareGroup = annotationShareGroup;
                }
            }
            warn(configuration);
            builder.withInputOrder(
                    (paramMode == .) ? . : .)
                   .withInputSize(.)
                   .withInputTimeout(.)
                   .withOutputOrder(.)
                   .withOutputSize(.)
                   .withOutputTimeout(.);
            final Timeout timeoutAnnotation = method.getAnnotation(Timeout.class);
            if (timeoutAnnotation != null) {
                builder.withReadTimeout(timeoutAnnotation.value(), timeoutAnnotation.unit())
                       .onReadTimeout(timeoutAnnotation.action());
            }
            return getRoutine(builder.buildConfiguration(), shareGrouptargetMethod,
                              (paramMode == .),
                              (returnMode == .));
        }
    }

    
Invocation handler wrapping the target object instance.
    private class ObjectInvocationHandler implements InvocationHandler {
        private final RoutineConfiguration mConfiguration;
        private final String mShareGroup;

        
Constructor.
        private ObjectInvocationHandler() {
             = getShareGroup();
             = RoutineConfiguration.notNull(getConfiguration());
        }
        public Object invoke(final Object proxyfinal Method methodfinal Object[] argsthrows
                Throwable {
            final OutputChannel<ObjectoutputChannel =
                    method(method).callAsync(args);
            final Class<?> returnType = method.getReturnType();
            if (!Void.class.equals(boxingClass(returnType))) {
                final Timeout methodAnnotation = method.getAnnotation(Timeout.class);
                TimeDuration outputTimeout = null;
                TimeoutAction outputAction = null;
                if (methodAnnotation != null) {
                    outputTimeout = fromUnit(methodAnnotation.value(), methodAnnotation.unit());
                    outputAction = methodAnnotation.action();
                }
                if (outputTimeout != null) {
                    outputChannel.afterMax(outputTimeout);
                }
                if (outputAction == .) {
                    outputChannel.eventuallyDeadlock();
                } else if (outputAction == .) {
                    outputChannel.eventuallyExit();
                } else if (outputAction == .) {
                    outputChannel.eventuallyAbort();
                }
                return outputChannel.readNext();
            }
            return null;
        }
    }
New to GrepCode? Check out our FAQ X