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.core;
 
 
 import java.util.List;
 import java.util.Map;
 
 
 
 import static com.gh.bmd.jrt.util.Reflection.boxingClass;
 import static com.gh.bmd.jrt.util.Reflection.findMethod;
 import static com.gh.bmd.jrt.util.Reflection.makeAccessible;

Utility class used to manage cached objects shared by routine builders.

Created by davide-maestroni on 3/23/15.

 
 public class RoutineBuilders {
 
     private static final WeakIdentityHashMap<Class<?>, Map<StringMethod>> sAliasCache =
             new WeakIdentityHashMap<Class<?>, Map<StringMethod>>();
 
     private static final WeakIdentityHashMap<Class<?>, Map<MethodMethodInfo>> sMethodCache =
             new WeakIdentityHashMap<Class<?>, Map<MethodMethodInfo>>();
 
     private static final WeakIdentityHashMap<ObjectMap<StringObject>> sMutexCache =
             new WeakIdentityHashMap<ObjectMap<StringObject>>();
 
     private static final WeakIdentityHashMap<Class<?>, Map<StringMethod>> sStaticAliasCache =
             new WeakIdentityHashMap<Class<?>, Map<StringMethod>>();

    
Avoid direct instantiation.
 
     protected RoutineBuilders() {
 
     }

    
Calls the specified target method from inside a routine invocation.

Parameters:
targetMethod the target method.
mutex the method mutex.
target the target instance.
objects the input objects.
result the invocation result channel.
inputMode the input transfer mode.
outputMode the output transfer mode.
Throws:
com.gh.bmd.jrt.channel.RoutineException in case of errors.
 
     @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
     public static void callFromInvocation(@Nonnull final Method targetMethod,
             @Nonnull final Object mutex, @Nonnull final Object target,
             @Nonnull final List<?> objects, @Nonnull final ResultChannel<Objectresult,
             @Nullable final InputMode inputMode, @Nullable final OutputMode outputMode) {
 
         makeAccessible(targetMethod);
 
         try {
 
             final Object methodResult;
 
            synchronized (mutex) {
                final Object[] args;
                if (inputMode == .) {
                    final Class<?> paramClass = targetMethod.getParameterTypes()[0];
                    if (paramClass.isArray()) {
                        final int size = objects.size();
                        final Object array = Array.newInstance(paramClass.getComponentType(), size);
                        for (int i = 0; i < size; ++i) {
                            Array.set(arrayiobjects.get(i));
                        }
                        args = new Object[]{array};
                    } else {
                        args = new Object[]{objects};
                    }
                } else {
                    args = objects.toArray(new Object[objects.size()]);
                }
                methodResult = targetMethod.invoke(targetargs);
            }
            final Class<?> returnType = targetMethod.getReturnType();
            if (!Void.class.equals(boxingClass(returnType))) {
                if (outputMode == .) {
                    if (returnType.isArray()) {
                        if (methodResult != null) {
                            result.orderByCall();
                            final int length = Array.getLength(methodResult);
                            for (int i = 0; i < length; ++i) {
                                result.pass(Array.get(methodResulti));
                            }
                        }
                    } else {
                        result.pass((Iterable<?>) methodResult);
                    }
                } else {
                    result.pass(methodResult);
                }
            }
        } catch (final RoutineException e) {
            throw e;
        } catch (final InvocationTargetException e) {
            throw new InvocationException(e.getCause());
        } catch (final Throwable t) {
            throw new InvocationException(t);
        }
    }

    
Gets the member method annotated with the specified alias name.

Parameters:
name the alias name.
targetClass the target class.
Returns:
the method.
Throws:
java.lang.IllegalArgumentException if no method with the specified alias name was found.
    @Nullable
    public static Method getAnnotatedMethod(@Nonnull final String name,
            @Nonnull final Class<?> targetClass) {
        return getAnnotatedMethod(nametargetClassfalse);
    }

    
Gets the class method annotated with the specified alias name.

Parameters:
name the alias name.
targetClass the target class.
Returns:
the method.
Throws:
java.lang.IllegalArgumentException if no method with the specified alias name was found.
    @Nullable
    public static Method getAnnotatedStaticMethod(@Nonnull final String name,
            @Nonnull final Class<?> targetClass) {
        return getAnnotatedMethod(nametargetClasstrue);
    }

    
Gets the input transfer mode associated to the specified method parameter.

Parameters:
method the proxy method.
index the index of the parameter.
Returns:
the input mode.
Throws:
java.lang.IllegalArgumentException if the method has been incorrectly annotated.
See also:
com.gh.bmd.jrt.annotation.Input Input
    @Nullable
    public static InputMode getInputMode(@Nonnull final Method methodfinal int index) {
        Input inputAnnotation = null;
        final Annotation[][] annotations = method.getParameterAnnotations();
        for (final Annotation annotation : annotations[index]) {
            if (annotation.annotationType() == Input.class) {
                inputAnnotation = (Inputannotation;
                break;
            }
        }
        if (inputAnnotation == null) {
            return null;
        }
        InputMode inputMode = inputAnnotation.mode();
        final Class<?> paramClass = inputAnnotation.value();
        final Class<?>[] parameterTypes = method.getParameterTypes();
        final Class<?> parameterType = parameterTypes[index];
        final int length = parameterTypes.length;
        final boolean isArray = parameterType.isArray();
        if (inputMode == .) {
            if (OutputChannel.class.isAssignableFrom(parameterType)) {
                if ((length == 1) && (paramClass.isArray() || paramClass.isAssignableFrom(
                        List.class))) {
                    inputMode = .;
                } else {
                    inputMode = .;
                }
            } else if (isArray || Iterable.class.isAssignableFrom(parameterType)) {
                if (isArray && !boxingClass(paramClass).isAssignableFrom(
                        boxingClass(parameterType.getComponentType()))) {
                    throw new IllegalArgumentException(
                            "[" + method + "] the async input array with mode " + .
                                    + " does not match the bound type: "
                                    + paramClass.getCanonicalName());
                }
                if (length > 1) {
                    throw new IllegalArgumentException(
                            "[" + method + "] an async input with mode " + .
                                    + " cannot be applied to a method taking " + length +
                                    " input parameters");
                }
                inputMode = .;
            } else {
                throw new IllegalArgumentException(
                        "[" + method + "] cannot automatically choose an "
                                + "input mode for an output of type: "
                                + parameterType.getCanonicalName());
            }
        } else if (inputMode == .) {
            if (!OutputChannel.class.isAssignableFrom(parameterType)) {
                throw new IllegalArgumentException(
                        "[" + method + "] an async input with mode " + .
                                + " must extends an " + OutputChannel.class.getCanonicalName());
            }
        } else if (inputMode == .) {
            if (!OutputChannel.class.isAssignableFrom(parameterType)) {
                throw new IllegalArgumentException(
                        "[" + method + "] an async input with mode " + .
                                + " must extends an " + OutputChannel.class.getCanonicalName());
            }
            if (!paramClass.isArray() && !paramClass.isAssignableFrom(List.class)) {
                throw new IllegalArgumentException(
                        "[" + method + "] an async input with 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 mode " + . +
                                " cannot be applied to a method taking " + length
                                + " input parameters");
            }
        } else { // InputMode.PARALLEL
            if (!isArray && !Iterable.class.isAssignableFrom(parameterType)) {
                throw new IllegalArgumentException(
                        "[" + method + "] an async input with 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 mode " + .
                                + " does not match the bound type: "
                                + paramClass.getCanonicalName());
            }
            if (length > 1) {
                throw new IllegalArgumentException(
                        "[" + method + "] an async input with mode " + .
                                + " cannot be applied to a method taking " + length
                                + " input parameters");
            }
        }
        return inputMode;
    }

    
Gets the inputs transfer mode associated to the specified method.

Parameters:
method the proxy method.
Returns:
the input mode.
Throws:
java.lang.IllegalArgumentException if the method has been incorrectly annotated.
See also:
com.gh.bmd.jrt.annotation.Inputs Inputs
    @Nullable
    public static InputMode getInputsMode(@Nonnull final Method method) {
        final Inputs methodAnnotation = method.getAnnotation(Inputs.class);
        if (methodAnnotation == null) {
            return null;
        }
        if (method.getParameterTypes().length > 0) {
            throw new IllegalArgumentException(
                    "methods annotated with " + Inputs.class.getSimpleName()
                            + " must have no input parameters: " + method);
        }
        if (!method.getReturnType().isAssignableFrom(InvocationChannel.class)) {
            throw new IllegalArgumentException(
                    "the proxy method has incompatible return type: " + method);
        }
        final Class<?>[] parameterTypes = methodAnnotation.value();
        InputMode inputMode = methodAnnotation.mode();
        if (inputMode == .) {
            if (parameterTypes.length == 1) {
                final Class<?> parameterType = parameterTypes[0];
                if (parameterType.isArray() || parameterType.isAssignableFrom(List.class)) {
                    inputMode = .;
                } else {
                    inputMode = .;
                }
            } else {
                inputMode = .;
            }
        } else if (inputMode == .) {
            final Class<?> parameterType = parameterTypes[0];
            if (!parameterType.isArray() && !parameterType.isAssignableFrom(List.class)) {
                throw new IllegalArgumentException(
                        "[" + method + "] an async input with mode " + .
                                + " must be bound to an array or a superclass of "
                                + List.class.getCanonicalName());
            }
            if (parameterTypes.length > 1) {
                throw new IllegalArgumentException(
                        "[" + method + "] an async input with mode " + . +
                                " cannot be applied to a method taking " + parameterTypes.length
                                + " input parameters");
            }
        } else if (inputMode == .) {
            if (parameterTypes.length > 1) {
                throw new IllegalArgumentException(
                        "[" + method + "] an async input with mode " + . +
                                " cannot be applied to a method taking " + parameterTypes.length
                                + " input parameters");
            }
        }
        return inputMode;
    }

    
Gets the output transfer mode of the return type of the specified method.

Parameters:
method the proxy method.
targetReturnType the target return type.
Returns:
the output mode.
Throws:
java.lang.IllegalArgumentException if the method has been incorrectly annotated.
See also:
com.gh.bmd.jrt.annotation.Output Output
    @Nullable
    public static OutputMode getOutputMode(@Nonnull final Method method,
            @Nonnull final Class<?> targetReturnType) {
        final Output outputAnnotation = method.getAnnotation(Output.class);
        if (outputAnnotation == null) {
            return null;
        }
        final Class<?> returnType = method.getReturnType();
        OutputMode outputMode = outputAnnotation.value();
        if (outputMode == .) {
            if (returnType.isArray() || returnType.isAssignableFrom(List.class)) {
                if (returnType.isArray() && !boxingClass(
                        returnType.getComponentType()).isAssignableFrom(
                        boxingClass(targetReturnType))) {
                    throw new IllegalArgumentException(
                            "[" + method + "] the async output array with mode "
                                    + . + " does not match the bound type: "
                                    + targetReturnType.getCanonicalName());
                }
                outputMode = .;
            } else if (returnType.isAssignableFrom(OutputChannel.class)) {
                if (targetReturnType.isArray() || Iterable.class.isAssignableFrom(
                        targetReturnType)) {
                    outputMode = .;
                } else {
                    outputMode = .;
                }
            } else {
                throw new IllegalArgumentException(
                        "[" + method + "] cannot automatically choose an "
                                + "output mode for an output of type: "
                                + returnType.getCanonicalName());
            }
        } else if (outputMode == .) {
            if (!returnType.isAssignableFrom(OutputChannel.class)) {
                final String channelClassName = OutputChannel.class.getCanonicalName();
                throw new IllegalArgumentException(
                        "[" + method + "] an async output with mode " + .
                                + " must be a superclass of " + channelClassName);
            }
        } else if (outputMode == .) {
            if (!returnType.isAssignableFrom(OutputChannel.class)) {
                final String channelClassName = OutputChannel.class.getCanonicalName();
                throw new IllegalArgumentException(
                        "[" + method + "] an async output with mode " + .
                                + " must be a superclass of " + channelClassName);
            }
            if (!targetReturnType.isArray() && !Iterable.class.isAssignableFrom(targetReturnType)) {
                throw new IllegalArgumentException(
                        "[" + method + "] an async output with mode " + .
                                + " must be bound to an array or a type implementing an "
                                + Iterable.class.getCanonicalName());
            }
        } else { // OutputMode.COLLECTION
            if (!returnType.isArray() && !returnType.isAssignableFrom(List.class)) {
                throw new IllegalArgumentException(
                        "[" + method + "] an async output with mode " + .
                                + " must be an array or a superclass of "
                                + List.class.getCanonicalName());
            }
            if (returnType.isArray() && !boxingClass(
                    returnType.getComponentType()).isAssignableFrom(
                    boxingClass(targetReturnType))) {
                throw new IllegalArgumentException(
                        "[" + method + "] the async output array with mode " + .
                                + " does not match the bound type: "
                                + targetReturnType.getCanonicalName());
            }
        }
        return outputMode;
    }

    
Returns the cached mutex associated with the specified target and share group.
If the cache was empty, it is filled with a new object automatically created.

Parameters:
target the target object instance.
shareGroup the share group name.
Returns:
the cached mutex.
    @Nonnull
    public static Object getSharedMutex(@Nonnull final Object target,
            @Nullable final String shareGroup) {
        synchronized () {
            final WeakIdentityHashMap<ObjectMap<StringObject>> mutexCache = ;
            Map<StringObjectmutexMap = mutexCache.get(target);
            if (mutexMap == null) {
                mutexMap = new HashMap<StringObject>();
                mutexCache.put(targetmutexMap);
            }
            final String groupName = (shareGroup != null) ? shareGroup : .;
            Object mutex = mutexMap.get(groupName);
            if (mutex == null) {
                mutex = new Object();
                mutexMap.put(groupNamemutex);
            }
            return mutex;
        }
    }

    
Gets info about the method targeted by the specified proxy one.

Parameters:
proxyMethod the proxy method.
targetClass the target class.
Returns:
the method info.
Throws:
java.lang.IllegalArgumentException if no target method was found.
    @Nonnull
    public static MethodInfo getTargetMethodInfo(@Nonnull final Method proxyMethod,
            @Nonnull final Class<?> targetClass) {
        MethodInfo methodInfo;
        synchronized () {
            final WeakIdentityHashMap<Class<?>, Map<MethodMethodInfo>> methodCache = ;
            Map<MethodMethodInfomethodMap = methodCache.get(targetClass);
            if (methodMap == null) {
                methodMap = new HashMap<MethodMethodInfo>();
                methodCache.put(targetClassmethodMap);
            }
            methodInfo = methodMap.get(proxyMethod);
            if (methodInfo == null) {
                InputMode inputMode = null;
                OutputMode outputMode = null;
                final Class<?>[] targetParameterTypes;
                final Inputs inputsAnnotation = proxyMethod.getAnnotation(Inputs.class);
                final Output outputAnnotation = proxyMethod.getAnnotation(Output.class);
                if (inputsAnnotation != null) {
                    targetParameterTypes = inputsAnnotation.value();
                    inputMode = getInputsMode(proxyMethod);
                    outputMode = .;
                } else {
                    targetParameterTypes = proxyMethod.getParameterTypes();
                    final Annotation[][] annotations = proxyMethod.getParameterAnnotations();
                    final int length = annotations.length;
                    for (int i = 0; i < length; ++i) {
                        final InputMode paramMode = getInputMode(proxyMethodi);
                        if (paramMode != null) {
                            inputMode = paramMode;
                            for (final Annotation paramAnnotation : annotations[i]) {
                                if (paramAnnotation.annotationType() == Input.class) {
                                    targetParameterTypes[i] = ((InputparamAnnotation).value();
                                    break;
                                }
                            }
                        }
                    }
                }
                final Method targetMethod =
                        getTargetMethod(proxyMethodtargetClasstargetParameterTypes);
                final Class<?> returnType = proxyMethod.getReturnType();
                final Class<?> targetReturnType = targetMethod.getReturnType();
                boolean isError = false;
                if (outputAnnotation != null) {
                    outputMode = getOutputMode(proxyMethodtargetReturnType);
                    if ((outputMode == .) && returnType.isArray()) {
                        isError = !boxingClass(returnType.getComponentType()).isAssignableFrom(
                                boxingClass(targetReturnType));
                    }
                } else if (inputsAnnotation == null) {
                    isError = !returnType.isAssignableFrom(targetReturnType);
                }
                if (isError) {
                    throw new IllegalArgumentException(
                            "the proxy method has incompatible return type: " + proxyMethod);
                }
                methodInfo = new MethodInfo(targetMethodinputModeoutputMode);
                methodMap.put(proxyMethodmethodInfo);
            }
        }
        return methodInfo;
    }

    
Invokes the routine wrapping the specified method.

Parameters:
routine the routine to be called.
method the target method.
args the method arguments.
inputMode the input transfer mode.
outputMode the output transfer mode.
Returns:
the invocation output.
Throws:
com.gh.bmd.jrt.channel.RoutineException in case of errors.
    @Nullable
    @SuppressWarnings("unchecked")
    public static Object invokeRoutine(@Nonnull final Routine<ObjectObjectroutine,
            @Nonnull final Method method, @Nonnull final Object[] args,
            @Nullable final InputMode inputMode, @Nullable final OutputMode outputMode) {
        if (method.getAnnotation(Inputs.class) != null) {
            return (inputMode == .) ? routine.parallelInvoke()
                    : routine.asyncInvoke();
        }
        final Class<?> returnType = method.getReturnType();
        final OutputChannel<ObjectoutputChannel;
        if (inputMode == .) {
            final InvocationChannel<ObjectObjectinvocationChannel = routine.parallelInvoke();
            final Class<?> parameterType = method.getParameterTypes()[0];
            final Object arg = args[0];
            if (arg == null) {
                invocationChannel.pass((Iterable<Object>) null);
            } else if (OutputChannel.class.isAssignableFrom(parameterType)) {
                invocationChannel.pass((OutputChannel<Object>) arg);
            } else if (parameterType.isArray()) {
                final int length = Array.getLength(arg);
                for (int i = 0; i < lengthi++) {
                    invocationChannel.pass(Array.get(argi));
                }
            } else {
                final Iterable<?> iterable = (Iterable<?>) arg;
                for (final Object input : iterable) {
                    invocationChannel.pass(input);
                }
            }
            outputChannel = invocationChannel.result();
        } else if (inputMode == .) {
            final InvocationChannel<ObjectObjectinvocationChannel =
                    routine.asyncInvoke().orderByCall();
            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])) {
                    invocationChannel.pass((OutputChannel<Object>) arg);
                } else {
                    invocationChannel.pass(arg);
                }
            }
            outputChannel = invocationChannel.result();
        } else if (inputMode == .) {
            outputChannel = routine.asyncInvoke()
                                   .orderByCall()
                                   .pass((OutputChannel<Object>) args[0])
                                   .result();
        } else {
            outputChannel = routine.asyncCall(args);
        }
        if (!Void.class.equals(boxingClass(returnType))) {
            if (outputMode != null) {
                if (OutputChannel.class.isAssignableFrom(returnType)) {
                    return outputChannel;
                }
                if (returnType.isAssignableFrom(List.class)) {
                    return outputChannel.all();
                }
                if (returnType.isArray()) {
                    final List<Objectresults = outputChannel.all();
                    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.all().iterator().next();
        }
        outputChannel.checkComplete();
        return null;
    }
    private static void fillMap(@Nonnull final Map<StringMethodmap,
            @Nonnull final Method[] methodsboolean isStatic) {
        for (final Method method : methods) {
            final boolean isStaticMethod = Modifier.isStatic(method.getModifiers());
            if (isStatic) {
                if (!isStaticMethod) {
                    continue;
                }
            } else if (isStaticMethod) {
                continue;
            }
            final Alias annotation = method.getAnnotation(Alias.class);
            if (annotation != null) {
                final String name = annotation.value();
                if (map.containsKey(name)) {
                    throw new IllegalArgumentException(
                            "the name '" + name + "' has already been used to identify a different"
                                    + " method");
                }
                map.put(namemethod);
            }
        }
    }
    @Nullable
    @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
    private static Method getAnnotatedMethod(@Nonnull final String name,
            @Nonnull final Class<?> targetClassfinal boolean isStatic) {
        final WeakIdentityHashMap<Class<?>, Map<StringMethod>> aliasCache =
                (isStatic) ?  : ;
        synchronized (aliasCache) {
            Map<StringMethodmethodMap = aliasCache.get(targetClass);
            if (methodMap == null) {
                methodMap = new HashMap<StringMethod>();
                fillMap(methodMaptargetClass.getMethods(), isStatic);
                final HashMap<StringMethoddeclaredMethodMap = new HashMap<StringMethod>();
                fillMap(declaredMethodMaptargetClass.getDeclaredMethods(), isStatic);
                for (final Entry<StringMethodmethodEntry : declaredMethodMap.entrySet()) {
                    final String methodName = methodEntry.getKey();
                    if (!methodMap.containsKey(methodName)) {
                        methodMap.put(methodNamemethodEntry.getValue());
                    }
                }
                aliasCache.put(targetClassmethodMap);
            }
            return methodMap.get(name);
        }
    }
    @Nonnull
    private static Method getTargetMethod(@Nonnull final Method method,
            @Nonnull final Class<?> targetClass, @Nonnull final Class<?>[] targetParameterTypes) {
        String name = null;
        Method targetMethod = null;
        final Alias annotation = method.getAnnotation(Alias.class);
        if (annotation != null) {
            name = annotation.value();
            targetMethod = getAnnotatedMethod(nametargetClassfalse);
        }
        if (targetMethod == null) {
            if (name == null) {
                name = method.getName();
            }
            targetMethod = findMethod(targetClassnametargetParameterTypes);
        }
        return targetMethod;
    }

    
Data class storing information about the target method.
    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD",
            justification = "this is an immutable data class")
    public static class MethodInfo {

        
The input transfer mode.
        public final InputMode inputMode;

        
The target method.
        public final Method method;

        
The output transfer mode.
        public final OutputMode outputMode;

        
Constructor.

Parameters:
method the target method.
inputMode the input mode.
outputMode the output mode.
        private MethodInfo(@Nonnull final Method method, @Nullable final InputMode inputMode,
                @Nullable final OutputMode outputMode) {
            this. = method;
            this. = inputMode;
            this. = outputMode;
        }
    }
New to GrepCode? Check out our FAQ X