All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gh.bmd.jrt.core.DefaultClassRoutineBuilder Maven / Gradle / Ivy

There is a newer version: 5.9.0
Show newest version
/*
 * 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.Input.InputMode;
import com.gh.bmd.jrt.annotation.Output.OutputMode;
import com.gh.bmd.jrt.annotation.Priority;
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.ClassRoutineBuilder;
import com.gh.bmd.jrt.builder.InvocationConfiguration;
import com.gh.bmd.jrt.builder.ProxyConfiguration;
import com.gh.bmd.jrt.channel.ResultChannel;
import com.gh.bmd.jrt.invocation.FunctionInvocation;
import com.gh.bmd.jrt.invocation.Invocation;
import com.gh.bmd.jrt.invocation.InvocationFactory;
import com.gh.bmd.jrt.routine.Routine;
import com.gh.bmd.jrt.util.WeakIdentityHashMap;

import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import static com.gh.bmd.jrt.core.RoutineBuilders.callFromInvocation;
import static com.gh.bmd.jrt.core.RoutineBuilders.getAnnotatedStaticMethod;
import static com.gh.bmd.jrt.core.RoutineBuilders.getSharedMutex;
import static com.gh.bmd.jrt.util.Reflection.findMethod;

/**
 * Class implementing a builder of routines wrapping a class method.
 * 

* Created by davide-maestroni on 9/21/14. */ class DefaultClassRoutineBuilder implements ClassRoutineBuilder, InvocationConfiguration.Configurable, ProxyConfiguration.Configurable { private static final WeakIdentityHashMap>> sRoutineCache = new WeakIdentityHashMap>>(); private final Class mTargetClass; private final WeakReference mTargetReference; private InvocationConfiguration mInvocationConfiguration = InvocationConfiguration.DEFAULT_CONFIGURATION; private ProxyConfiguration mProxyConfiguration = ProxyConfiguration.DEFAULT_CONFIGURATION; /** * Constructor. * * @param targetClass the target class. * @throws java.lang.IllegalArgumentException if the specified class represents an interface. */ DefaultClassRoutineBuilder(@Nonnull final Class targetClass) { if (targetClass.isInterface()) { throw new IllegalArgumentException( "the target class must not be an interface: " + targetClass.getName()); } mTargetClass = targetClass; mTargetReference = null; } /** * Constructor. * * @param target the target object. */ DefaultClassRoutineBuilder(@Nonnull final Object target) { mTargetClass = target.getClass(); mTargetReference = new WeakReference(target); } @Nonnull public Routine aliasMethod(@Nonnull final String name) { final Method method = getAnnotatedStaticMethod(name, mTargetClass); if (method == null) { throw new IllegalArgumentException( "no annotated method with alias '" + name + "' has been found"); } return method(method); } @Nonnull public Routine method(@Nonnull final String name, @Nonnull final Class... parameterTypes) { return method(findMethod(mTargetClass, name, parameterTypes)); } @Nonnull public Routine method(@Nonnull final Method method) { return method(mInvocationConfiguration, mProxyConfiguration, method); } @Nonnull public InvocationConfiguration.Builder invocations() { final InvocationConfiguration configuration = mInvocationConfiguration; return new InvocationConfiguration.Builder(this, configuration); } @Nonnull public ProxyConfiguration.Builder proxies() { final ProxyConfiguration configuration = mProxyConfiguration; return new ProxyConfiguration.Builder(this, configuration); } @Nonnull @SuppressWarnings("ConstantConditions") public ClassRoutineBuilder setConfiguration(@Nonnull final ProxyConfiguration configuration) { if (configuration == null) { throw new NullPointerException("the proxy configuration must not be null"); } mProxyConfiguration = configuration; return this; } @Nonnull @SuppressWarnings("ConstantConditions") public ClassRoutineBuilder setConfiguration( @Nonnull final InvocationConfiguration configuration) { if (configuration == null) { throw new NullPointerException("the invocation configuration must not be null"); } mInvocationConfiguration = configuration; return this; } /** * Returns the internal invocation configuration. * * @return the configuration. */ @Nonnull protected InvocationConfiguration getInvocationConfiguration() { return mInvocationConfiguration; } /** * Returns the internal proxy configuration. * * @return the configuration. */ @Nonnull protected ProxyConfiguration getProxyConfiguration() { return mProxyConfiguration; } /** * Gets or creates the routine. * * @param configuration the invocation configuration. * @param shareGroup the share group name. * @param method the method to wrap. * @param inputMode the input transfer mode. * @param outputMode the output transfer mode. * @return the routine instance. */ @Nonnull @SuppressWarnings("unchecked") protected Routine getRoutine( @Nonnull final InvocationConfiguration configuration, @Nullable final String shareGroup, @Nonnull final Method method, @Nullable final InputMode inputMode, @Nullable final OutputMode outputMode) { final Object target = (mTargetReference != null) ? mTargetReference.get() : mTargetClass; if (target == null) { throw new IllegalStateException("the target object has been destroyed"); } synchronized (sRoutineCache) { final WeakIdentityHashMap>> routineCache = sRoutineCache; HashMap> routineMap = routineCache.get(target); if (routineMap == null) { routineMap = new HashMap>(); routineCache.put(target, routineMap); } final String methodShareGroup = (shareGroup != null) ? shareGroup : ShareGroup.ALL; final RoutineInfo routineInfo = new RoutineInfo(configuration, method, methodShareGroup, inputMode, outputMode); Routine routine = routineMap.get(routineInfo); if (routine == null) { final Object mutex; if (!ShareGroup.NONE.equals(methodShareGroup)) { mutex = getSharedMutex(target, methodShareGroup); } else { mutex = null; } final MethodInvocationFactory factory = new MethodInvocationFactory(target, method, mutex, inputMode, outputMode); routine = new DefaultRoutine(configuration, factory); routineMap.put(routineInfo, routine); } return (Routine) routine; } } /** * Returns the builder target class. * * @return the target class. */ @Nonnull protected Class getTargetClass() { return mTargetClass; } /** * Returns the builder reference to the target object. * * @return the target reference. */ @Nullable protected WeakReference getTargetReference() { return mTargetReference; } /** * Returns a routine used to call the specified method. * * @param invocationConfiguration the invocation configuration. * @param proxyConfiguration the share configuration. * @param targetMethod the target method. * @return the routine. */ @Nonnull protected Routine method( @Nonnull final InvocationConfiguration invocationConfiguration, @Nonnull final ProxyConfiguration proxyConfiguration, @Nonnull final Method targetMethod) { String methodShareGroup = proxyConfiguration.getShareGroupOr(null); final InvocationConfiguration.Builder builder = invocationConfiguration.builderFrom(); final Priority priorityAnnotation = targetMethod.getAnnotation(Priority.class); if (priorityAnnotation != null) { builder.withPriority(priorityAnnotation.value()); } final ShareGroup shareGroupAnnotation = targetMethod.getAnnotation(ShareGroup.class); if (shareGroupAnnotation != null) { methodShareGroup = shareGroupAnnotation.value(); } final Timeout timeoutAnnotation = targetMethod.getAnnotation(Timeout.class); if (timeoutAnnotation != null) { builder.withExecutionTimeout(timeoutAnnotation.value(), timeoutAnnotation.unit()); } final TimeoutAction actionAnnotation = targetMethod.getAnnotation(TimeoutAction.class); if (actionAnnotation != null) { builder.withExecutionTimeoutAction(actionAnnotation.value()); } return getRoutine(builder.set(), methodShareGroup, targetMethod, null, null); } /** * Implementation of a simple invocation wrapping the target method. */ private static class MethodFunctionInvocation extends FunctionInvocation { private final InputMode mInputMode; private final Method mMethod; private final Object mMutex; private final OutputMode mOutputMode; private final WeakReference mTargetReference; /** * Constructor. * * @param targetReference the reference to target object. * @param method the method to wrap. * @param mutex the mutex used for synchronization. * @param inputMode the input transfer mode. * @param outputMode the output transfer mode. */ public MethodFunctionInvocation(@Nonnull final WeakReference targetReference, @Nonnull final Method method, @Nullable final Object mutex, @Nullable final InputMode inputMode, @Nullable final OutputMode outputMode) { mTargetReference = targetReference; mMethod = method; mMutex = (mutex != null) ? mutex : this; mInputMode = inputMode; mOutputMode = outputMode; } @Override public void onCall(@Nonnull final List objects, @Nonnull final ResultChannel result) { final Object target = mTargetReference.get(); if (target == null) { throw new IllegalStateException("the target object has been destroyed"); } callFromInvocation(mMethod, mMutex, target, objects, result, mInputMode, mOutputMode); } } /** * Factory creating method invocations. */ private static class MethodInvocationFactory implements InvocationFactory { private final InputMode mInputMode; private final Method mMethod; private final Object mMutex; private final OutputMode mOutputMode; private final WeakReference mTargetReference; /** * Constructor. * * @param target the target object. * @param method the method to wrap. * @param mutex the mutex used for synchronization. * @param inputMode the input transfer mode. * @param outputMode the output transfer mode. */ public MethodInvocationFactory(@Nonnull final Object target, @Nonnull final Method method, @Nullable final Object mutex, @Nullable final InputMode inputMode, @Nullable final OutputMode outputMode) { mTargetReference = new WeakReference(target); mMethod = method; mMutex = (mutex != null) ? mutex : this; mInputMode = inputMode; mOutputMode = outputMode; } @Nonnull public Invocation newInvocation() { return new MethodFunctionInvocation(mTargetReference, mMethod, mMutex, mInputMode, mOutputMode); } } /** * Class used as key to identify a specific routine instance. */ private static final class RoutineInfo { private final InvocationConfiguration mConfiguration; private final InputMode mInputMode; private final Method mMethod; private final OutputMode mOutputMode; private final String mShareGroup; /** * Constructor. * * @param configuration the invocation configuration. * @param method the method to wrap. * @param shareGroup the group name. * @param inputMode the input transfer mode. * @param outputMode the output transfer mode. */ public RoutineInfo(@Nonnull final InvocationConfiguration configuration, @Nonnull final Method method, @Nonnull final String shareGroup, @Nullable final InputMode inputMode, @Nullable final OutputMode outputMode) { mMethod = method; mShareGroup = shareGroup; mConfiguration = configuration; mInputMode = inputMode; mOutputMode = outputMode; } @Override public int hashCode() { // auto-generated code int result = mConfiguration.hashCode(); result = 31 * result + (mInputMode != null ? mInputMode.hashCode() : 0); result = 31 * result + mMethod.hashCode(); result = 31 * result + (mOutputMode != null ? mOutputMode.hashCode() : 0); result = 31 * result + mShareGroup.hashCode(); return result; } @Override public boolean equals(final Object o) { // auto-generated code if (this == o) { return true; } if (!(o instanceof RoutineInfo)) { return false; } final RoutineInfo that = (RoutineInfo) o; return mConfiguration.equals(that.mConfiguration) && mInputMode == that.mInputMode && mMethod.equals(that.mMethod) && mOutputMode == that.mOutputMode && mShareGroup.equals(that.mShareGroup); } } }