Package de.scoopgmbh.copper.persistent

Source Code of de.scoopgmbh.copper.persistent.PersistentScottyEngine

/*
* Copyright 2002-2013 SCOOP Software GmbH
*
* 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 de.scoopgmbh.copper.persistent;

import java.sql.Connection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.scoopgmbh.copper.Acknowledge;
import de.scoopgmbh.copper.CopperException;
import de.scoopgmbh.copper.CopperRuntimeException;
import de.scoopgmbh.copper.EngineState;
import de.scoopgmbh.copper.PersistentProcessingEngine;
import de.scoopgmbh.copper.ProcessingState;
import de.scoopgmbh.copper.Response;
import de.scoopgmbh.copper.WaitHook;
import de.scoopgmbh.copper.WaitMode;
import de.scoopgmbh.copper.Workflow;
import de.scoopgmbh.copper.WorkflowInstanceDescr;
import de.scoopgmbh.copper.common.AbstractProcessingEngine;
import de.scoopgmbh.copper.common.ProcessorPool;
import de.scoopgmbh.copper.common.ProcessorPoolManager;
import de.scoopgmbh.copper.management.DBStorageMXBean;
import de.scoopgmbh.copper.management.PersistentProcessingEngineMXBean;
import de.scoopgmbh.copper.management.ProcessorPoolMXBean;
import de.scoopgmbh.copper.management.model.EngineType;
import de.scoopgmbh.copper.management.model.WorkflowInfo;

/**
* COPPER processing engine that offers persistent workflow processing.
* @author austermann
*
*/
public class PersistentScottyEngine extends AbstractProcessingEngine implements PersistentProcessingEngine, PersistentProcessingEngineMXBean {

  private static final Logger logger = LoggerFactory.getLogger(PersistentScottyEngine.class);

  private ScottyDBStorageInterface dbStorage;
  private ProcessorPoolManager<? extends PersistentProcessorPool> processorPoolManager;
  private boolean notifyProcessorPoolsOnResponse = false;
  private final Map<String, Workflow<?>> workflowMap = new ConcurrentHashMap<String, Workflow<?>>();
  private final Map<String, List<WaitHook>> waitHookMap = new HashMap<String, List<WaitHook>>();

  /**
   * If true, the engine notifies all processor pools about a new reponse available.
   * This may lead to shorter latency times, but may also increase CPU load or database I/O,
   * so use with care
   *
   * @param notifyProcessorPoolsOnResponse
   */
  public void setNotifyProcessorPoolsOnResponse(boolean notifyProcessorPoolsOnResponse) {
    this.notifyProcessorPoolsOnResponse = notifyProcessorPoolsOnResponse;
  }

  public void setDbStorage(ScottyDBStorageInterface dbStorage) {
    this.dbStorage = dbStorage;
  }

  public ScottyDBStorageInterface getDbStorage() {
    return dbStorage;
  }

  public void setProcessorPoolManager(ProcessorPoolManager<? extends PersistentProcessorPool> processorPoolManager) {
    this.processorPoolManager = processorPoolManager;
  }


  @Override
  public void notify(Response<?> response, Acknowledge ack) {
    if (logger.isTraceEnabled()) logger.trace("notify("+response+")");
    try {
      if (response.getResponseId() == null) {
        response.setResponseId(createUUID());
      }
      startupBlocker.pass();
      dbStorage.notify(response,ack);
      if (notifyProcessorPoolsOnResponse) {
        for (PersistentProcessorPool ppp : processorPoolManager.processorPools()) {
          ppp.doNotify();
        }
      }
    }
    catch (Exception e) {
      throw new CopperRuntimeException("notify failed",e);
    }

  }

  @Override
  public synchronized void shutdown() {
    if (engineState != EngineState.STARTED) {
      logger.debug("engine is not running - shutdown aborted");
      return;
    }
    logger.info("Engine is shutting down...");
    engineState = EngineState.SHUTTING_DOWN;
    processorPoolManager.shutdown();
    dbStorage.shutdown();
    super.shutdown();
    logger.info("Engine is stopped");
    engineState = EngineState.STOPPED;
  }

  @Override
  public synchronized void startup() {
    if (engineState != EngineState.RAW) throw new IllegalStateException();
    try {
      logger.info("starting up...");

      processorPoolManager.setEngine(this);
      dependencyInjector.setEngine(this);

      wfRepository.start();
      dbStorage.startup();

      processorPoolManager.startup();
      startupBlocker.unblock();
      engineState = EngineState.STARTED;

      logger.info("Engine is running");
    }
    catch(RuntimeException e) {
      throw e;
    }
    catch(Exception e) {
      throw new CopperRuntimeException("startup failed",e);
    }
  }

  @Override
  public void registerCallbacks(Workflow<?> w, WaitMode mode, long timeoutMsec, String... correlationIds) {
    if (logger.isTraceEnabled()) logger.trace("registerCallbacks("+w+", "+mode+", "+timeoutMsec+", "+Arrays.asList(correlationIds)+")");
    if (correlationIds.length == 0) throw new IllegalArgumentException("No correlationids given");
    PersistentWorkflow<?> pw = (PersistentWorkflow<?>)w;
    if (processorPoolManager.getProcessorPool(pw.getProcessorPoolId()) == null) {
      logger.error("Unkown processor pool '"+pw.getProcessorPoolId()+"' - using default pool instead");
      pw.setProcessorPoolId(PersistentProcessorPool.DEFAULT_POOL_ID);
    }
    pw.registerCall = new RegisterCall(w, mode, timeoutMsec > 0 ? timeoutMsec : null, correlationIds, getAndRemoveWaitHooks(pw));
  }

  @Override
  protected void run(Workflow<?> wf) throws CopperException {
    run(wf,null);
  }

  private void notifyProcessorPool(String ppoolId) {
    PersistentProcessorPool pp = processorPoolManager.getProcessorPool(ppoolId);
    if (pp == null) {
      pp = processorPoolManager.getProcessorPool(PersistentProcessorPool.DEFAULT_POOL_ID);
    }
    if (pp != null) {
      pp.doNotify();
    }
  }

  @Override
  public void run(List<Workflow<?>> list) throws CopperException {
    run(list,null);
  }

  /**
   * Enqueues the specified list of workflow instances into the engine for execution. 
   * @param w the list of workflow instances to run
   * @param con connection used for the inserting the workflow to the database
   * @throws CopperException if the engine can not run the workflow, e.g. in case of a unkown processor pool id
   */
  public void run(List<Workflow<?>> list, Connection con) throws CopperException {
    if (logger.isTraceEnabled()) {
      for (Workflow<?> w : list)
        logger.trace("run("+w+")");
    }
    try {
      startupBlocker.pass();

      Set<String> ppoolIds = new HashSet<String>();
      for (Workflow<?> wf : list) {
        if (!(wf instanceof PersistentWorkflow<?>)) {
          throw new IllegalArgumentException(wf.getClass()+" is no instance of PersistentWorkflow");
        }
        if (wf.getId() == null) {
          wf.setId(createUUID());
        }
        if (wf.getProcessorPoolId() == null) {
          wf.setProcessorPoolId(PersistentProcessorPool.DEFAULT_POOL_ID);
        }

        if (processorPoolManager.getProcessorPool(wf.getProcessorPoolId()) == null) {
          logger.error("Unkown processor pool '"+wf.getProcessorPoolId()+"' - using default pool instead");
          wf.setProcessorPoolId(PersistentProcessorPool.DEFAULT_POOL_ID);
        }

        ppoolIds.add(wf.getProcessorPoolId());
      }
      dbStorage.insert(list, con);
      for (String ppoolId : ppoolIds) {
        notifyProcessorPool(ppoolId);
      }
    }
    catch(RuntimeException e) {
      throw e;
    }
    catch(CopperException e) {
      throw e;
    }
    catch(Exception e) {
      throw new CopperException("run failed",e);
    }
  }

  /**
   * Enqueues the specified workflow instance into the engine for execution. 
   * @param w the workflow instance to run
   * @param con connection used for the inserting the workflow to the database
   * @throws CopperException if the engine can not run the workflow, e.g. in case of a unkown processor pool id
   */
  public void run(Workflow<?> wf, Connection con) throws CopperException {
    if (logger.isTraceEnabled()) logger.trace("run("+wf+")");
    if (!(wf instanceof PersistentWorkflow<?>)) {
      throw new IllegalArgumentException(wf.getClass()+" is no instance of PersistentWorkflow");
    }
    try {
      startupBlocker.pass();

      if (wf.getId() == null) {
        wf.setId(createUUID());
      }
      if (wf.getProcessorPoolId() == null) {
        wf.setProcessorPoolId(PersistentProcessorPool.DEFAULT_POOL_ID);
      }
      if (processorPoolManager.getProcessorPool(wf.getProcessorPoolId()) == null) {
        logger.error("Unkown processor pool '"+wf.getProcessorPoolId()+"' - using default pool instead");
        wf.setProcessorPoolId(PersistentProcessorPool.DEFAULT_POOL_ID);
      }
      dbStorage.insert(wf, con);
      notifyProcessorPool(wf.getProcessorPoolId());
    }
    catch(RuntimeException e) {
      throw e;
    }
    catch(CopperException e) {
      throw e;
    }
    catch(Exception e) {
      throw new CopperException("run failed",e);
    }

  }

  @Override
  public void restart(String workflowInstanceId) throws Exception {
    dbStorage.restart(workflowInstanceId);
  }

  @Override
  public String getState() {
    return getEngineState().name();
  }

  @Override
  public List<WorkflowInfo> queryWorkflowInstances() {
    List<WorkflowInfo> rv = new ArrayList<WorkflowInfo>();
    for (Workflow<?> wf : workflowMap.values()) {
      WorkflowInfo wfi = convert2Wfi(wf);
      rv.add(wfi);
    }
    logger.info("queryWorkflowInstances returned "+rv.size()+" instance(s)");
    return rv;
  }

  @Override
  public WorkflowInfo queryWorkflowInstance(String id) {
    return convert2Wfi(workflowMap.get(id));
  }


  void register(Workflow<?> wf) {
    if (logger.isTraceEnabled()) logger.trace("register("+wf.getId()+")");
    Workflow<?> existingWF = workflowMap.put(wf.getId(),wf);
    assert existingWF == null;
  }

  void unregister(Workflow<?> wf) {
    Workflow<?> existingWF = workflowMap.remove(wf.getId());
    assert existingWF != null;
    if (existingWF != null && existingWF.getProcessingState() == ProcessingState.FINISHED) {
      statisticsCollector.submit(getEngineId()+"."+wf.getClass().getSimpleName()+".ExecutionTime", 1, System.currentTimeMillis()-wf.getCreationTS().getTime(), TimeUnit.MILLISECONDS);
    }
    getAndRemoveWaitHooks(wf); // Clean up...
  }

  public int getNumberOfWorkflowInstances() {
    return workflowMap.size();
  }

  @Override
  public void restartAll() throws Exception {
    dbStorage.restartAll();
  }

  @Override
  public void run(WorkflowInstanceDescr<?> wfInstanceDescr, Connection con) throws CopperException {
    try {
      this.run(createWorkflowInstance(wfInstanceDescr), con);
    }
    catch(CopperException e) {
      throw e;
    }
    catch(RuntimeException e) {
      throw e;
    }
    catch(Exception e) {
      throw new CopperException("run failed",e);
    }
  }

  @Override
  public void runBatch(List<WorkflowInstanceDescr<?>> wfInstanceDescr, Connection con) throws CopperException {
    try {
      List<Workflow<?>> wfList = new ArrayList<Workflow<?>>(wfInstanceDescr.size());
      for (WorkflowInstanceDescr<?> wfInsDescr : wfInstanceDescr) {
        wfList.add(createWorkflowInstance(wfInsDescr));
      }
      run(wfList, con);
    }
    catch(CopperException e) {
      throw e;
    }
    catch(RuntimeException e) {
      throw e;
    }
    catch(Exception e) {
      throw new CopperException("run failed",e);
    }
  }

  @Override
  public void notify(Response<?> response, Connection c) throws CopperRuntimeException {
    final List<Response<?>> list = new ArrayList<Response<?>>(1);
    list.add(response);
    this.notify(list, c);
  }

  @Override
  public void notify(List<Response<?>> responses, Connection c) throws CopperRuntimeException {
    try  {
      for (Response<?> r : responses) {
        if (r.getResponseId() == null) {
          r.setResponseId(createUUID());
        }
      }
      dbStorage.notify(responses,c);
    }
    catch(RuntimeException e) {
      throw e;
    }
    catch(Exception e) {
      throw new CopperRuntimeException(e);
    }
  }

  @Override
  public void addWaitHook(String wfInstanceId, WaitHook waitHook) {
    if (wfInstanceId == null) throw new NullPointerException();
    if (waitHook == null) throw new NullPointerException();
   
    synchronized (waitHookMap) {
      if (!workflowMap.containsKey(wfInstanceId)) {
        throw new CopperRuntimeException("Unkown workflow instance with id '"+wfInstanceId+"'");
      }
      List<WaitHook> l = waitHookMap.get(wfInstanceId);
      if (l == null) {
        l = new ArrayList<WaitHook>();
        waitHookMap.put(wfInstanceId, l);
      }
      l.add(waitHook);
    }
  }
 
  private List<WaitHook> getAndRemoveWaitHooks(Workflow<?> wf) {
    synchronized (waitHookMap) {
      List<WaitHook> l = waitHookMap.remove(wf.getId());
      return l == null ? Collections.<WaitHook>emptyList() : l;
    }
  }

  @Override
  public List<ProcessorPoolMXBean> getProcessorPools() {
    final List<ProcessorPoolMXBean> result = new ArrayList<ProcessorPoolMXBean>();
    for (ProcessorPool pp : processorPoolManager.processorPools()) {
      if (pp instanceof ProcessorPoolMXBean) {
        result.add((ProcessorPoolMXBean) pp);
      }
    }
    return result;
  }

  @Override
  public EngineType getEngineType() {
    return EngineType.persistent;
  }

  @Override
  public DBStorageMXBean getDBStorage() {
    return (DBStorageMXBean) (dbStorage instanceof DBStorageMXBean ? dbStorage : null);
  }

}
TOP

Related Classes of de.scoopgmbh.copper.persistent.PersistentScottyEngine

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.