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

com.devebot.opflow.OpflowServerlet Maven / Gradle / Ivy

There is a newer version: 0.3.1
Show newest version
package com.devebot.opflow;

import com.devebot.opflow.exception.OpflowBootstrapException;
import com.devebot.opflow.exception.OpflowInterceptionException;
import com.google.gson.JsonSyntaxException;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.devebot.opflow.annotation.OpflowTargetRoutine;

/**
 *
 * @author drupalex
 */
public class OpflowServerlet {
    private final static Logger LOG = LoggerFactory.getLogger(OpflowServerlet.class);
    private final OpflowLogTracer logTracer;
    
    private OpflowPubsubHandler configurer;
    private OpflowRpcWorker rpcWorker;
    private OpflowPubsubHandler subscriber;
    private Instantiator instantiator;
    
    private final ListenerDescriptor listenerMap;
    
    private final Map kwargs;
    
    public OpflowServerlet(ListenerDescriptor listeners, Map kwargs) throws OpflowBootstrapException {
        this.kwargs = OpflowUtil.ensureNotNull(kwargs);

        logTracer = OpflowLogTracer.ROOT.branch("serverletId", OpflowUtil.getOptionField(this.kwargs, "serverletId", true));
        
        if (OpflowLogTracer.has(LOG, "info")) LOG.info(logTracer
                .text("Serverlet[${serverletId}].new()")
                .stringify());
        
        if (listeners == null) {
            throw new OpflowBootstrapException("Listener definitions must not be null");
        }
        listenerMap = listeners;
        
        Map configurerCfg = (Map)this.kwargs.get("configurer");
        Map rpcWorkerCfg = (Map)this.kwargs.get("rpcWorker");
        Map subscriberCfg = (Map)this.kwargs.get("subscriber");
        
        HashSet checkExchange = new HashSet();
        HashSet checkQueue = new HashSet();
        HashSet checkRecyclebin = new HashSet();
        
        if (configurerCfg != null && !Boolean.FALSE.equals(configurerCfg.get("enabled"))) {
            if (configurerCfg.get("exchangeName") == null || configurerCfg.get("routingKey") == null) {
                throw new OpflowBootstrapException("Invalid Configurer connection parameters");
            } 
            if (!checkExchange.add(configurerCfg.get("exchangeName").toString() + configurerCfg.get("routingKey").toString())) {
                throw new OpflowBootstrapException("Duplicated Configurer connection parameters");
            }
            if (configurerCfg.get("subscriberName") != null && !checkQueue.add(configurerCfg.get("subscriberName").toString())) {
                throw new OpflowBootstrapException("Configurer[subscriberName] must not be duplicated");
            }
            if (configurerCfg.get("recyclebinName") != null) checkRecyclebin.add(configurerCfg.get("recyclebinName").toString());
        }

        if (rpcWorkerCfg != null && !Boolean.FALSE.equals(rpcWorkerCfg.get("enabled"))) {
            if (rpcWorkerCfg.get("exchangeName") == null || rpcWorkerCfg.get("routingKey") == null) {
                throw new OpflowBootstrapException("Invalid RpcWorker connection parameters");
            }
            if (!checkExchange.add(rpcWorkerCfg.get("exchangeName").toString() + rpcWorkerCfg.get("routingKey").toString())) {
                throw new OpflowBootstrapException("Duplicated RpcWorker connection parameters");
            }
            if (rpcWorkerCfg.get("operatorName") != null && !checkQueue.add(rpcWorkerCfg.get("operatorName").toString())) {
                throw new OpflowBootstrapException("RpcWorker[operatorName] must not be duplicated");
            }
            if (rpcWorkerCfg.get("responseName") != null && !checkQueue.add(rpcWorkerCfg.get("responseName").toString())) {
                throw new OpflowBootstrapException("RpcWorker[responseName] must not be duplicated");
            }
        }
        
        if (subscriberCfg != null && !Boolean.FALSE.equals(subscriberCfg.get("enabled"))) {
            if (subscriberCfg.get("exchangeName") == null || subscriberCfg.get("routingKey") == null) {
                throw new OpflowBootstrapException("Invalid Subscriber connection parameters");
            }
            if (!checkExchange.add(subscriberCfg.get("exchangeName").toString() + subscriberCfg.get("routingKey").toString())) {
                throw new OpflowBootstrapException("Duplicated Subscriber connection parameters");
            }
            if (subscriberCfg.get("subscriberName") != null && !checkQueue.add(subscriberCfg.get("subscriberName").toString())) {
                throw new OpflowBootstrapException("Subscriber[subscriberName] must not be duplicated");
            }
            if (subscriberCfg.get("recyclebinName") != null) checkRecyclebin.add(subscriberCfg.get("recyclebinName").toString());
        }
        
        checkRecyclebin.retainAll(checkQueue);
        if (!checkRecyclebin.isEmpty()) {
            if (OpflowLogTracer.has(LOG, "error")) LOG.error(logTracer
                .text("duplicated_recyclebin_queue_name").toString());
            throw new OpflowBootstrapException("Invalid recyclebinName (duplicated with some queueNames)");
        }
        
        try {
            if (configurerCfg != null && !Boolean.FALSE.equals(configurerCfg.get("enabled"))) {
                String pubsubHandlerId = OpflowUtil.getLogID();
                configurerCfg.put("pubsubHandlerId", pubsubHandlerId);
                if (OpflowLogTracer.has(LOG, "info")) LOG.info(logTracer
                        .put("pubsubHandlerId", pubsubHandlerId)
                        .text("Serverlet[${serverletId}] creates a new configurer")
                        .stringify());
                configurer = new OpflowPubsubHandler(configurerCfg);
            }

            if (rpcWorkerCfg != null && !Boolean.FALSE.equals(rpcWorkerCfg.get("enabled"))) {
                String rpcWorkerId = OpflowUtil.getLogID();
                rpcWorkerCfg.put("rpcWorkerId", rpcWorkerId);
                if (OpflowLogTracer.has(LOG, "info")) LOG.info(logTracer
                        .put("rpcWorkerId", rpcWorkerId)
                        .text("Serverlet[${serverletId}] creates a new rpcWorker")
                        .stringify());
                rpcWorker = new OpflowRpcWorker(rpcWorkerCfg);
                instantiator = new Instantiator(rpcWorker, OpflowUtil.buildMap()
                        .put("instantiatorId", rpcWorkerId).toMap());
            }

            if (subscriberCfg != null && !Boolean.FALSE.equals(subscriberCfg.get("enabled"))) {
                String pubsubHandlerId = OpflowUtil.getLogID();
                subscriberCfg.put("pubsubHandlerId", pubsubHandlerId);
                if (OpflowLogTracer.has(LOG, "info")) LOG.info(logTracer
                        .put("pubsubHandlerId", pubsubHandlerId)
                        .text("Serverlet[${serverletId}] creates a new subscriber")
                        .stringify());
                subscriber = new OpflowPubsubHandler(subscriberCfg);
            }
        } catch(OpflowBootstrapException exception) {
            this.close();
            throw exception;
        }
        
        if (OpflowLogTracer.has(LOG, "info")) LOG.info(logTracer
                .text("Serverlet[${serverletId}].new() end!")
                .stringify());
    }
    
    public final void start() {
        if (OpflowLogTracer.has(LOG, "info")) LOG.info(logTracer
                .text("Serverlet[${serverletId}].start()")
                .stringify());
        
        if (configurer != null && listenerMap.getConfigurer() != null) {
            configurer.subscribe(listenerMap.getConfigurer());
        }
        
        if (rpcWorker != null) {
            Map rpcListeners = listenerMap.getRpcListeners();
            for(Map.Entry entry:rpcListeners.entrySet()) {
                rpcWorker.process(entry.getKey(), entry.getValue());
            }
        }
        if (instantiator != null) {
            instantiator.process();
        }
        
        if (subscriber != null && listenerMap.getSubscriber() != null) {
            subscriber.subscribe(listenerMap.getSubscriber());
        }
        
        if (OpflowLogTracer.has(LOG, "info")) LOG.info(logTracer
                .text("Serverlet[${serverletId}].start() has completed!")
                .stringify());
    }
    
    public void instantiateType(Class type) {
        instantiateType(type, null);
    }
    
    public void instantiateType(Class type, Object target) {
        if (instantiator != null) {
            instantiator.instantiateType(type, target);
        } else {
            throw new UnsupportedOperationException("instantiator is nulls");
        }
    }
    
    public void instantiateTypes(Class[] types) {
        instantiateTypes(Arrays.asList(types));
    }
    
    public void instantiateTypes(Collection types) {
        Set typeSet = new HashSet();
        typeSet.addAll(types);
        for (Class type : typeSet) {
            if (!Modifier.isAbstract(type.getModifiers())) {
                instantiateType(type);
            }
        }
    }
    
    public final void close() {
        if (OpflowLogTracer.has(LOG, "info")) LOG.info(logTracer
                .text("Serverlet[${serverletId}].close()")
                .stringify());
        
        if (configurer != null) configurer.close();
        if (rpcWorker != null) rpcWorker.close();
        if (subscriber != null) subscriber.close();
        
        if (OpflowLogTracer.has(LOG, "info")) LOG.info(logTracer
                .text("Serverlet[${serverletId}].close() has completed!")
                .stringify());
    }
    
    public static DescriptorBuilder getDescriptorBuilder() {
        return new DescriptorBuilder();
    }
    
    public static class DescriptorBuilder {
        private DescriptorBuilder() {}
        
        private ListenerDescriptor map = new ListenerDescriptor();
        
        public DescriptorBuilder setConfigurer(OpflowPubsubListener configurer) {
            map.configurer = configurer;
            return this;
        }
        
        public DescriptorBuilder setSubscriber(OpflowPubsubListener subscriber) {
            map.subscriber = subscriber;
            return this;
        }
        
        public DescriptorBuilder addRpcListener(String routineId, OpflowRpcListener listener) {
            map.rpcListeners.put(routineId, listener);
            return this;
        }
        
        public ListenerDescriptor build() {
            return map;
        }
    }
    
    public static class ListenerDescriptor {
        public static final ListenerDescriptor EMPTY = new ListenerDescriptor();
        private OpflowPubsubListener configurer;
        private Map rpcListeners = new HashMap();
        private OpflowPubsubListener subscriber;
        
        private ListenerDescriptor() {}
        
        public OpflowPubsubListener getConfigurer() {
            return configurer;
        }

        public Map getRpcListeners() {
            Map cloned = new HashMap();
            cloned.putAll(rpcListeners);
            return cloned;
        }

        public OpflowPubsubListener getSubscriber() {
            return subscriber;
        }
    }
    
    public static class Instantiator {
        private static final Logger LOG = LoggerFactory.getLogger(Instantiator.class);
        private final OpflowLogTracer logTracer;
        private final OpflowRpcWorker rpcWorker;
        private final OpflowRpcListener listener;
        private final Set routineIds = new HashSet();
        private final Map methodRef = new HashMap();
        private final Map targetRef = new HashMap();
        private final Map methodOfAlias = new HashMap();
        private boolean processing = false;
        
        public Instantiator(OpflowRpcWorker worker) throws OpflowBootstrapException {
            this(worker, null);
        }
        
        public Instantiator(OpflowRpcWorker worker, Map options) throws OpflowBootstrapException {
            if (worker == null) {
                throw new OpflowBootstrapException("RpcWorker should not be null");
            }
            options = OpflowUtil.ensureNotNull(options);
            logTracer = OpflowLogTracer.ROOT.branch("instantiatorId", options.getOrDefault("instantiatorId", OpflowUtil.getLogID()));
            rpcWorker = worker;
            listener = new OpflowRpcListener() {
                @Override
                public Boolean processMessage(OpflowMessage message, OpflowRpcResponse response) throws IOException {
                    String requestId = OpflowUtil.getRequestId(message.getInfo());
                    OpflowLogTracer listenerTrail = logTracer.branch("requestId", requestId);
                    String routineId = OpflowUtil.getRoutineId(message.getInfo());
                    String methodId = methodOfAlias.getOrDefault(routineId, routineId);
                    if (OpflowLogTracer.has(LOG, "info")) LOG.info(listenerTrail
                            .put("routineId", routineId)
                            .put("methodId", methodId)
                            .text("Receives new method call")
                            .stringify());
                    Method method = methodRef.get(methodId);
                    Object target = targetRef.get(methodId);
                    try {
                        Method origin = target.getClass().getMethod(method.getName(), method.getParameterTypes());
                        OpflowTargetRoutine routine = extractMethodInfo(origin);
                        if (routine != null && routine.enabled() == false) {
                            throw new UnsupportedOperationException("Method " + origin.toString() + " is disabled");
                        }
                        
                        String json = message.getBodyAsString();
                        if (OpflowLogTracer.has(LOG, "trace")) LOG.trace(listenerTrail
                                .put("arguments", json)
                                .text("Method arguments in json string")
                                .stringify());
                        Object[] args = OpflowJsontool.toObjectArray(json, method.getParameterTypes());
                        
                        Object returnValue = method.invoke(target, args);
                        String result = OpflowJsontool.toString(returnValue);
                        if (OpflowLogTracer.has(LOG, "trace")) LOG.trace(listenerTrail
                                .put("return", OpflowUtil.truncate(result))
                                .text("Return value of method")
                                .stringify());
                        response.emitCompleted(result);
                        
                        if (OpflowLogTracer.has(LOG, "info")) LOG.info(listenerTrail
                            .text("Method call has completed")
                            .stringify());
                    } catch (JsonSyntaxException error) {
                        response.emitFailed(OpflowUtil.buildMap()
                                .put("type", error.getClass().getName())
                                .put("message", error.getMessage())
                                .toString());
                        throw error;
                    } catch (NoSuchMethodException ex) {
                        LOG.error(null, ex);
                        response.emitFailed(OpflowUtil.buildMap()
                                .put("type", ex.getClass().getName())
                                .put("message", ex.getMessage())
                                .toString());
                    } catch (SecurityException ex) {
                        LOG.error(null, ex);
                        response.emitFailed(OpflowUtil.buildMap()
                                .put("type", ex.getClass().getName())
                                .put("message", ex.getMessage())
                                .toString());
                    } catch (IllegalAccessException ex) {
                        LOG.error(null, ex);
                        response.emitFailed(OpflowUtil.buildMap()
                                .put("type", ex.getClass().getName())
                                .put("message", ex.getMessage())
                                .toString());
                    } catch (IllegalArgumentException ex) {
                        LOG.error(null, ex);
                        response.emitFailed(OpflowUtil.buildMap()
                                .put("type", ex.getClass().getName())
                                .put("message", ex.getMessage())
                                .toString());
                    } catch (InvocationTargetException ex) {
                        Throwable catched = (Exception) ex.getCause();
                        catched.getStackTrace();
                        response.emitFailed(OpflowUtil.buildMap()
                                .put("exceptionClass", catched.getClass().getName())
                                .put("exceptionPayload", OpflowJsontool.toString(catched))
                                .put("type", catched.getClass().getName())
                                .put("message", catched.getMessage())
                                .toString());
                    } catch (UnsupportedOperationException ex) {
                        ex.getStackTrace();
                        response.emitFailed(OpflowUtil.buildMap()
                                .put("exceptionClass", ex.getClass().getName())
                                .put("exceptionPayload", OpflowJsontool.toString(ex))
                                .put("type", ex.getClass().getName())
                                .put("message", ex.getMessage())
                                .toString());
                    } catch (Exception ex) {
                        response.emitFailed(OpflowUtil.buildMap()
                                .put("type", ex.getClass().getName())
                                .put("message", ex.getMessage())
                                .toString());
                    }
                    return null;
                }
            };
            if (Boolean.TRUE.equals(options.get("autorun"))) {
                process();
            }
        }
        
        public final void process() {
            if (!processing) {
                rpcWorker.process(routineIds, listener);
                processing = true;
            }
        }
        
        public void instantiateType(Class type) {
            instantiateType(type, null);
        }
        
        public void instantiateType(Class type, Object target) {
            if (type == null && target == null) {
                throw new OpflowInterceptionException("Both type and target should not be null");
            }
            if (Modifier.isAbstract(type.getModifiers()) && target == null) {
                if (OpflowLogTracer.has(LOG, "error")) LOG.error(logTracer
                        .text("Class should not be an abstract type")
                        .stringify());
                throw new OpflowInterceptionException("Class should not be an abstract type");
            }
            try {
                if (target == null) target = type.newInstance();
                for (Method method : type.getDeclaredMethods()) {
                    String methodId = OpflowUtil.getMethodSignature(method);
                    OpflowTargetRoutine routine = extractMethodInfo(method);
                    if (routine != null && routine.alias() != null) {
                        String[] aliases = routine.alias();
                        for(String alias:aliases) {
                            if (methodOfAlias.containsKey(alias)) {
                                throw new OpflowInterceptionException("Alias[" + alias + "]/routineId[" + methodId + "]" + 
                                        " is conflicted with alias of routineId[" + methodOfAlias.get(alias) + "]");
                            }
                            methodOfAlias.put(alias, methodId);
                            if (OpflowLogTracer.has(LOG, "trace")) LOG.trace(logTracer
                                    .put("alias", alias)
                                    .put("routineId", methodId)
                                    .text("link alias to routineId")
                                    .stringify());
                        }
                    }
                }
                routineIds.addAll(methodOfAlias.keySet());
                List> clazzes = OpflowUtil.getAllAncestorTypes(type);
                for(Class clz: clazzes) {
                    Method[] methods = clz.getDeclaredMethods();
                    for (Method method : methods) {
                        String methodId = OpflowUtil.getMethodSignature(method);
                        if (OpflowLogTracer.has(LOG, "trace")) LOG.trace(logTracer
                                .put("routineId", methodId)
                                .put("methodId", methodId)
                                .tags("Attach method to RpcWorker listener")
                                .text("Attach method to RpcWorker listener")
                                .stringify());
                        if (!routineIds.add(methodId) && !method.equals(methodRef.get(methodId))) {
                            throw new OpflowInterceptionException("routineId[" + methodId + "] is conflicted");
                        }
                        methodRef.put(methodId, method);
                        targetRef.put(methodId, target);
                    }
                }
            } catch (InstantiationException except) {
                if (OpflowLogTracer.has(LOG, "error")) LOG.error(logTracer
                        .put("errorType", except.getClass().getName())
                        .put("errorMessage", except.getMessage())
                        .text("Could not instantiate the class")
                        .stringify());
                throw new OpflowInterceptionException("Could not instantiate the class", except);
            } catch (IllegalAccessException except) {
                if (OpflowLogTracer.has(LOG, "error")) LOG.error(logTracer
                        .put("errorType", except.getClass().getName())
                        .put("errorMessage", except.getMessage())
                        .text("Constructor is not accessible")
                        .stringify());
                throw new OpflowInterceptionException("Constructor is not accessible", except);
            } catch (SecurityException except) {
                if (OpflowLogTracer.has(LOG, "error")) LOG.error(logTracer
                        .put("errorType", except.getClass().getName())
                        .put("errorMessage", except.getMessage())
                        .text("Class loaders is not the same or denies access")
                        .stringify());
                throw new OpflowInterceptionException("Class loaders is not the same or denies access", except);
            } catch (Exception except) {
                if (OpflowLogTracer.has(LOG, "error")) LOG.error(logTracer
                        .put("errorType", except.getClass().getName())
                        .put("errorMessage", except.getMessage())
                        .text("Unknown exception")
                        .stringify());
                throw new OpflowInterceptionException("Unknown exception", except);
            }
            process();
        }
        
        private OpflowTargetRoutine extractMethodInfo(Method method) {
            if (method.isAnnotationPresent(OpflowTargetRoutine.class)) {
                Annotation annotation = method.getAnnotation(OpflowTargetRoutine.class);
                OpflowTargetRoutine routine = (OpflowTargetRoutine) annotation;
                return routine;
            }
            return null;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy