com.codebullets.sagalib.processing.ReflectionInvoker.java Source code

Java tutorial

Introduction

Here is the source code for com.codebullets.sagalib.processing.ReflectionInvoker.java

Source

/*
 * Copyright 2013 Stefan Domnanovits
 *
 * 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.codebullets.sagalib.processing;

import com.codebullets.sagalib.Saga;
import com.codebullets.sagalib.startup.MessageHandler;
import com.codebullets.sagalib.startup.SagaAnalyzer;
import com.codebullets.sagalib.startup.SagaHandlersMap;
import com.google.common.base.MoreObjects;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Objects;

/**
 * Invokes the target handler on the provided saga for the given handler.
 */
public class ReflectionInvoker implements HandlerInvoker {
    private static final Logger LOG = LoggerFactory.getLogger(ReflectionInvoker.class);
    private final LoadingCache<InvokerKey, Method> methodInvokers;

    /**
     * Generates a new instance of ReflectionInvoker.
     */
    @Inject
    public ReflectionInvoker(final SagaAnalyzer analyzer) {
        methodInvokers = CacheBuilder.newBuilder().build(new MethodSearcher(analyzer.scanHandledMessageTypes()));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void invoke(final Saga saga, final Object message)
            throws InvocationTargetException, IllegalAccessException {
        InvokerKey key = InvokerKey.create(saga, message);
        Method method = tryGetMethod(key);
        if (method != null) {
            method.invoke(saga, message);
        } else {
            LOG.warn("No method found to call. key = {}}", key);
        }
    }

    /**
     * Finds the method to invoke without causing an exception.
     */
    private Method tryGetMethod(final InvokerKey key) {
        Method methodToInvoke = null;
        try {
            methodToInvoke = methodInvokers.get(key);
        } catch (Exception ex) {
            LOG.warn("Error fetching method to invoke method {}", key, ex);
        }

        return methodToInvoke;
    }

    /**
     * Searches for a specific handler method in the scanned map of handlers.
     */
    private static class MethodSearcher extends CacheLoader<InvokerKey, Method> {
        private final Map<Class<? extends Saga>, SagaHandlersMap> handlersMapMap;

        MethodSearcher(final Map<Class<? extends Saga>, SagaHandlersMap> handlersMapMap) {
            this.handlersMapMap = handlersMapMap;
        }

        @Override
        public Method load(final InvokerKey key) throws Exception {
            Method methodToInvoke = null;
            SagaHandlersMap handlers = handlersMapMap.get(key.getSagaClass());
            if (handlers != null) {
                for (MessageHandler handler : handlers.messageHandlers()) {
                    if (handler.getMessageType().isAssignableFrom(key.getMsgClass())) {
                        methodToInvoke = handler.getMethodToInvoke();
                        break;
                    }
                }
            }

            return methodToInvoke;
        }
    }

    /**
     * Combined key of saga type and message being handled.
     */
    private static class InvokerKey {
        private final Class sagaClazz;
        private final Class msgClazz;

        InvokerKey(final Class sagaClazz, final Class msgClazz) {
            this.sagaClazz = sagaClazz;
            this.msgClazz = msgClazz;
        }

        private Class getSagaClass() {
            return sagaClazz;
        }

        private Class getMsgClass() {
            return msgClazz;
        }

        @Override
        public int hashCode() {
            return Objects.hash(sagaClazz, msgClazz);
        }

        @Override
        public boolean equals(final Object obj) {
            boolean isEqual = false;

            if (obj instanceof InvokerKey) {
                InvokerKey other = (InvokerKey) obj;
                isEqual = Objects.equals(msgClazz, other.msgClazz) && Objects.equals(sagaClazz, other.sagaClazz);
            }

            return isEqual;
        }

        public static InvokerKey create(final Saga sagaInstance, final Object msgInstance) {
            return new InvokerKey(sagaInstance.getClass(), msgInstance.getClass());
        }

        @Override
        public String toString() {
            return MoreObjects.toStringHelper(this).add("saga", sagaClazz).add("msg", msgClazz).toString();
        }
    }
}