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

com.devebot.opflow.OpflowRpcMaster 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.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author drupalex
 */
public class OpflowRpcMaster {
    private final static Logger LOG = LoggerFactory.getLogger(OpflowRpcMaster.class);
    private final OpflowLogTracer logTracer;
    
    private final int PREFETCH_NUM = 1;
    private final int CONSUMER_MAX = 1;
    
    private final Lock lock = new ReentrantLock();
    private final Condition idle = lock.newCondition();
    
    private final OpflowEngine engine;
    private final OpflowExecutor executor;
    
    private final String responseName;
    
    private final boolean monitorEnabled;
    private final String monitorId;
    private final int monitorInterval;
    private final long monitorTimeout;
    
    public OpflowRpcMaster(Map params) throws OpflowBootstrapException {
        params = OpflowUtil.ensureNotNull(params);
        
        final String rpcMasterId = OpflowUtil.getOptionField(params, "rpcMasterId", true);
        logTracer = OpflowLogTracer.ROOT.branch("rpcMasterId", rpcMasterId);
        
        if (OpflowLogTracer.has(LOG, "info")) LOG.info(logTracer
                .text("RpcMaster[${rpcMasterId}].new()")
                .stringify());
        
        Map brokerParams = new HashMap();
        OpflowUtil.copyParameters(brokerParams, params, OpflowEngine.PARAMETER_NAMES);
        brokerParams.put("engineId", rpcMasterId);
        brokerParams.put("mode", "rpc_master");
        brokerParams.put("exchangeType", "direct");
        
        engine = new OpflowEngine(brokerParams);
        executor = new OpflowExecutor(engine);
        
        responseName = (String) params.get("responseName");
        if (responseName != null) {
            executor.assertQueue(responseName);
        }
        
        if (params.get("monitorEnabled") != null && params.get("monitorEnabled") instanceof Boolean) {
            monitorEnabled = (Boolean) params.get("monitorEnabled");
        } else {
            monitorEnabled = true;
        }
        
        monitorId = params.get("monitorId") instanceof String ? (String)params.get("monitorId") : rpcMasterId;
        
        if (params.get("monitorInterval") != null && params.get("monitorInterval") instanceof Integer) {
            monitorInterval = (Integer) params.get("monitorInterval");
        } else {
            monitorInterval = 14000; // can run 2-3 times in 30s
        }
        
        if (params.get("monitorTimeout") != null && params.get("monitorTimeout") instanceof Long) {
            monitorTimeout = (Long) params.get("monitorTimeout");
        } else {
            monitorTimeout = 0;
        }
        
        if (OpflowLogTracer.has(LOG, "info")) LOG.info(logTracer
                .put("responseName", responseName)
                .put("monitorId", monitorId)
                .put("monitorEnabled", monitorEnabled)
                .put("monitorInterval", monitorInterval)
                .put("monitorTimeout", monitorTimeout)
                .tags("RpcMaster.new() parameters")
                .text("RpcMaster[${rpcMasterId}].new() parameters")
                .stringify());
        
        if (OpflowLogTracer.has(LOG, "info")) LOG.info(logTracer
                .text("RpcMaster[${rpcMasterId}].new() end!")
                .stringify());
    }

    private final Map tasks = new ConcurrentHashMap();
    
    private OpflowEngine.ConsumerInfo responseConsumer;

    private OpflowEngine.ConsumerInfo initResponseConsumer(final boolean forked) {
        final String _consumerId = OpflowUtil.getLogID();
        final OpflowLogTracer logSession = logTracer.branch("consumerId", _consumerId);
        if (OpflowLogTracer.has(LOG, "info")) LOG.info(logSession
                .put("forked", forked)
                .text("initResponseConsumer() is invoked")
                .stringify());
        return engine.consume(new OpflowListener() {
            @Override
            public boolean processMessage(byte[] content, AMQP.BasicProperties properties, 
                    String queueName, Channel channel, String workerTag) throws IOException {
                String taskId = properties.getCorrelationId();
                Map headers = properties.getHeaders();

                String requestId = OpflowUtil.getRequestId(headers, true);
                OpflowLogTracer logResult = null;
                if (OpflowLogTracer.has(LOG, "info")) logResult = logSession.branch("requestId", requestId);

                if (OpflowLogTracer.has(LOG, "info") && logResult != null) LOG.info(logResult
                        .put("correlationId", taskId)
                        .text("initResponseConsumer() - task[${correlationId}] receives a result")
                        .stringify());

                if (OpflowLogTracer.has(LOG, "debug") && logResult != null) LOG.debug(logResult
                        .put("bodyLength", (content != null ? content.length : -1))
                        .text("initResponseConsumer() - result body length")
                        .stringify());

                OpflowRpcRequest task = tasks.get(taskId);
                if (taskId == null || task == null) {
                    if (OpflowLogTracer.has(LOG, "debug") && logResult != null) LOG.debug(logResult
                        .put("correlationId", taskId)
                        .text("initResponseConsumer() - task[${correlationId}] not found, skipped")
                        .stringify());
                } else {
                    if (OpflowLogTracer.has(LOG, "debug") && logResult != null) LOG.debug(logResult
                        .put("correlationId", taskId)
                        .text("initResponseConsumer() - push Message object to task[${correlationId}]")
                        .stringify());
                    OpflowMessage message = new OpflowMessage(content, properties.getHeaders());
                    task.push(message);
                    if (OpflowLogTracer.has(LOG, "debug") && logResult != null) LOG.debug(logResult
                        .put("correlationId", taskId)
                        .text("initResponseConsumer() - returned value of task[${correlationId}]")
                        .stringify());
                }
                return true;
            }
        }, OpflowUtil.buildMap(new OpflowUtil.MapListener() {
            @Override
            public void transform(Map opts) {
                opts.put("consumerId", _consumerId);
                if (!forked) {
                    opts.put("queueName", responseName);
                    opts.put("consumerLimit", CONSUMER_MAX);
                    opts.put("forceNewChannel", Boolean.FALSE);
                }
                opts.put("binding", Boolean.FALSE);
                opts.put("prefetch", PREFETCH_NUM);
            }
        }).toMap());
    }
    
    private OpflowTask.TimeoutMonitor timeoutMonitor = null;
    
    private OpflowTask.TimeoutMonitor initTimeoutMonitor() {
        OpflowTask.TimeoutMonitor monitor = null;
        if (monitorEnabled) {
            monitor = new OpflowTask.TimeoutMonitor(tasks, monitorInterval, monitorTimeout, monitorId);
            monitor.start();
        }
        return monitor;
    }
    
    public OpflowRpcRequest request(String routineId, String body) {
        return request(routineId, body, null);
    }
    
    public OpflowRpcRequest request(String routineId, String body, Map options) {
        return request(routineId, OpflowUtil.getBytes(body), options);
    }
    
    public OpflowRpcRequest request(String routineId, byte[] body) {
        return request(routineId, body, null);
    }
    
    public OpflowRpcRequest request(String routineId, byte[] body, Map options) {
        options = OpflowUtil.ensureNotNull(options);
        
        Object requestId = options.get("requestId");
        if (requestId == null) {
            options.put("requestId", requestId = OpflowUtil.getLogID());
        }
        
        final OpflowLogTracer logRequest = logTracer.branch("requestId", requestId);
        
        if (routineId != null) {
            options.put("routineId", routineId);
        }
        
        if (timeoutMonitor == null) {
            timeoutMonitor = initTimeoutMonitor();
        }
        
        final boolean forked = "forked".equals((String)options.get("mode"));
        final OpflowEngine.ConsumerInfo consumerInfo;
        if (forked) {
            consumerInfo = initResponseConsumer(true);
        } else {
            if (responseConsumer == null) {
                responseConsumer = initResponseConsumer(false);
            }
            consumerInfo = responseConsumer;
        }
        
        final String taskId = OpflowUtil.getLogID();
        OpflowTask.Listener listener = new OpflowTask.Listener() {
            private OpflowLogTracer logTask = null;
            @Override
            public void handleEvent() {
                lock.lock();
                if (OpflowLogTracer.has(LOG, "debug") && logRequest != null) logTask = logRequest.copy();
                try {
                    tasks.remove(taskId);
                    if (tasks.isEmpty()) {
                        if (forked) {
                            engine.cancelConsumer(consumerInfo);
                        }
                        idle.signal();
                    }
                    if (logTask != null && OpflowLogTracer.has(LOG, "debug")) LOG.debug(logTask
                            .put("taskId", taskId)
                            .put("taskListSize", tasks.size())
                            .text("Request[${requestId}] - RpcMaster[${rpcMasterId}]"
                                    + "- tasksize after removing task[${taskId}]: ${taskListSize}")
                            .stringify());
                } finally {
                    lock.unlock();
                }
            }
        };
        
        OpflowRpcRequest task = new OpflowRpcRequest(options, listener);
        tasks.put(taskId, task);
        
        Map headers = new HashMap();
        headers.put("requestId", task.getRequestId());
        headers.put("routineId", task.getRoutineId());
        
        Boolean progressEnabled = null;
        if (options.get("progressEnabled") instanceof Boolean) {
            headers.put("progressEnabled", progressEnabled = (Boolean) options.get("progressEnabled"));
        }
        
        AMQP.BasicProperties.Builder builder = new AMQP.BasicProperties.Builder()
                .correlationId(taskId);
        
        if (!consumerInfo.isFixedQueue()) {
            if (OpflowLogTracer.has(LOG, "trace") && logRequest != null) LOG.trace(logRequest
                    .put("replyTo", consumerInfo.getQueueName())
                    .text("Request[${requestId}] - RpcMaster[${rpcMasterId}] - Use dynamic replyTo: ${replyTo}")
                    .stringify());
        } else {
            if (OpflowLogTracer.has(LOG, "trace") && logRequest != null) LOG.trace(logRequest
                    .put("replyTo", consumerInfo.getQueueName())
                    .text("Request[${requestId}] - RpcMaster[${rpcMasterId}] - Use static replyTo: ${replyTo}")
                    .stringify());
        }
        builder.replyTo(consumerInfo.getQueueName());
        
        engine.produce(body, headers, builder);
        
        return task;
    }

    public class State extends OpflowEngine.State {
        public State(OpflowEngine.State superState) {
            super(superState);
        }
    }
    
    public State check() {
        State state = new State(engine.check());
        return state;
    }
    
    public void close() {
        lock.lock();
        if (OpflowLogTracer.has(LOG, "trace")) LOG.trace(logTracer
                .text("RpcMaster[${rpcMasterId}].close() - obtain the lock")
                .stringify());
        try {
            if (OpflowLogTracer.has(LOG, "trace")) LOG.trace(logTracer
                .text("RpcMaster[${rpcMasterId}].close() - check tasks.isEmpty()? and await...")
                .stringify());
            while(!tasks.isEmpty()) idle.await();
            
            if (OpflowLogTracer.has(LOG, "trace")) LOG.trace(logTracer
                .text("RpcMaster[${rpcMasterId}].close() - cancel responseConsumer")
                .stringify());
            if (responseConsumer != null) engine.cancelConsumer(responseConsumer);
            
            if (OpflowLogTracer.has(LOG, "trace")) LOG.trace(logTracer
                .text("RpcMaster[${rpcMasterId}].close() - stop timeoutMonitor")
                .stringify());
            if (timeoutMonitor != null) timeoutMonitor.stop();
            
            if (OpflowLogTracer.has(LOG, "trace")) LOG.trace(logTracer
                .text("RpcMaster[${rpcMasterId}].close() - close broker/engine")
                .stringify());
            if (engine != null) engine.close();
        } catch(InterruptedException ex) {
            if (OpflowLogTracer.has(LOG, "error")) LOG.error(logTracer
                .text("RpcMaster[${rpcMasterId}].close() - an interruption has been raised")
                .stringify());
        } finally {
            lock.unlock();
            if (OpflowLogTracer.has(LOG, "trace")) LOG.trace(logTracer
                .text("RpcMaster[${rpcMasterId}].close() - lock has been released")
                .stringify());
        }
    }
    
    public OpflowExecutor getExecutor() {
        return executor;
    }
    
    public String getResponseName() {
        return responseName;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy