Пример #1
0
/**
 * The BPEL server implementation.
 *
 * <p>This implementation is intended to be thread safe. The key concurrency mechanism is a
 * "management" read/write lock that synchronizes all management operations (they require "write"
 * access) and prevents concurrent management operations and processing (processing requires "read"
 * access). Write access to the lock is scoped to the method, while read access is scoped to a
 * transaction.
 *
 * @author Maciej Szefler <mszefler at gmail dot com>
 * @author Matthieu Riou <mriou at apache dot org>
 */
public class BpelServerImpl implements BpelServer, Scheduler.JobProcessor {

  private static final Log __log = LogFactory.getLog(BpelServerImpl.class);

  private static final Messages __msgs = MessageBundle.getMessages(Messages.class);

  /** Maximum age of a process before it is quiesced */
  private static Long __processMaxAge;

  /** RNG, for delays */
  private Random _random = new Random(System.currentTimeMillis());

  private static double _delayMean = 0;

  /**
   * Set of processes that are registered with the server. Includes hydrated and dehydrated
   * processes. Guarded by _mngmtLock.writeLock().
   */
  private final ConcurrentHashMap<QName, ODEProcess> _registeredProcesses =
      new ConcurrentHashMap<QName, ODEProcess>();

  /** Mapping from myrole service name to active process. */
  private final HashMap<QName, List<ODEProcess>> _serviceMap =
      new HashMap<QName, List<ODEProcess>>();

  /** Weak-reference cache of all the my-role message exchange objects. */
  private final MyRoleMessageExchangeCache _myRoleMexCache = new MyRoleMessageExchangeCache();

  private State _state = State.SHUTDOWN;

  Contexts _contexts = new Contexts();

  private DehydrationPolicy _dehydrationPolicy;

  private OdeConfigProperties _properties;

  private ExecutorService _exec;

  BpelDatabase _db;

  /** The last time we started a {@link ServerCallable}. Useful for keeping track of idleness. */
  private final AtomicLong _lastTimeOfServerCallable = new AtomicLong(System.currentTimeMillis());

  /** Mapping from a potentially shared endpoint to its EPR */
  private SharedEndpoints _sharedEps;

  static {
    // TODO Clean this up and factorize engine configuration
    try {
      String processMaxAge = System.getProperty("ode.process.maxage");
      if (processMaxAge != null && processMaxAge.length() > 0) {
        __processMaxAge = Long.valueOf(processMaxAge);
        __log.info("Process definition max age adjusted. Max age = " + __processMaxAge + "ms.");
      }
    } catch (Throwable t) {
      if (__log.isDebugEnabled()) {
        __log.debug("Could not parse ode.process.maxage environment variable.", t);
      } else {
        __log.info("Could not parse ode.process.maxage environment variable; reaping disabled.");
      }
    }
  }

  private enum State {
    SHUTDOWN,
    INIT,
    RUNNING
  }

  public BpelServerImpl() {}

  protected void waitForQuiessence() {
    do {
      long ltime = _lastTimeOfServerCallable.get();
      try {
        Thread.sleep(150);
      } catch (InterruptedException e) {;
      }
      try {
        Thread.sleep(150);
      } catch (InterruptedException ie) {;
      }
      if (_lastTimeOfServerCallable.get() == ltime) return;
    } while (true);
  }

  public void start() {
    if (!checkState(State.INIT, State.RUNNING)) {
      __log.debug("start() ignored -- already started");
      return;
    }

    __log.debug("BPEL SERVER starting.");

    if (_exec == null) {
      ThreadFactory threadFactory =
          new ThreadFactory() {
            int threadNumber = 0;

            public Thread newThread(Runnable r) {
              threadNumber += 1;
              Thread t = new Thread(r, "ODEServerImpl-" + threadNumber);
              t.setDaemon(true);
              return t;
            }
          };
      _exec = Executors.newCachedThreadPool(threadFactory);
    }

    if (_contexts.txManager == null) {
      String errmsg = "Transaction manager not specified; call setTransactionManager(...)!";
      __log.fatal(errmsg);
      throw new IllegalStateException(errmsg);
    }

    if (_contexts.scheduler == null) {
      String errmsg = "Scheduler not specified; call setScheduler(...)!";
      __log.fatal(errmsg);
      throw new IllegalStateException(errmsg);
    }

    _contexts.scheduler.start();
    _state = State.RUNNING;
    __log.info(__msgs.msgServerStarted());
    if (_dehydrationPolicy != null) new Thread(new ProcessDefReaper()).start();
  }

  public BpelDatabase getBpelDb() {
    return _db;
  }

  public void registerExternalVariableEngine(ExternalVariableModule eve) {
    _contexts.externalVariableEngines.put(eve.getName(), eve);
  }

  /**
   * Register a global listener to receive {@link BpelEvent}s froom all processes.
   *
   * @param listener
   */
  public void registerBpelEventListener(BpelEventListener listener) {
    listener.startup(_properties.getProperties());

    // Do not synchronize, eventListeners is copy-on-write array.
    _contexts.eventListeners.add(listener);
  }

  /**
   * Unregister a global listener from receive {@link BpelEvent}s from all processes.
   *
   * @param listener
   */
  public void unregisterBpelEventListener(BpelEventListener listener) {
    // Do not synchronize, eventListeners is copy-on-write array.
    if (_contexts.eventListeners.remove(listener)) {
      try {
        listener.shutdown();
      } catch (Exception e) {
        __log.warn(
            "Stopping BPEL event listener "
                + listener.getClass().getName()
                + " failed, nevertheless it has been unregistered.",
            e);
      }
    }
  }

  private void unregisterBpelEventListeners() {
    for (BpelEventListener l : _contexts.eventListeners) {
      unregisterBpelEventListener(l);
    }
  }

  public void registerExtensionBundle(ExtensionBundleRuntime bundle) {
    _contexts.extensionRegistry.put(bundle.getNamespaceURI(), bundle);
    bundle.registerExtensionActivities();
  }

  public void unregisterExtensionBundle(String nsURI) {
    _contexts.extensionRegistry.remove(nsURI);
  }

  public void stop() {
    if (!checkState(State.RUNNING, State.INIT)) {
      __log.debug("stop() ignored -- already stopped");
      return;
    }

    __log.debug("BPEL SERVER STOPPING");

    _contexts.scheduler.stop();
    _state = State.INIT;
    __log.info(__msgs.msgServerStopped());
  }

  public void init() throws BpelEngineException {
    if (!checkState(State.SHUTDOWN, State.INIT)) return;

    __log.debug("BPEL SERVER initializing ");

    _db = new BpelDatabase(_contexts);
    _state = State.INIT;
    _sharedEps = new SharedEndpoints();
    _sharedEps.init();
  }

  public void shutdown() throws BpelEngineException {
    stop();
    unregisterBpelEventListeners();

    _sharedEps = null;
    _db = null;
    _state = State.SHUTDOWN;
  }

  public void register(ProcessConf conf) {
    if (conf == null)
      throw new NullPointerException("must specify non-null process configuration.");

    __log.debug("register: " + conf.getProcessId());

    // Ok, IO out of the way, we will mod the server state, so need to get a
    // lock.

    // If the process is already active, do nothing.
    if (_registeredProcesses.containsKey(conf.getProcessId())) {
      __log.debug(
          "skipping doRegister" + conf.getProcessId() + ") -- process is already registered");
      return;
    }

    __log.debug("Registering process " + conf.getProcessId() + " with server.");

    ODEProcess process = new ODEProcess(this, conf, null, _myRoleMexCache);

    for (Endpoint e : process.getServiceNames()) {
      __log.debug("Register process: serviceId=" + e + ", process=" + process);
      // Get the list of processes associated with the given service
      List<ODEProcess> processes = _serviceMap.get(e.serviceName);
      if (processes == null) {
        // Create an empty list, if no processes were associated
        _serviceMap.put(e.serviceName, processes = new ArrayList<ODEProcess>());
      }
      // Remove any older version of the process from the list
      for (int i = 0; i < processes.size(); i++) {
        ODEProcess cachedVersion = processes.get(i);
        __log.debug(
            "cached version "
                + cachedVersion.getPID()
                + " vs registering version "
                + process.getPID());
        if (cachedVersion.getProcessType().equals(process.getProcessType())) {
          processes.remove(cachedVersion);
        }
      }
      // Add the given process to the list associated with the given service
      processes.add(process);
    }

    process.activate(_contexts);

    _registeredProcesses.put(process.getPID(), process);
    if (_dehydrationPolicy == null) process.hydrate();

    __log.info(__msgs.msgProcessRegistered(conf.getProcessId()));
  }

  public void unregister(QName pid) throws BpelEngineException {
    if (__log.isTraceEnabled()) __log.trace("unregister: " + pid);

    try {
      ODEProcess p = _registeredProcesses.remove(pid);
      if (p == null) return;

      // TODO Looks like there are some possible bugs here, if a new version of a process gets
      // deployed, the service will be removed.
      p.deactivate();

      // Remove the process from any services that might reference it.
      // However, don't remove the service itself from the map.
      for (List<ODEProcess> processes : _serviceMap.values()) {
        __log.debug(
            "removing process " + pid + "; handle " + p + "; exists " + processes.contains(p));
        processes.remove(p);
      }

      __log.info(__msgs.msgProcessUnregistered(pid));

    } catch (Exception ex) {
      __log.error(__msgs.msgProcessUnregisterFailed(pid), ex);
      throw new BpelEngineException(ex);
    }
  }

  public void cleanupProcess(QName pid) throws BpelEngineException {
    deleteProcessDAO(pid);
  }

  /**
   * Register a global message exchange interceptor.
   *
   * @param interceptor message-exchange interceptor
   */
  public void registerMessageExchangeInterceptor(MessageExchangeInterceptor interceptor) {
    // NOTE: do not synchronize, globalInterceptors is copy-on-write.
    _contexts.globalIntereceptors.add(interceptor);
  }

  /**
   * Unregister a global message exchange interceptor.
   *
   * @param interceptor message-exchange interceptor
   */
  public void unregisterMessageExchangeInterceptor(MessageExchangeInterceptor interceptor) {
    // NOTE: do not synchronize, globalInterceptors is copy-on-write.
    _contexts.globalIntereceptors.remove(interceptor);
  }

  /**
   * Route to a process using the service id. Note, that we do not need the endpoint name here, we
   * are assuming that two processes would not be registered under the same service qname but
   * different endpoint.
   *
   * @param service target service id
   * @param request request message
   * @return process corresponding to the targetted service, or <code>null</code> if service
   *     identifier is not recognized.
   */
  List<ODEProcess> route(QName service, Message request) {
    // TODO: use the message to route to the correct service if more than
    // one service is listening on the same endpoint.
    return _serviceMap.get(service);
  }

  /** Check a state transition from state "i" to state "j". */
  private final boolean checkState(State i, State j) {
    if (_state == i) return true;
    if (_state == j) return false;
    return false;
  }

  /* TODO: We need to have a method of cleaning up old deployment data. */
  private boolean deleteProcessDAO(final QName pid) {
    try {
      // Delete it from the database.
      return _db.exec(
          new BpelDatabase.Callable<Boolean>() {
            public Boolean run(BpelDAOConnection conn) throws Exception {
              ProcessDAO proc = conn.getProcess(pid);
              if (proc != null) {
                proc.delete();
                return true;
              }
              return false;
            }
          });
    } catch (Exception ex) {
      String errmsg = "DbError";
      __log.error(errmsg, ex);
      throw new BpelEngineException(errmsg, ex);
    }
  }

  public void onScheduledJob(final JobInfo jobInfo) throws JobProcessorException {
    try {
      final WorkEvent we = new WorkEvent(jobInfo.jobDetail);
      ODEProcess process = _registeredProcesses.get(we.getProcessId());
      if (process == null) {
        // If the process is not active, it means that we should not be
        // doing any work on its behalf, therefore we will reschedule the
        // events for some time in the future (1 minute).

        // 31-05-2010
        // According to the bpel server logic, we won't find a process with matching
        // process id in future for this type of case where we can't find the process at
        // first job invocation. Because all the processes(both hydrated and dehydrated) are there
        // in _registeredProcesses Map. And even we deploy a new version of the process, it's
        // QName will be different because of the versioning.
        // So I am removing scheduling part.
        // TODO: Let's revert the logic if an issue occurred because of this.
        _contexts.execTransaction(
            new Callable<Void>() {
              public Void call() throws Exception {
                _contexts.scheduler.jobCompleted(jobInfo.jobName);
                // Date future = new Date(System.currentTimeMillis() + (60 * 1000));
                // __log.info(__msgs.msgReschedulingJobForInactiveProcess(we.getProcessId(),
                // jobInfo.jobName, future));
                // _contexts.scheduler.schedulePersistedJob(we.getDetail(), future);
                return null;
              }
            });
        return;
      }

      if (we.getType().equals(WorkEvent.Type.INVOKE_CHECK)) {
        if (__log.isDebugEnabled())
          __log.debug("handleWorkEvent: InvokeCheck event for mexid " + we.getMexId());

        PartnerRoleMessageExchange mex =
            (PartnerRoleMessageExchange) getMessageExchange(we.getMexId());
        if (mex.getStatus() == MessageExchange.Status.ASYNC
            || mex.getStatus() == MessageExchange.Status.ACK) {
          String msg =
              "Dangling invocation (mexId=" + we.getMexId() + "), forcing it into a failed state.";
          if (__log.isDebugEnabled()) __log.debug(msg);
          mex.replyWithFailure(MessageExchange.FailureType.COMMUNICATION_ERROR, msg, null);
        }
        return;
      }

      process.handleWorkEvent(jobInfo);
    } catch (Exception ex) {
      throw new JobProcessorException(ex, jobInfo.jobDetail.get("inmem") == null);
    }
  }

  public void setTransactionManager(TransactionManager txm) {
    _contexts.txManager = txm;
  }

  public void setDehydrationPolicy(DehydrationPolicy dehydrationPolicy) {
    _dehydrationPolicy = dehydrationPolicy;
  }

  public void setConfigProperties(OdeConfigProperties properties) {
    _properties = properties;
  }

  public OdeConfigProperties getConfigProperties() {
    return _properties;
  }

  public void setMessageExchangeContext(MessageExchangeContext mexContext)
      throws BpelEngineException {
    _contexts.mexContext = mexContext;
  }

  public void setScheduler(Scheduler scheduler) throws BpelEngineException {
    _contexts.scheduler = scheduler;
  }

  public void setEndpointReferenceContext(EndpointReferenceContext eprContext)
      throws BpelEngineException {
    _contexts.eprContext = eprContext;
  }

  /**
   * Set the DAO connection factory. The DAO is used by the BPEL engine to persist information about
   * active processes.
   *
   * @param daoCF {@link BpelDAOConnectionFactory} implementation.
   */
  public void setDaoConnectionFactory(BpelDAOConnectionFactory daoCF) throws BpelEngineException {
    _contexts.dao = daoCF;
  }

  public void setBindingContext(BindingContext bc) {
    _contexts.bindingContext = bc;
  }

  public SharedEndpoints getSharedEndpoints() {
    return _sharedEps;
  }

  public void setExecutor(ExecutorService exec) {
    _exec = exec;
  }

  public MyRoleMessageExchange createMessageExchange(
      final InvocationStyle istyle,
      final QName targetService,
      final String operation,
      final String clientKey)
      throws BpelEngineException {

    // Obtain the list of processes that this service is potentially targeted at
    final List<ODEProcess> targets = route(targetService, null);

    if (targets == null || targets.size() == 0)
      throw new BpelEngineException("NoSuchService: " + targetService);

    if (targets.size() == 1) {
      // If the number of targets is one, create and return a simple MEX
      ODEProcess target = targets.get(0);
      return createNewMyRoleMex(target, istyle, targetService, operation, clientKey);
    } else {
      // If the number of targets is greater than one, create and return
      // a brokered MEX that embeds the simple MEXs for each of the targets
      ArrayList<MyRoleMessageExchange> meps = new ArrayList<MyRoleMessageExchange>();
      for (ODEProcess target : targets) {
        meps.add(createNewMyRoleMex(target, istyle, targetService, operation, clientKey));
      }
      return createNewMyRoleMex(targets.get(0), meps, istyle);
    }
  }

  /**
   * Return a simple type of MEX for a given process target
   *
   * @param process
   * @param istyle
   * @param targetService
   * @param operation
   * @param clientKey
   * @return
   */
  private MyRoleMessageExchange createNewMyRoleMex(
      ODEProcess process,
      final InvocationStyle istyle,
      final QName targetService,
      final String operation,
      final String clientKey) {
    if (istyle == InvocationStyle.RELIABLE || istyle == InvocationStyle.TRANSACTED)
      assertTransaction();
    else assertNoTransaction();

    return process.createNewMyRoleMex(istyle, targetService, operation);
  }

  /**
   * Return a brokered MEX that delegates invocations to each of the embedded MEXs contained in the
   * <code>meps</code> list, using the appropriate style.
   *
   * @param target
   * @param meps
   * @param istyle
   * @return
   * @throws BpelEngineException
   */
  private MyRoleMessageExchange createNewMyRoleMex(
      ODEProcess target, List<MyRoleMessageExchange> meps, InvocationStyle istyle)
      throws BpelEngineException {
    String mexId = new GUID().toString();
    MyRoleMessageExchange template = meps.get(0);
    switch (istyle) {
      case RELIABLE:
        return new BrokeredReliableMyRoleMessageExchangeImpl(target, meps, mexId, template);
      case TRANSACTED:
        return new BrokeredTransactedMyRoleMessageExchangeImpl(target, meps, mexId, template);
      case UNRELIABLE:
        return new BrokeredUnreliableMyRoleMessageExchangeImpl(target, meps, mexId, template);
      case P2P:
      default:
        throw new BpelEngineException("Unsupported Invocation Style: " + istyle);
    }
  }

  public MessageExchange getMessageExchange(final String mexId) throws BpelEngineException {

    final MessageExchangeDAO inmemdao = getInMemMexDAO(mexId);

    Callable<MessageExchange> loadMex =
        new Callable<MessageExchange>() {

          public MessageExchange call() {
            MessageExchangeDAO mexdao =
                (inmemdao == null)
                    ? mexdao = _contexts.dao.getConnection().getMessageExchange(mexId)
                    : inmemdao;
            if (mexdao == null) return null;

            ProcessDAO pdao = mexdao.getProcess();
            ODEProcess process =
                pdao == null ? null : _registeredProcesses.get(pdao.getProcessId());

            if (process == null) {
              String errmsg = __msgs.msgProcessNotActive(pdao.getProcessId());
              __log.error(errmsg);
              // TODO: Perhaps we should define a checked exception for this
              // condition.
              throw new BpelEngineException(errmsg);
            }

            InvocationStyle istyle = mexdao.getInvocationStyle();
            if (istyle == InvocationStyle.RELIABLE || istyle == InvocationStyle.TRANSACTED)
              assertTransaction();

            switch (mexdao.getDirection()) {
              case MessageExchangeDAO.DIR_BPEL_INVOKES_PARTNERROLE:
                return process.createPartnerRoleMex(mexdao);
              case MessageExchangeDAO.DIR_PARTNER_INVOKES_MYROLE:
                return process.lookupMyRoleMex(mexdao);
              default:
                String errmsg =
                    "BpelEngineImpl: internal error, invalid MexDAO direction: " + mexId;
                __log.fatal(errmsg);
                throw new BpelEngineException(errmsg);
            }
          }
        };

    try {
      if (inmemdao != null || _contexts.isTransacted()) return loadMex.call();
      else return enqueueTransaction(loadMex).get();
    } catch (ContextException e) {
      throw new BpelEngineException(e);
    } catch (Exception e) {
      throw new BpelEngineException(e);
    }
  }

  public MessageExchange getMessageExchangeByForeignKey(String foreignKey)
      throws BpelEngineException {
    // TODO Auto-generated method stub
    return null;
  }

  /*
   * Calculate the invocation style of the target(s) associated with the given service
   * If more than one target exists, then take the intersection of their individual styles.
   *
   * (non-Javadoc)
   * @see org.apache.ode.bpel.iapi.BpelServer#getSupportedInvocationStyle(javax.xml.namespace.QName)
   */
  public Set<InvocationStyle> getSupportedInvocationStyle(QName serviceId) {

    List<ODEProcess> processes = route(serviceId, null);
    if (processes == null || processes.size() == 0)
      throw new BpelEngineException("No such service: " + serviceId);

    // Compute the intersection of the styles of all providing processes
    Set<InvocationStyle> istyles = new HashSet<InvocationStyle>();
    for (ODEProcess process : processes) {
      Set<InvocationStyle> pistyles = process.getSupportedInvocationStyle(serviceId);
      if (istyles.isEmpty()) {
        istyles.addAll(pistyles);
      } else {
        for (InvocationStyle istyle : istyles) {
          if (!pistyles.contains(istyle)) {
            istyles.remove(istyle);
          }
        }
      }
    }
    return istyles;
  }

  MessageExchangeDAO getInMemMexDAO(String mexId) {
    for (ODEProcess p : _registeredProcesses.values()) {
      MessageExchangeDAO mexDao = p.getInMemMexDAO(mexId);
      if (mexDao != null) return mexDao;
    }

    return null;
  }

  public ProcessModel getProcessModel(QName processId) {
    ODEProcess process = _registeredProcesses.get(processId);

    if (process == null) return null;

    return process.getProcessModel();
  }

  <T> Future<T> enqueueTransaction(final Callable<T> transaction) throws ContextException {
    return _exec.submit(new ServerCallable<T>(new TransactedCallable<T>(transaction)));
  }

  void enqueueRunnable(final Runnable runnable) {
    _exec.submit(new ServerRunnable(runnable));
  }

  /**
   * Schedule a {@link Runnable} object for execution after the completion of the current
   * transaction.
   *
   * @param runnable
   */
  void scheduleRunnable(final Runnable runnable) {
    assertTransaction();
    _contexts.registerCommitSynchronizer(
        new Runnable() {

          public void run() {
            _exec.submit(new ServerRunnable(runnable));
          }
        });
  }

  protected void assertTransaction() {
    if (!_contexts.isTransacted())
      throw new BpelEngineException("Operation must be performed in a transaction!");
  }

  protected void assertNoTransaction() {
    if (_contexts.isTransacted())
      throw new BpelEngineException("Operation must be performed outside of a transaction!");
  }

  void fireEvent(BpelEvent event) {
    // Note that the eventListeners list is a copy-on-write array, so need
    // to mess with synchronization.
    for (org.apache.ode.bpel.iapi.BpelEventListener l : _contexts.eventListeners) {
      l.onEvent(event);
    }
  }

  /**
   * Block the thread for random amount of time. Used for testing for races and the like. The delay
   * generated is exponentially distributed with the mean obtained from the <code>ODE_DEBUG_TX_DELAY
   * </code> environment variable.
   */
  private void debuggingDelay() {
    // Do a delay for debugging purposes.
    if (_delayMean != 0)
      try {
        long delay = randomExp(_delayMean);
        // distribution
        // with mean
        // _delayMean
        __log.warn("Debugging delay has been activated; delaying transaction for " + delay + "ms.");
        Thread.sleep(delay);
      } catch (InterruptedException e) {; // ignore
      }
  }

  private long randomExp(double mean) {
    double u = _random.nextDouble(); // Uniform
    return (long) (-Math.log(u) * mean);
  }

  private class ProcessDefReaper implements Runnable {
    public void run() {
      __log.debug("Starting process definition reaper thread.");
      long pollingTime = 10000;
      try {
        while (true) {
          Thread.sleep(pollingTime);
          // Copying the runnning process list to avoid synchronizatMessageExchangeInterion
          // problems and a potential mess if a policy modifies the list
          List<ODEProcess> candidates = new ArrayList<ODEProcess>(_registeredProcesses.values());
          CollectionsX.remove_if(
              candidates,
              new MemberOfFunction<ODEProcess>() {
                public boolean isMember(ODEProcess o) {
                  return !o.hintIsHydrated();
                }
              });

          // And the happy winners are...
          List<ODEProcess> ripped = _dehydrationPolicy.markForDehydration(candidates);
          // Bye bye
          for (ODEProcess process : ripped) {
            __log.debug("Dehydrating process " + process.getPID());
            process.dehydrate();
          }
        }
      } catch (InterruptedException e) {
        __log.info(e);
      }
    }
  }

  public ODEProcess getBpelProcess(QName processId) {
    return _registeredProcesses.get(processId);
  }

  private void ticktock() {
    _lastTimeOfServerCallable.set(System.currentTimeMillis());
  }

  class ServerRunnable implements Runnable {
    final Runnable _work;

    ServerRunnable(Runnable work) {
      _work = work;
    }

    public void run() {
      ticktock();
      try {
        ticktock();
        _work.run();
        ticktock();
      } catch (Throwable ex) {
        ticktock();
        __log.fatal("Internal Error", ex);
      }
    }
  }

  class ServerCallable<T> implements Callable<T> {
    final Callable<T> _work;

    ServerCallable(Callable<T> work) {
      _work = work;
    }

    public T call() throws Exception {
      ticktock();
      try {
        ticktock();
        return _work.call();
      } catch (Exception ex) {
        ticktock();
        __log.fatal("Internal Error", ex);
        throw ex;
      } finally {
        ticktock();
      }
    }
  }

  class TransactedCallable<T> implements Callable<T> {
    Callable<T> _work;

    TransactedCallable(Callable<T> work) {
      _work = work;
    }

    public T call() throws Exception {
      return _contexts.execTransaction(_work);
    }
  }

  class TransactedRunnable implements Runnable {
    Runnable _work;

    TransactedRunnable(Runnable work) {
      _work = work;
    }

    public void run() {
      _contexts.execTransaction(_work);
    }
  }

  public void setTransacted(boolean atomicScope) {}
}
Пример #2
0
/** @author Matthieu Riou <mriou at apache dot org> */
public class PartnerLinkMyRoleImpl extends PartnerLinkRoleImpl {
  private static final Log __log = LogFactory.getLog(BpelProcess.class);
  private static final Messages __msgs = MessageBundle.getMessages(Messages.class);

  /** The local endpoint for this "myrole". */
  public Endpoint _endpoint;

  PartnerLinkMyRoleImpl(BpelProcess process, OPartnerLink plink, Endpoint endpoint) {
    super(process, plink);
    _endpoint = endpoint;
  }

  public String toString() {
    StringBuffer buf = new StringBuffer("{PartnerLinkRole-");
    buf.append(_plinkDef.name);
    buf.append('.');
    buf.append(_plinkDef.myRoleName);
    buf.append(" on ");
    buf.append(_endpoint);
    buf.append('}');

    return buf.toString();
  }

  public boolean isCreateInstance(MyRoleMessageExchangeImpl mex) {
    Operation operation = getMyRoleOperation(mex.getOperationName());
    return _plinkDef.isCreateInstanceOperation(operation);
  }

  public List<RoutingInfo> findRoute(MyRoleMessageExchangeImpl mex) {
    List<RoutingInfo> routingInfos = new ArrayList<RoutingInfo>();

    if (__log.isTraceEnabled()) {
      __log.trace(
          ObjectPrinter.stringifyMethodEnter(
              this + ":inputMsgRcvd", new Object[] {"messageExchange", mex}));
    }

    Operation operation = getMyRoleOperation(mex.getOperationName());
    if (operation == null) {
      __log.error(
          __msgs.msgUnknownOperation(mex.getOperationName(), _plinkDef.myRolePortType.getQName()));
      mex.setFailure(MessageExchange.FailureType.UNKNOWN_OPERATION, mex.getOperationName(), null);
      return null;
    }
    setMexRole(mex);

    // now, the tricks begin: when a message arrives we have to see if there
    // is anyone waiting for it. Get the correlator, a persisted communication-reduction
    // data structure supporting correlation correlationKey matching!
    String correlatorId = BpelProcess.genCorrelatorId(_plinkDef, operation.getName());

    CorrelatorDAO correlator = _process.getProcessDAO().getCorrelator(correlatorId);

    CorrelationKeySet keySet;

    // We need to compute the correlation keys (based on the operation
    // we can  infer which correlation keys to compute - this is merely a set
    // consisting of each correlationKey used in each correlation sets
    // that is ever referenced in an <receive>/<onMessage> on this
    // partnerlink/operation.
    try {
      keySet = computeCorrelationKeys(mex);
    } catch (InvalidMessageException ime) {
      // We'd like to do a graceful exit here, no sense in rolling back due to a
      // a message format problem.
      __log.debug("Unable to evaluate correlation keys, invalid message format. ", ime);
      mex.setFailure(MessageExchange.FailureType.FORMAT_ERROR, ime.getMessage(), null);
      return null;
    }

    String mySessionId = mex.getProperty(MessageExchange.PROPERTY_SEP_MYROLE_SESSIONID);
    String partnerSessionId = mex.getProperty(MessageExchange.PROPERTY_SEP_PARTNERROLE_SESSIONID);
    if (__log.isDebugEnabled()) {
      __log.debug(
          "INPUTMSG: "
              + correlatorId
              + ": MSG RCVD keys="
              + keySet
              + " mySessionId="
              + mySessionId
              + " partnerSessionId="
              + partnerSessionId);
    }

    // Try to find a route for one of our keys.
    List<MessageRouteDAO> messageRoutes = correlator.findRoute(keySet);
    if (messageRoutes != null && messageRoutes.size() > 0) {
      for (MessageRouteDAO messageRoute : messageRoutes) {
        if (__log.isDebugEnabled()) {
          __log.debug(
              "INPUTMSG: "
                  + correlatorId
                  + ": ckeySet "
                  + messageRoute.getCorrelationKeySet()
                  + " route is to "
                  + messageRoute);
        }
        routingInfos.add(
            new RoutingInfo(messageRoute, messageRoute.getCorrelationKeySet(), correlator, keySet));
      }
    }

    if (routingInfos.size() == 0) {
      routingInfos.add(new RoutingInfo(null, null, correlator, keySet));
    }

    return routingInfos;
  }

  public static class RoutingInfo {
    public MessageRouteDAO messageRoute;
    public CorrelationKeySet matchedKeySet;
    public CorrelatorDAO correlator;
    //        CorrelationKey[] keys;
    public CorrelationKeySet wholeKeySet;

    public RoutingInfo(
        MessageRouteDAO messageRoute,
        CorrelationKeySet matchedKeySet,
        CorrelatorDAO correlator,
        CorrelationKeySet wholeKeySet) {
      this.messageRoute = messageRoute;
      this.matchedKeySet = matchedKeySet;
      this.correlator = correlator;
      this.wholeKeySet = wholeKeySet;
    }
  }

  public void invokeNewInstance(MyRoleMessageExchangeImpl mex, RoutingInfo routing) {
    Operation operation = getMyRoleOperation(mex.getOperationName());

    if (__log.isDebugEnabled()) {
      __log.debug(
          "INPUTMSG: "
              + routing.correlator.getCorrelatorId()
              + ": routing failed, CREATING NEW INSTANCE");
    }
    ProcessDAO processDAO = _process.getProcessDAO();

    if (_process._pconf.getState() == ProcessState.RETIRED) {
      throw new InvalidProcessException(
          "Process is retired.", InvalidProcessException.RETIRED_CAUSE_CODE);
    }

    if (!_process.processInterceptors(mex, InterceptorInvoker.__onNewInstanceInvoked)) {
      __log.debug("Not creating a new instance for mex " + mex + "; interceptor prevented!");
      throw new InvalidProcessException(
          "Cannot instantiate process '" + _process.getPID() + "' any more.",
          InvalidProcessException.TOO_MANY_INSTANCES_CAUSE_CODE);
    }

    ProcessInstanceDAO newInstance = processDAO.createInstance(routing.correlator);

    BpelRuntimeContextImpl instance =
        _process.createRuntimeContext(newInstance, new PROCESS(_process.getOProcess()), mex);

    // send process instance event
    NewProcessInstanceEvent evt =
        new NewProcessInstanceEvent(
            new QName(_process.getOProcess().targetNamespace, _process.getOProcess().getName()),
            _process.getProcessDAO().getProcessId(),
            newInstance.getInstanceId());
    evt.setPortType(mex.getPortType().getQName());
    evt.setOperation(operation.getName());
    evt.setMexId(mex.getMessageExchangeId());
    _process._debugger.onEvent(evt);
    _process.saveEvent(evt, newInstance);
    mex.setCorrelationStatus(MyRoleMessageExchange.CorrelationStatus.CREATE_INSTANCE);
    mex.getDAO().setInstance(newInstance);
    if (mex.getDAO().getCreateTime() == null)
      mex.getDAO().setCreateTime(instance.getCurrentEventDateTime());

    _process._engine.acquireInstanceLock(newInstance.getInstanceId());
    instance.execute();
  }

  public void invokeInstance(MyRoleMessageExchangeImpl mex, RoutingInfo routing) {
    Operation operation = getMyRoleOperation(mex.getOperationName());
    if (__log.isDebugEnabled()) {
      __log.debug(
          "INPUTMSG: "
              + routing.correlator.getCorrelatorId()
              + ": ROUTING to existing instance "
              + routing.messageRoute.getTargetInstance().getInstanceId());
    }

    ProcessInstanceDAO instanceDao = routing.messageRoute.getTargetInstance();
    BpelProcess process2 =
        _process._engine._activeProcesses.get(instanceDao.getProcess().getProcessId());

    // Reload process instance for DAO.
    BpelRuntimeContextImpl instance = process2.createRuntimeContext(instanceDao, null, null);
    instance.inputMsgMatch(routing.messageRoute.getGroupId(), routing.messageRoute.getIndex(), mex);

    // Kill the route so some new message does not get routed to
    // same process instance.
    routing.correlator.removeRoutes(routing.messageRoute.getGroupId(), instanceDao);

    // send process instance event
    CorrelationMatchEvent evt =
        new CorrelationMatchEvent(
            new QName(process2.getOProcess().targetNamespace, process2.getOProcess().getName()),
            process2.getProcessDAO().getProcessId(),
            instanceDao.getInstanceId(),
            routing.matchedKeySet);
    evt.setPortType(mex.getPortType().getQName());
    evt.setOperation(operation.getName());
    evt.setMexId(mex.getMessageExchangeId());

    process2._debugger.onEvent(evt);
    // store event
    process2.saveEvent(evt, instanceDao);

    mex.setCorrelationStatus(MyRoleMessageExchange.CorrelationStatus.MATCHED);
    mex.getDAO().setInstance(routing.messageRoute.getTargetInstance());
    if (mex.getDAO().getCreateTime() == null)
      mex.getDAO().setCreateTime(instance.getCurrentEventDateTime());

    instance.execute();
  }

  public void noRoutingMatch(MyRoleMessageExchangeImpl mex, List<RoutingInfo> routings) {
    if (!mex.isAsynchronous()) {
      mex.setFailure(
          MessageExchange.FailureType.NOMATCH,
          "No process instance matching correlation keys.",
          null);
    } else {
      // enqueue message with the last message route, as per the comments in caller (@see
      // BpelProcess.invokeProcess())
      RoutingInfo routing =
          (routings != null && routings.size() > 0) ? routings.get(routings.size() - 1) : null;
      if (routing != null) {
        if (__log.isDebugEnabled()) {
          __log.debug(
              "INPUTMSG: " + routing.correlator.getCorrelatorId() + ": SAVING to DB (no match) ");
        }

        // send event
        CorrelationNoMatchEvent evt =
            new CorrelationNoMatchEvent(
                mex.getPortType().getQName(),
                mex.getOperation().getName(),
                mex.getMessageExchangeId(),
                routing.wholeKeySet);

        evt.setProcessId(_process.getProcessDAO().getProcessId());
        evt.setProcessName(
            new QName(_process.getOProcess().targetNamespace, _process.getOProcess().getName()));
        _process._debugger.onEvent(evt);

        mex.setCorrelationStatus(MyRoleMessageExchange.CorrelationStatus.QUEUED);

        // No match, means we add message exchange to the queue.
        routing.correlator.enqueueMessage(mex.getDAO(), routing.wholeKeySet);
      }
    }
  }

  private void setMexRole(MyRoleMessageExchangeImpl mex) {
    Operation operation = getMyRoleOperation(mex.getOperationName());
    mex.getDAO().setPartnerLinkModelId(_plinkDef.getId());
    mex.setPortOp(_plinkDef.myRolePortType, operation);
    mex.setPattern(
        operation.getOutput() == null
            ? MessageExchange.MessageExchangePattern.REQUEST_ONLY
            : MessageExchange.MessageExchangePattern.REQUEST_RESPONSE);
  }

  private Operation getMyRoleOperation(String operationName) {
    return _plinkDef.getMyRoleOperation(operationName);
  }

  private CorrelationKeySet computeCorrelationKeys(MyRoleMessageExchangeImpl mex) {
    CorrelationKeySet keySet = new CorrelationKeySet();

    Operation operation = mex.getOperation();
    Element msg = mex.getRequest().getMessage();
    javax.wsdl.Message msgDescription = operation.getInput().getMessage();

    Set<OScope.CorrelationSet> csets =
        _plinkDef.getNonInitiatingCorrelationSetsForOperation(operation);
    for (OScope.CorrelationSet cset : csets) {
      CorrelationKey key =
          computeCorrelationKey(
              cset, _process.getOProcess().messageTypes.get(msgDescription.getQName()), msg);
      keySet.add(key);
    }

    csets = _plinkDef.getJoinningCorrelationSetsForOperation(operation);
    for (OScope.CorrelationSet cset : csets) {
      CorrelationKey key =
          computeCorrelationKey(
              cset, _process.getOProcess().messageTypes.get(msgDescription.getQName()), msg);
      keySet.add(key);
    }

    // Let's creata a key based on the sessionId
    String mySessionId = mex.getProperty(MessageExchange.PROPERTY_SEP_MYROLE_SESSIONID);
    if (mySessionId != null) keySet.add(new CorrelationKey("-1", new String[] {mySessionId}));

    return keySet;
  }

  @SuppressWarnings("unchecked")
  private CorrelationKey computeCorrelationKey(
      OScope.CorrelationSet cset, OMessageVarType messagetype, Element msg) {
    CorrelationKey key = null;

    String[] values = new String[cset.properties.size()];

    int jIdx = 0;
    for (Iterator j = cset.properties.iterator(); j.hasNext(); ++jIdx) {
      OProcess.OProperty property = (OProcess.OProperty) j.next();
      OProcess.OPropertyAlias alias = property.getAlias(messagetype);

      if (alias == null) {
        // TODO: Throw a real exception! And catch this at compile
        // time.
        throw new IllegalArgumentException(
            "No alias matching property '"
                + property.name
                + "' with message type '"
                + messagetype
                + "'");
      }

      String value;
      try {
        value = _process.extractProperty(msg, alias, msg.toString());
      } catch (FaultException fe) {
        String emsg =
            __msgs.msgPropertyAliasDerefFailedOnMessage(alias.getDescription(), fe.getMessage());
        __log.error(emsg, fe);
        throw new InvalidMessageException(emsg, fe);
      }
      values[jIdx] = value;
    }

    if (cset.hasJoinUseCases) {
      key = new OptionalCorrelationKey(cset.name, values);
    } else {
      key = new CorrelationKey(cset.name, values);
    }

    return key;
  }

  @SuppressWarnings("unchecked")
  public boolean isOneWayOnly() {
    PortType portType = _plinkDef.myRolePortType;
    if (portType == null) {
      return false;
    }
    for (Operation operation : (List<Operation>) portType.getOperations()) {
      if (operation.getOutput() != null) {
        return false;
      }
    }
    return true;
  }
}
Пример #3
0
/** @author mriou <mriou at apache dot org> */
public class JaxpFunctionResolver implements XPathFunctionResolver {

  private static final XPathMessages __msgs = MessageBundle.getMessages(XPathMessages.class);

  private CompilerContext _cctx;
  private OXPath20ExpressionBPEL20 _out;
  private NSContext _nsContext;
  private String _bpelNS;

  public JaxpFunctionResolver(
      CompilerContext cctx, OXPath20ExpressionBPEL20 out, NSContext nsContext, String bpelNS) {
    _cctx = cctx;
    _bpelNS = bpelNS;
    _nsContext = nsContext;
    _bpelNS = bpelNS;
    _out = out;
  }

  public XPathFunction resolveFunction(QName functionName, int arity) {
    if (functionName.getNamespaceURI() == null) {
      throw new WrappedResolverException("Undeclared namespace for " + functionName);
    } else if (functionName.getNamespaceURI().equals(_bpelNS)) {
      String localName = functionName.getLocalPart();
      if (Constants.EXT_FUNCTION_GETVARIABLEPROPERTY.equals(localName)) {
        return new GetVariableProperty();
      } else if (Constants.EXT_FUNCTION_DOXSLTRANSFORM.equals(localName)) {
        return new DoXslTransform();
      } else {
        throw new WrappedResolverException(__msgs.errUnknownBpelFunction(localName));
      }
    } else if (functionName.getNamespaceURI().equals(Namespaces.ODE_EXTENSION_NS)) {
      String localName = functionName.getLocalPart();
      if (Constants.NON_STDRD_FUNCTION_SPLIT_TO_ELEMENTS.equals(localName)
          || Constants.NON_STDRD_FUNCTION_DEPRECATED_SPLIT_TO_ELEMENTS.equals(localName)) {
        return new SplitToElements();
      } else if (Constants.NON_STDRD_FUNCTION_COMBINE_URL.equals(localName)
          || Constants.NON_STDRD_FUNCTION_DEPRECATED_COMBINE_URL.equals(localName)) {
        return new CombineUrl();
      } else if (Constants.NON_STDRD_FUNCTION_COMPOSE_URL.equals(localName)
          || Constants.NON_STDRD_FUNCTION_EXPAND_TEMPLATE.equals(localName)
          || Constants.NON_STDRD_FUNCTION_DEPRECATED_COMPOSE_URL.equals(localName)
          || Constants.NON_STDRD_FUNCTION_DEPRECATED_EXPAND_TEMPLATE.equals(localName)) {
        return new ComposeUrl();
      } else if (Constants.NON_STDRD_FUNCTION_DOM_TO_STRING.equals(localName)
          || Constants.NON_STDRD_FUNCTION_DEPRECATED_DOM_TO_STRING.equals(localName)) {
        return new DomToString();
      } else if (Constants.NON_STDRD_FUNCTION_INSERT_AFTER.equals(localName)) {
        return new InsertAfter();
      } else if (Constants.NON_STDRD_FUNCTION_INSERT_AS_FIRST_INTO.equals(localName)) {
        return new InsertAsFirstInto();
      } else if (Constants.NON_STDRD_FUNCTION_INSERT_AS_LAST_INTO.equals(localName)) {
        return new InsertAsLastInto();
      } else if (Constants.NON_STDRD_FUNCTION_INSERT_BEFORE.equals(localName)) {
        return new InsertBefore();
      } else if (Constants.NON_STDRD_FUNCTION_DELETE.equals(localName)) {
        return new Delete();
      } else if (Constants.NON_STDRD_FUNCTION_RENAME.equals(localName)) {
        return new Rename();
      } else if (Constants.NON_STDRD_FUNCTION_PROCESS_PROPERTY.equals(localName)) {
        return new ProcessProperty();
      }
    } else if (functionName.getNamespaceURI().equals(Namespaces.DEPRECATED_XDT_NS)) {
      String localName = functionName.getLocalPart();
      if (Constants.NON_STDRD_FUNCTION_DAY_TIME_DURATION.equals(localName)) {
        return new DayTimeDuration();
      } else if (Constants.NON_STDRD_FUNCTION_YEAR_MONTH_DURATION.equals(localName)) {
        return new YearMonthDuration();
      }
    }

    return null;
  }

  public class GetVariableProperty implements XPathFunction {
    public Object evaluate(List params) throws XPathFunctionException {
      if (params.size() != 2) {
        throw new CompilationException(
            __msgs.errInvalidNumberOfArguments(Constants.EXT_FUNCTION_GETVARIABLEPROPERTY));
      }
      String varName = (String) params.get(0);
      OScope.Variable v = _cctx.resolveVariable(varName);
      _out.getVars().put(varName, v);

      String propName = (String) params.get(1);
      QName qname = _nsContext.derefQName(propName);

      if (qname == null) throw new CompilationException(__msgs.errInvalidQName(propName));

      OProcess.OProperty property = _cctx.resolveProperty(qname);
      // Make sure we can...
      _cctx.resolvePropertyAlias(v, qname);

      _out.getProperties().put(propName, property);
      _out.getVars().put(varName, v);
      return "";
    }
  }

  public class DoXslTransform implements XPathFunction {
    public Object evaluate(List params) throws XPathFunctionException {
      if (params.size() < 2 || params.size() % 2 != 0) {
        throw new CompilationException(
            __msgs.errInvalidNumberOfArguments(Constants.EXT_FUNCTION_DOXSLTRANSFORM));
      }

      String xslUri = (String) params.get(0);
      OXslSheet xslSheet = _cctx.compileXslt(xslUri);
      try {
        XslTransformHandler.getInstance()
            .parseXSLSheet(
                _cctx.getOProcess().getQName(),
                xslSheet.getUri(),
                xslSheet.getSheetBody(),
                new XslCompileUriResolver(_cctx, _out));
      } catch (Exception e) {
        throw new CompilationException(__msgs.errXslCompilation(xslUri, e.toString()));
      }

      _out.setXslSheet(xslSheet.getUri(), xslSheet);
      return "";
    }
  }

  /** Compile time checking for the non standard ode:splitToElements function. */
  public static class SplitToElements implements XPathFunction {
    public Object evaluate(List params) throws XPathFunctionException {
      if (params.size() < 3 || params.size() > 4) {
        throw new CompilationException(
            __msgs.errInvalidNumberOfArguments(Constants.NON_STDRD_FUNCTION_SPLIT_TO_ELEMENTS));
      }
      return "";
    }
  }

  public static class CombineUrl implements XPathFunction {
    public Object evaluate(List args) throws XPathFunctionException {
      if (args.size() != 2) {
        throw new CompilationException(
            __msgs.errInvalidNumberOfArguments(Constants.NON_STDRD_FUNCTION_COMBINE_URL));
      }
      return "";
    }
  }

  public static class DomToString implements XPathFunction {
    public Object evaluate(List args) throws XPathFunctionException {
      if (args.size() != 1) {
        throw new CompilationException(
            __msgs.errInvalidNumberOfArguments(Constants.NON_STDRD_FUNCTION_DOM_TO_STRING));
      }
      return "";
    }
  }

  public static class ComposeUrl implements XPathFunction {
    public Object evaluate(List args) throws XPathFunctionException {
      boolean separareParameteters;
      if (args.size() != 2 && (args.size() <= 2 || args.size() % 2 != 1)) {
        throw new CompilationException(
            __msgs.errInvalidNumberOfArguments(Constants.NON_STDRD_FUNCTION_COMPOSE_URL));
      }
      return "";
    }
  }

  public static class InsertInto implements XPathFunction {
    public Object evaluate(List args) throws XPathFunctionException {
      if (args.size() != 3) {
        throw new CompilationException(
            __msgs.errInvalidNumberOfArguments(Constants.NON_STDRD_FUNCTION_INSERT_AFTER));
      }
      return "";
    }
  }

  public static class InsertAfter implements XPathFunction {
    public Object evaluate(List args) throws XPathFunctionException {
      if (args.size() < 2 || args.size() > 3) {
        throw new CompilationException(
            __msgs.errInvalidNumberOfArguments(Constants.NON_STDRD_FUNCTION_INSERT_AFTER));
      }
      return "";
    }
  }

  public static class InsertBefore implements XPathFunction {
    public Object evaluate(List args) throws XPathFunctionException {
      if (args.size() < 2 || args.size() > 3) {
        throw new CompilationException(
            __msgs.errInvalidNumberOfArguments(Constants.NON_STDRD_FUNCTION_INSERT_BEFORE));
      }
      return "";
    }
  }

  public static class InsertAsFirstInto implements XPathFunction {
    public Object evaluate(List args) throws XPathFunctionException {
      if (args.size() != 2) {
        throw new CompilationException(
            __msgs.errInvalidNumberOfArguments(Constants.NON_STDRD_FUNCTION_INSERT_AS_FIRST_INTO));
      }
      return "";
    }
  }

  public static class InsertAsLastInto implements XPathFunction {
    public Object evaluate(List args) throws XPathFunctionException {
      if (args.size() != 2) {
        throw new CompilationException(
            __msgs.errInvalidNumberOfArguments(Constants.NON_STDRD_FUNCTION_INSERT_AS_LAST_INTO));
      }
      return "";
    }
  }

  public static class Delete implements XPathFunction {
    public Object evaluate(List args) throws XPathFunctionException {
      if (args.size() < 1 || args.size() > 2) {
        throw new CompilationException(
            __msgs.errInvalidNumberOfArguments(Constants.NON_STDRD_FUNCTION_DELETE));
      }
      return "";
    }
  }

  public static class Rename implements XPathFunction {
    public Object evaluate(List args) throws XPathFunctionException {
      if (args.size() < 2) {
        throw new CompilationException(
            __msgs.errInvalidNumberOfArguments(Constants.NON_STDRD_FUNCTION_RENAME));
      }
      return "";
    }
  }

  public static class ProcessProperty implements XPathFunction {
    public Object evaluate(List args) throws XPathFunctionException {
      if (args.size() != 1) {
        throw new CompilationException(
            __msgs.errInvalidNumberOfArguments(Constants.NON_STDRD_FUNCTION_PROCESS_PROPERTY));
      }
      return "";
    }
  }

  public static class DayTimeDuration implements XPathFunction {
    public Object evaluate(List args) throws XPathFunctionException {
      if (args.size() != 1) {
        throw new CompilationException(
            __msgs.errInvalidNumberOfArguments(Constants.NON_STDRD_FUNCTION_DAY_TIME_DURATION));
      }
      return "";
    }
  }

  public static class YearMonthDuration implements XPathFunction {
    public Object evaluate(List args) throws XPathFunctionException {
      if (args.size() != 1) {
        throw new CompilationException(
            __msgs.errInvalidNumberOfArguments(Constants.NON_STDRD_FUNCTION_YEAR_MONTH_DURATION));
      }
      return "";
    }
  }
}
Пример #4
0
public class DbConfStoreConnectionFactory implements ConfStoreConnectionFactory {
  private static final Log __log = LogFactory.getLog(DbConfStoreConnectionFactory.class);

  private static final Messages __msgs = MessageBundle.getMessages(Messages.class);

  private static final String _guid = new GUID().toString();

  private static final Map<String, DataSource> _dataSources =
      new ConcurrentHashMap<String, DataSource>();

  private TransactionManager _txMgr;

  private final DataSource _ds;

  final SessionFactory _sessionFactory;

  public DbConfStoreConnectionFactory(
      DataSource ds, Properties initialProps, boolean createDatamodel, String txFactoryClassName) {
    _ds = ds;

    // Don't want to pollute original properties
    Properties properties = new Properties();
    for (Object prop : initialProps.keySet()) {
      properties.put(prop, initialProps.get(prop));
    }

    __log.debug("using data source: " + ds);
    _dataSources.put(_guid, ds);

    if (createDatamodel) {
      properties.put(Environment.HBM2DDL_AUTO, "create-drop");
    }

    // Note that we don't allow the following properties to be overriden by the client.
    if (properties.containsKey(Environment.CONNECTION_PROVIDER))
      __log.warn("Ignoring user-specified Hibernate property: " + Environment.CONNECTION_PROVIDER);
    if (properties.containsKey(Environment.TRANSACTION_MANAGER_STRATEGY))
      __log.warn(
          "Ignoring user-specified Hibernate property: "
              + Environment.TRANSACTION_MANAGER_STRATEGY);
    if (properties.containsKey(Environment.SESSION_FACTORY_NAME))
      __log.warn("Ignoring user-specified Hibernate property: " + Environment.SESSION_FACTORY_NAME);

    properties.put(SessionManager.PROP_GUID, _guid);
    properties.put(Environment.CONNECTION_PROVIDER, DataSourceConnectionProvider.class.getName());
    properties.put(
        Environment.TRANSACTION_MANAGER_STRATEGY,
        HibernateTransactionManagerLookup.class.getName());
    properties.put(
        Environment.TRANSACTION_STRATEGY, "org.hibernate.transaction.JTATransactionFactory");
    properties.put(Environment.CURRENT_SESSION_CONTEXT_CLASS, "jta");

    if (__log.isDebugEnabled()) __log.debug("Store connection properties: " + properties);

    initTxMgr(txFactoryClassName);
    SessionManager.registerTransactionManager(_guid, _txMgr);

    _sessionFactory = getDefaultConfiguration().setProperties(properties).buildSessionFactory();
  }

  public ConfStoreConnectionHib getConnection() {
    return new ConfStoreConnectionHib(_sessionFactory.getCurrentSession());
  }

  @SuppressWarnings("unchecked")
  private void initTxMgr(String txFactoryClassName) {
    __log.info("ProcessStore initializing transaction manager using " + txFactoryClassName);
    try {
      Class<?> txFactClass = getClass().getClassLoader().loadClass(txFactoryClassName);
      Object txFact = txFactClass.newInstance();
      _txMgr =
          (TransactionManager)
              txFactClass.getMethod("getTransactionManager", (Class[]) null).invoke(txFact);
    } catch (Exception e) {
      __log.fatal(
          "Couldn't initialize a transaction manager with factory: " + txFactoryClassName, e);
      throw new RuntimeException(
          "Couldn't initialize a transaction manager with factory: " + txFactoryClassName, e);
    }
  }

  public void beginTransaction() {
    try {
      _txMgr.begin();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  public void commitTransaction() {
    try {
      _txMgr.commit();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  public void rollbackTransaction() {
    try {
      _txMgr.rollback();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  static Configuration getDefaultConfiguration() throws MappingException {
    return new Configuration()
        .addClass(ProcessConfDaoImpl.class)
        .addClass(DeploymentUnitDaoImpl.class)
        .addClass(VersionTrackerDAOImpl.class);
  }

  public static class DataSourceConnectionProvider implements ConnectionProvider {
    private String _guid;

    public DataSourceConnectionProvider() {}

    public void configure(Properties props) throws HibernateException {
      _guid = props.getProperty(SessionManager.PROP_GUID);
    }

    public Connection getConnection() throws SQLException {
      return _dataSources.get(_guid).getConnection();
    }

    public void closeConnection(Connection arg0) throws SQLException {
      arg0.close();
    }

    public void close() throws HibernateException {}

    public boolean supportsAggressiveRelease() {
      return true;
    }
  }
}
Пример #5
0
/** Generates code for <code>&lt;reply&gt;</code> activities. */
class ReplyGenerator extends DefaultActivityGenerator {

  private static final CommonCompilationMessages _cmsgsGeneral =
      MessageBundle.getMessages(CommonCompilationMessages.class);

  private static final ReplyGeneratorMessages __cmsgsLocal =
      MessageBundle.getMessages(ReplyGeneratorMessages.class);

  public OActivity newInstance(Activity src) {
    return new OReply(_context.getOProcess(), _context.getCurrent());
  }

  public void compile(OActivity output, Activity src) {
    ReplyActivity replyDef = (ReplyActivity) src;
    OReply oreply = (OReply) output;

    oreply.isFaultReply = replyDef.getFaultName() != null;
    oreply.partnerLink = _context.resolvePartnerLink(replyDef.getPartnerLink());
    oreply.messageExchangeId = replyDef.getMessageExchangeId();
    if (replyDef.getVariable() != null) {
      oreply.variable = _context.resolveVariable(replyDef.getVariable());
      if (!(oreply.variable.type instanceof OMessageVarType))
        throw new CompilationException(
            _cmsgsGeneral.errMessageVariableRequired(oreply.variable.name));
    }

    if (oreply.partnerLink.myRolePortType == null)
      throw new CompilationException(
          _cmsgsGeneral.errPartnerLinkDoesNotDeclareMyRole(oreply.partnerLink.getName()));
    // The portType on the reply is not necessary, so we check its validty only when present.
    if (replyDef.getPortType() != null
        && !oreply.partnerLink.myRolePortType.getQName().equals(replyDef.getPortType()))
      throw new CompilationException(
          _cmsgsGeneral.errPortTypeMismatch(
              replyDef.getPortType(), oreply.partnerLink.myRolePortType.getQName()));

    oreply.operation = _context.resolveMyRoleOperation(oreply.partnerLink, replyDef.getOperation());
    if (oreply.operation.getOutput() == null)
      throw new CompilationException(
          _cmsgsGeneral.errTwoWayOperationExpected(oreply.operation.getName()));

    if (oreply.isFaultReply) {
      Fault flt = null;
      if (replyDef
          .getFaultName()
          .getNamespaceURI()
          .equals(oreply.partnerLink.myRolePortType.getQName().getNamespaceURI()))
        flt = oreply.operation.getFault(replyDef.getFaultName().getLocalPart());
      if (flt == null)
        throw new CompilationException(
            __cmsgsLocal.errUndeclaredFault(
                replyDef.getFaultName().getLocalPart(), oreply.operation.getName()));
      if (oreply.variable != null
          && !((OMessageVarType) oreply.variable.type)
              .messageType.equals(flt.getMessage().getQName()))
        throw new CompilationException(
            _cmsgsGeneral.errVariableTypeMismatch(
                oreply.variable.name,
                flt.getMessage().getQName(),
                ((OMessageVarType) oreply.variable.type).messageType));
      oreply.fault = replyDef.getFaultName();
    } else /* !oreply.isFaultReply */ {
      assert oreply.fault == null;
      if (oreply.variable == null)
        throw new CompilationException(__cmsgsLocal.errOutputVariableMustBeSpecified());
      if (!((OMessageVarType) oreply.variable.type)
          .messageType.equals(oreply.operation.getOutput().getMessage().getQName()))
        throw new CompilationException(
            _cmsgsGeneral.errVariableTypeMismatch(
                oreply.variable.name,
                oreply.operation.getOutput().getMessage().getQName(),
                ((OMessageVarType) oreply.variable.type).messageType));
    }

    for (Correlation correlation : replyDef.getCorrelations()) {
      OScope.CorrelationSet cset = _context.resolveCorrelationSet(correlation.getCorrelationSet());

      switch (correlation.getInitiate()) {
        case UNSET:
        case NO:
          oreply.assertCorrelations.add(cset);
          break;
        case YES:
          oreply.initCorrelations.add(cset);
          break;
        default:
          // TODO: Make error for this.
          throw new AssertionError();
      }

      for (Iterator<OProcess.OProperty> j = cset.properties.iterator(); j.hasNext(); ) {
        OProcess.OProperty property = j.next();
        // Force resolution of alias, to make sure that we have one for this variable-property pair.
        _context.resolvePropertyAlias(oreply.variable, property.name);
      }
    }
  }
}
Пример #6
0
/**
 * The BPEL server implementation.
 *
 * <p>This implementation is intended to be thread safe. The key concurrency mechanism is a
 * "management" read/write lock that synchronizes all management operations (they require "write"
 * access) and prevents concurrent management operations and processing (processing requires "read"
 * access). Write access to the lock is scoped to the method, while read access is scoped to a
 * transaction.
 *
 * @author Maciej Szefler <mszefler at gmail dot com>
 * @author Matthieu Riou <mriou at apache dot org>
 */
public class BpelServerImpl implements BpelServer, Scheduler.JobProcessor {
  private static final Log __log = LogFactory.getLog(BpelServerImpl.class);

  private static final Messages __msgs = MessageBundle.getMessages(Messages.class);

  /** Maximum age of a process before it is quiesced */
  private static Long __processMaxAge;

  public static final String DEFERRED_PROCESS_INSTANCE_CLEANUP_DISABLED_NAME =
      "org.apache.ode.disable.deferredProcessInstanceCleanup";

  private static boolean DEFERRED_PROCESS_INSTANCE_CLEANUP_DISABLED =
      Boolean.getBoolean(DEFERRED_PROCESS_INSTANCE_CLEANUP_DISABLED_NAME);

  /**
   * Set of processes that are registered with the server. Includes hydrated and dehydrated
   * processes. Guarded by _mngmtLock.writeLock().
   */
  private final Set<BpelProcess> _registeredProcesses = new HashSet<BpelProcess>();

  private State _state = State.SHUTDOWN;
  private final Contexts _contexts = new Contexts();
  private Properties _configProperties;
  private DehydrationPolicy _dehydrationPolicy;
  private boolean _hydrationLazy;
  private int _hydrationLazyMinimumSize;
  private int _migrationTransactionTimeout;
  private Thread processDefReaper;

  BpelEngineImpl _engine;
  protected BpelDatabase _db;

  /**
   * Management lock for synchronizing management operations and preventing processing
   * (transactions) from occuring while management operations are in progress.
   */
  private ReadWriteLock _mngmtLock = new ReentrantReadWriteLock();

  static {
    // TODO Clean this up and factorize engine configuration
    try {
      String processMaxAge = System.getProperty("ode.process.maxage");
      if (processMaxAge != null && processMaxAge.length() > 0) {
        __processMaxAge = Long.valueOf(processMaxAge);
        __log.info("Process definition max age adjusted. Max age = " + __processMaxAge + "ms.");
      }
    } catch (Throwable t) {
      if (__log.isDebugEnabled()) {
        __log.debug("Could not parse ode.process.maxage environment variable.", t);
      } else {
        __log.info("Could not parse ode.process.maxage environment variable; reaping disabled.");
      }
    }
  }

  private enum State {
    SHUTDOWN,
    INIT,
    RUNNING
  }

  public BpelServerImpl() {}

  public Contexts getContexts() {
    return _contexts;
  }

  public void start() {
    _mngmtLock.writeLock().lock();
    try {
      if (!checkState(State.INIT, State.RUNNING)) {
        __log.debug("start() ignored -- already started");
        return;
      }
      __log.debug("BPEL SERVER starting.");

      // Eventually running some migrations before starting
      if (!(new MigrationHandler(_contexts)
          .migrate(_registeredProcesses, _migrationTransactionTimeout))) {
        throw new BpelEngineException(
            "An error occurred while migrating your database to a newer version of the server. Please make sure that the required migration scripts have been executed before starting the server.");
      }

      _state = State.RUNNING;
      __log.info(__msgs.msgServerStarted());
      if (_dehydrationPolicy != null) {
        processDefReaper = new Thread(new ProcessDefReaper(), "Dehydrator");
        processDefReaper.setDaemon(true);
        processDefReaper.start();
      }
    } finally {
      _mngmtLock.writeLock().unlock();
    }
  }

  public void registerExternalVariableEngine(ExternalVariableModule eve) {
    _contexts.externalVariableEngines.put(eve.getName(), eve);
  }

  /**
   * Register a global listener to receive {@link BpelEvent}s froom all processes.
   *
   * @param listener
   */
  public void registerBpelEventListener(BpelEventListener listener) {
    // Do not synchronize, eventListeners is copy-on-write array.
    listener.startup(_configProperties);
    _contexts.eventListeners.add(listener);
  }

  /**
   * Unregister a global listener from receive {@link BpelEvent}s from all processes.
   *
   * @param listener
   */
  public void unregisterBpelEventListener(BpelEventListener listener) {
    // Do not synchronize, eventListeners is copy-on-write array.
    try {
      listener.shutdown();
    } catch (Exception e) {
      __log.warn(
          "Stopping BPEL event listener "
              + listener.getClass().getName()
              + " failed, nevertheless it has been unregistered.",
          e);
    } finally {
      _contexts.eventListeners.remove(listener);
    }
  }

  private void unregisterBpelEventListeners() {
    for (BpelEventListener l : _contexts.eventListeners) {
      unregisterBpelEventListener(l);
    }
  }

  public void stop() {
    _mngmtLock.writeLock().lock();
    try {
      if (!checkState(State.RUNNING, State.INIT)) {
        __log.debug("stop() ignored -- already stopped");
        return;
      }

      __log.debug("BPEL SERVER STOPPING");

      if (processDefReaper != null) {
        processDefReaper.interrupt();
        processDefReaper = null;
      }
      _contexts.scheduler.stop();
      _engine = null;
      _state = State.INIT;
      __log.info(__msgs.msgServerStopped());
    } finally {
      _mngmtLock.writeLock().unlock();
    }
  }

  public void init() throws BpelEngineException {
    _mngmtLock.writeLock().lock();
    try {
      if (!checkState(State.SHUTDOWN, State.INIT)) return;

      __log.debug("BPEL SERVER initializing ");

      _db = new BpelDatabase(_contexts.dao, _contexts.scheduler);
      _state = State.INIT;

      _engine = createBpelEngineImpl(_contexts);
    } finally {
      _mngmtLock.writeLock().unlock();
    }
  }

  // enable extensibility
  protected BpelEngineImpl createBpelEngineImpl(Contexts contexts) {
    return new BpelEngineImpl(contexts);
  }

  public void shutdown() throws BpelEngineException {
    _mngmtLock.writeLock().lock();
    try {
      stop();
      unregisterBpelEventListeners();

      _db = null;
      _engine = null;
      _state = State.SHUTDOWN;
    } finally {
      _mngmtLock.writeLock().unlock();
    }
  }

  public BpelEngine getEngine() {
    boolean registered = false;
    _mngmtLock.readLock().lock();
    try {
      _contexts.scheduler.registerSynchronizer(
          new Synchronizer() {
            public void afterCompletion(boolean success) {
              _mngmtLock.readLock().unlock();
            }

            public void beforeCompletion() {}
          });
      registered = true;
    } finally {
      // If we failed to register the synchro,then there was an ex/throwable; we need to unlock now.
      if (!registered) _mngmtLock.readLock().unlock();
    }
    return _engine;
  }

  /**
   * This is the unsecured version of above method getEngine. Please use with care. This is a hack
   * to make BPS instance management work. TODO: Need to refactor this code in Apache ODE to make it
   * more flexible to manage process engine.
   *
   * @return
   */
  public BpelEngine getEngineUnsecured() {
    return _engine;
  }

  public void register(ProcessConf conf) {
    if (conf == null)
      throw new NullPointerException("must specify non-null process configuration.");

    __log.debug("register: " + conf.getProcessId());

    // Ok, IO out of the way, we will mod the server state, so need to get a
    // lock.
    try {
      _mngmtLock.writeLock().lockInterruptibly();
    } catch (InterruptedException ie) {
      __log.debug("register(...) interrupted.", ie);
      throw new BpelEngineException(__msgs.msgOperationInterrupted());
    }

    try {
      // If the process is already active, do nothing.
      if (_engine.isProcessRegistered(conf.getProcessId())) {
        __log.debug(
            "skipping doRegister" + conf.getProcessId() + ") -- process is already registered");
        return;
      }

      __log.debug("Registering process " + conf.getProcessId() + " with server.");

      BpelProcess process = createBpelProcess(conf);
      process._classLoader = Thread.currentThread().getContextClassLoader();

      _engine.registerProcess(process);
      _registeredProcesses.add(process);
      if (!isLazyHydratable(process)) {
        process.hydrate();
      } else {
        _engine.setProcessSize(process.getPID(), false);
      }

      __log.info(__msgs.msgProcessRegistered(conf.getProcessId()));

    } catch (Exception ex) {
      __log.error(ex);
      throw new BpelEngineException(ex);
    } finally {
      _mngmtLock.writeLock().unlock();
    }
  }

  private boolean isLazyHydratable(BpelProcess process) {
    if (process.isHydrationLazySet()) {
      return process.isHydrationLazy();
    }
    if (!_hydrationLazy) {
      return false;
    }
    return process.getEstimatedHydratedSize() < _hydrationLazyMinimumSize;
  }

  // enable extensibility
  protected BpelProcess createBpelProcess(ProcessConf conf) {
    return new BpelProcess(conf);
  }

  public void unregister(QName pid) throws BpelEngineException {
    if (__log.isTraceEnabled()) __log.trace("unregister: " + pid);

    try {
      _mngmtLock.writeLock().lockInterruptibly();
    } catch (InterruptedException ie) {
      __log.debug("unregister() interrupted.", ie);
      throw new BpelEngineException(__msgs.msgOperationInterrupted());
    }

    try {
      BpelProcess p;
      if (_engine != null) {
        p = _engine.unregisterProcess(pid);
        if (p != null) {
          _registeredProcesses.remove(p);
          XslTransformHandler.getInstance().clearXSLSheets(p.getProcessType());
          __log.info(__msgs.msgProcessUnregistered(pid));
        }
      }
    } catch (Exception ex) {
      __log.error(__msgs.msgProcessUnregisterFailed(pid), ex);
      throw new BpelEngineException(ex);
    } finally {
      _mngmtLock.writeLock().unlock();
    }
  }

  /**
   * Register a global message exchange interceptor.
   *
   * @param interceptor message-exchange interceptor
   */
  public void registerMessageExchangeInterceptor(MessageExchangeInterceptor interceptor) {
    // NOTE: do not synchronize, globalInterceptors is copy-on-write.
    _contexts.globalInterceptors.add(interceptor);
  }

  /**
   * Unregister a global message exchange interceptor.
   *
   * @param interceptor message-exchange interceptor
   */
  public void unregisterMessageExchangeInterceptor(MessageExchangeInterceptor interceptor) {
    // NOTE: do not synchronize, globalInterceptors is copy-on-write.
    _contexts.globalInterceptors.remove(interceptor);
  }

  /** Check a state transition from state "i" to state "j". */
  private boolean checkState(State i, State j) {
    if (_state == i) return true;
    if (_state == j) return false;
    return false;
  }

  protected boolean deleteProcessDAO(final QName pid, boolean isInMemory) {
    try {
      if (isInMemory) {
        return deleteProcessDAO(_contexts.inMemDao.getConnection(), pid);
      } else {
        return _db.exec(
            new BpelDatabase.Callable<Boolean>() {
              public Boolean run(BpelDAOConnection conn) throws Exception {
                return deleteProcessDAO(conn, pid);
              }
            });
      }
    } catch (BpelEngineException re) {
      throw re;
    } catch (Exception e) {
      throw new BpelEngineException(e);
    }
  }

  private boolean deleteProcessDAO(BpelDAOConnection conn, QName pid) {
    final ProcessDAO proc = conn.getProcess(pid);
    if (proc != null) {
      // delete routes
      if (__log.isDebugEnabled()) __log.debug("Deleting only the process " + pid + "...");
      proc.deleteProcessAndRoutes();
      if (__log.isInfoEnabled()) __log.info("Deleted only the process " + pid + ".");
      // we do deferred instance cleanup only for hibernate, for now
      if (proc instanceof DeferredProcessInstanceCleanable
          && !DEFERRED_PROCESS_INSTANCE_CLEANUP_DISABLED) {
        // schedule deletion of process runtime data
        _engine._contexts.scheduler.scheduleMapSerializableRunnable(
            new ProcessCleanUpRunnable(((DeferredProcessInstanceCleanable) proc).getPidId()),
            new Date());
      } else if (proc instanceof DeferredProcessInstanceCleanable) {
        ((DeferredProcessInstanceCleanable) proc).deleteInstances(Integer.MAX_VALUE);
      }
      return true;
    }
    return false;
  }

  public void onScheduledJob(JobInfo jobInfo) throws JobProcessorException {
    getEngine().onScheduledJob(jobInfo);
  }

  private class ProcessDefReaper implements Runnable {
    public void run() {
      __log.debug("Starting process definition reaper thread.");
      long pollingTime = 10000;
      try {
        while (true) {
          Thread.sleep(pollingTime);
          if (!_mngmtLock.writeLock().tryLock(100L, TimeUnit.MILLISECONDS)) continue;
          try {
            __log.debug("Kicking reaper, OProcess instances: " + OProcess.instanceCount);
            // Copying the runnning process list to avoid synchronization
            // problems and a potential mess if a policy modifies the list
            List<BpelProcess> candidates = new ArrayList<BpelProcess>(_registeredProcesses);
            CollectionsX.remove_if(
                candidates,
                new MemberOfFunction<BpelProcess>() {
                  public boolean isMember(BpelProcess o) {
                    return !o.hintIsHydrated();
                  }
                });

            // And the happy winners are...
            List<BpelProcess> ripped = _dehydrationPolicy.markForDehydration(candidates);
            // Bye bye
            for (BpelProcess process : ripped) {
              __log.debug("Dehydrating process " + process.getPID());
              process.dehydrate();
            }
          } finally {
            _mngmtLock.writeLock().unlock();
          }
        }
      } catch (InterruptedException e) {
        __log.debug(e);
      }
    }
  }

  public void setDehydrationPolicy(DehydrationPolicy dehydrationPolicy) {
    _dehydrationPolicy = dehydrationPolicy;
  }

  public void setConfigProperties(Properties configProperties) {
    _configProperties = configProperties;
  }

  public void setMessageExchangeContext(MessageExchangeContext mexContext)
      throws BpelEngineException {
    _contexts.mexContext = mexContext;
  }

  public void setScheduler(Scheduler scheduler) throws BpelEngineException {
    _contexts.scheduler = scheduler;
  }

  public void setCronScheduler(CronScheduler cronScheduler) throws BpelEngineException {
    _contexts.cronScheduler = cronScheduler;
  }

  public void setEndpointReferenceContext(EndpointReferenceContext eprContext)
      throws BpelEngineException {
    _contexts.eprContext = eprContext;
  }

  /**
   * Set the DAO connection factory. The DAO is used by the BPEL engine to persist information about
   * active processes.
   *
   * @param daoCF {@link BpelDAOConnectionFactory} implementation.
   */
  public void setDaoConnectionFactory(BpelDAOConnectionFactory daoCF) throws BpelEngineException {
    JdbcDelegate.setGlobalDAOConnFac(daoCF);
    _contexts.dao = daoCF;
  }

  public void setInMemDaoConnectionFactory(BpelDAOConnectionFactory daoCF) {
    _contexts.inMemDao = daoCF;
  }

  public void setBindingContext(BindingContext bc) {
    _contexts.bindingContext = bc;
  }

  public DebuggerContext getDebugger(QName pid) throws BpelEngineException {
    return _engine._activeProcesses.get(pid)._debugger;
  }

  public boolean hasActiveInstances(final QName pid) {
    try {
      return _db.exec(
          new BpelDatabase.Callable<Boolean>() {
            public Boolean run(BpelDAOConnection conn) throws Exception {
              return conn.getNumInstances(pid) > 0;
            }
          });
    } catch (BpelEngineException re) {
      throw re;
    } catch (Exception e) {
      throw new BpelEngineException(e);
    }
  }

  public void setHydrationLazy(boolean hydrationLazy) {
    this._hydrationLazy = hydrationLazy;
  }

  public void setProcessThrottledMaximumSize(long hydrationThrottledMaximumSize) {
    _engine.setProcessThrottledMaximumSize(hydrationThrottledMaximumSize);
  }

  public void setProcessThrottledMaximumCount(int hydrationThrottledMaximumCount) {
    _engine.setProcessThrottledMaximumCount(hydrationThrottledMaximumCount);
  }

  public void setHydrationLazyMinimumSize(int hydrationLazyMinimumSize) {
    this._hydrationLazyMinimumSize = hydrationLazyMinimumSize;
  }

  public void setInstanceThrottledMaximumCount(int instanceThrottledMaximumCount) {
    _engine.setInstanceThrottledMaximumCount(instanceThrottledMaximumCount);
  }

  public BpelDatabase getBpelDb() {
    return _db;
  }

  /**
   * A polled runnable instance that implements this interface will be set with the contexts before
   * the run() method is called.
   *
   * @author sean
   */
  public interface ContextsAware {
    void setContexts(Contexts contexts);
  }

  /**
   * This wraps up the executor service for polled runnables.
   *
   * @author sean
   */
  public static class PolledRunnableProcessor implements Scheduler.JobProcessor {
    private ExecutorService _polledRunnableExec;
    private Contexts _contexts;

    // this map contains all polled runnable results that are not completed.
    // keep an eye on this one, since if we re-use this polled runnable and
    // generate too many entries in this map, this becomes a memory leak(
    // long-running memory occupation)
    private final Map<String, PolledRunnableResults> resultsByJobId =
        new HashMap<String, PolledRunnableResults>();

    public void setContexts(Contexts contexts) {
      _contexts = contexts;
    }

    public void setPolledRunnableExecutorService(ExecutorService polledRunnableExecutorService) {
      _polledRunnableExec = polledRunnableExecutorService;
    }

    public void onScheduledJob(final Scheduler.JobInfo jobInfo)
        throws Scheduler.JobProcessorException {
      JOB_STATUS statusOfPriorTry = JOB_STATUS.PENDING;
      Exception exceptionThrownOnPriorTry = null;
      boolean toRetry = false;

      synchronized (resultsByJobId) {
        PolledRunnableResults results = resultsByJobId.get(jobInfo.jobName);
        if (results != null) {
          statusOfPriorTry = results._status;
          exceptionThrownOnPriorTry = results._exception;
        }
        if (statusOfPriorTry == JOB_STATUS.COMPLETED) {
          resultsByJobId.remove(jobInfo.jobName);
          jobInfo.jobDetail.getDetailsExt().put("runnable_status", JOB_STATUS.COMPLETED);
          return;
        }
        if (statusOfPriorTry == JOB_STATUS.PENDING || statusOfPriorTry == JOB_STATUS.FAILED) {
          resultsByJobId.put(
              jobInfo.jobName, new PolledRunnableResults(JOB_STATUS.IN_PROGRESS, null));
          toRetry = true;
        }
      }

      if (toRetry) {
        // re-try
        _polledRunnableExec.submit(
            new Runnable() {
              public void run() {
                try {
                  MapSerializableRunnable runnable =
                      (MapSerializableRunnable) jobInfo.jobDetail.getDetailsExt().get("runnable");
                  runnable.restoreFromDetails(jobInfo.jobDetail);
                  if (runnable instanceof ContextsAware) {
                    ((ContextsAware) runnable).setContexts(_contexts);
                  }
                  runnable.run();
                  synchronized (resultsByJobId) {
                    resultsByJobId.put(
                        jobInfo.jobName, new PolledRunnableResults(JOB_STATUS.COMPLETED, null));
                  }
                } catch (Exception e) {
                  __log.error("", e);
                  synchronized (resultsByJobId) {
                    resultsByJobId.put(
                        jobInfo.jobName, new PolledRunnableResults(JOB_STATUS.FAILED, e));
                  }
                } finally {
                }
              }
            });
      }

      jobInfo.jobDetail.getDetailsExt().put("runnable_status", JOB_STATUS.IN_PROGRESS);
      if (exceptionThrownOnPriorTry != null) {
        throw new Scheduler.JobProcessorException(exceptionThrownOnPriorTry, true);
      }
    }

    private static enum JOB_STATUS {
      PENDING,
      IN_PROGRESS,
      FAILED,
      COMPLETED
    }

    private class PolledRunnableResults {
      private JOB_STATUS _status = JOB_STATUS.PENDING;
      private Exception _exception;

      public PolledRunnableResults(JOB_STATUS status, Exception exception) {
        _status = status;
        _exception = exception;
      }
    }
  }

  public void cleanupProcess(ProcessConf pconf) throws BpelEngineException {
    if (pconf != null) {
      deleteProcessDAO(pconf.getProcessId(), pconf.isTransient());
    }
  }

  public void setMigrationTransactionTimeout(int migrationTransactionTimeout) {
    this._migrationTransactionTimeout = migrationTransactionTimeout;
  }

  public void acquireTransactionLocks() {
    if (Boolean.parseBoolean(
        _configProperties.getProperty("ode.acquireTransactionLocks", "false"))) {
      _contexts.scheduler.acquireTransactionLocks();
    }
  }

  public void registerExtensionBundle(ExtensionBundleRuntime bundle) {
    _contexts.extensionRegistry.put(bundle.getNamespaceURI(), bundle);
    bundle.registerExtensionActivities();
  }

  public void unregisterExtensionBundle(String nsURI) {
    _contexts.extensionRegistry.remove(nsURI);
  }

  public void registerExtensionCorrelationFilter(ExtensionCorrelationFilter filter) {
    _contexts.correlationFilterRegistry.put(filter.getNamespaceURI(), filter);
    filter.registerExtensionCorrelationFilter();
  }

  public void unregisterExtensionCorrelationFilter(String nsURI) {
    _contexts.correlationFilterRegistry.remove(nsURI);
  }
}
Пример #7
0
/**
 * Implementation of {@link OdeInternalInstance} for the "modern" runtime. This class also serves as a repository for kitchen sink type
 * methods that the activities all use. A lot of these methods are simply deferals to similar methods on
 * {@link OdeRTInstanceContext}; however here these methods use representation-specific classes (e.g.
 * {@link OPartnerLink) while the {@link OdeRTInstanceContext} methods use only the general (non-representation specific) interfaces
 * (e.g. {@link PartnerLink}.
 *
 * @author Maciej Szefler
 *
 */
public class RuntimeInstanceImpl implements OdeInternalInstance, OdeRTInstance {
  private static final Log __log = LogFactory.getLog(RuntimeInstanceImpl.class);

  private static final Messages __msgs = MessageBundle.getMessages(Messages.class);

  private OdeRTInstanceContext _brc;

  /** JACOB VPU */
  protected JacobVPU _vpu;

  /** JACOB ExecutionQueue (state) */
  protected ExecutionQueueImpl _soup;

  private RuntimeImpl _runtime;

  public RuntimeInstanceImpl(RuntimeImpl runtime, ExecutionQueueImpl soup) {
    _runtime = runtime;
    _vpu = new JacobVPU();
    _vpu.registerExtension(OdeRTInstanceContext.class, this);
    if (soup == null) {
      _soup = new ExecutionQueueImpl(getClass().getClassLoader());
      _soup.setGlobalData(new OutstandingRequestManager());
    } else {
      _soup = soup;
    }

    _soup.setReplacementMap(_runtime._replacementMap);
    _vpu.setContext(_soup);
  }

  public ProcessModel getProcessModel() {
    return _runtime._oprocess;
  }

  public boolean isCorrelationInitialized(CorrelationSetInstance correlationSet) {
    return _brc.isCorrelationInitialized(correlationSet);
  }

  public boolean isVariableInitialized(VariableInstance var) {
    return _brc.isVariableInitialized(var);
  }

  public boolean isPartnerRoleEndpointInitialized(PartnerLinkInstance pLink) {
    return _brc.isPartnerRoleEndpointInitialized(pLink);
  }

  public void completedFault(FaultData faultData) {
    cleanupOutstandingMyRoleExchanges(faultData);
    _brc.completedFault(faultData);
  }

  public void completedOk() {
    cleanupOutstandingMyRoleExchanges(null);
    _brc.completedOk();
  }

  public Long createScopeInstance(Long parentScopeId, String name, int modelId) {
    return _brc.createScopeInstance(parentScopeId, name, modelId);
  }

  public void initializePartnerLinks(Long parentScopeId, Collection<OPartnerLink> partnerLinks) {
    _brc.initializePartnerLinks(parentScopeId, partnerLinks);
  }

  public void cancelOutstandingRequests(String channelId) {
    getORM().cancel(channelId);
  }

  public void select(
      PickResponseChannel pickResponseChannel,
      Date timeout,
      boolean createInstance,
      Selector[] selectors)
      throws FaultException {

    final String pickResponseChannelStr = pickResponseChannel.export();

    int conflict = getORM().findConflict(selectors);
    if (conflict != -1)
      throw new FaultException(
          _runtime._oprocess.constants.qnConflictingReceive, selectors[conflict].toString());

    getORM().register(pickResponseChannelStr, selectors);

    _brc.select(pickResponseChannelStr, timeout, selectors);
  }

  public CorrelationKey readCorrelation(CorrelationSetInstance cset) {
    return _brc.readCorrelation(cset);
  }

  public Node fetchVariableData(
      VariableInstance variable, ScopeFrame scopeFrame, boolean forWriting) throws FaultException {
    if (variable.declaration.extVar != null) {
      // Note, that when using external variables, the database will not contain the value of the
      // variable, instead we need to go the external variable subsystems.
      Element reference =
          (Element)
              _brc.fetchVariableData(
                  scopeFrame.resolve(variable.declaration.extVar.related), false);
      try {
        Node ret = _brc.readExtVar(variable, reference);
        if (ret == null) {
          throw new FaultException(
              _runtime._oprocess.constants.qnUninitializedVariable,
              "The external variable \""
                  + variable.declaration.name
                  + "\" has not been initialized.");
        }
        return ret;
      } catch (IncompleteKeyException ike) {
        // This indicates that the external variable needed to be written do, put has not been.
        __log.error(
            "External variable could not be read due to incomplete key; the following key "
                + "components were missing: "
                + ike.getMissing());
        throw new FaultException(
            _runtime._oprocess.constants.qnUninitializedVariable,
            "The extenral variable \""
                + variable.declaration.name
                + "\" has not been properly initialized;"
                + "the following key compoenents were missing:"
                + ike.getMissing());
      } catch (ExternalVariableModuleException e) {
        throw new BpelEngineException(e);
      }
    } else /* not external */ {
      Node data = _brc.fetchVariableData(variable, forWriting);
      if (data == null) {
        // Special case of messageType variables with no part
        if (variable.declaration.type instanceof OMessageVarType) {
          OMessageVarType msgType = (OMessageVarType) variable.declaration.type;
          if (msgType.parts.size() == 0) {
            Document doc = DOMUtils.newDocument();
            Element root = doc.createElement("message");
            doc.appendChild(root);
            return root;
          }
        }
        throw new FaultException(
            _runtime._oprocess.constants.qnUninitializedVariable,
            "The variable " + variable.declaration.name + " isn't properly initialized.");
      }
      return data;
    }
  }

  public Node fetchVariableData(
      VariableInstance var, ScopeFrame scopeFrame, OMessageVarType.Part part, boolean forWriting)
      throws FaultException {
    Node val = fetchVariableData(var, scopeFrame, forWriting);
    if (part != null) return getPartData((Element) val, part);
    return val;
  }

  public void writeCorrelation(CorrelationSetInstance cset, CorrelationKey ckeyVal)
      throws FaultException {
    OScope.CorrelationSet csetdef = cset.declaration;
    QName[] propNames = new QName[csetdef.properties.size()];
    for (int m = 0; m < csetdef.properties.size(); m++) {
      OProcess.OProperty oProperty = csetdef.properties.get(m);
      propNames[m] = oProperty.name;
    }

    ckeyVal.setUnique(cset.declaration.isUnique());
    _brc.writeCorrelation(cset, propNames, ckeyVal);
  }

  /**
   * Proxy to {@link OdeRTInstanceContext#sendEvent(org.apache.ode.bpel.evt.ProcessInstanceEvent)}.
   *
   * @param event
   */
  public void sendEvent(ScopeEvent event) {
    _brc.sendEvent(event);
  }

  public void unregisterActivityForRecovery(ActivityRecoveryChannel recoveryChannel) {
    _brc.unregisterActivityForRecovery(recoveryChannel.export());
  }

  /**
   * Proxy to {@link RecoveryContext#registerActivityForRecovery(String, long, String, Date,
   * Element, String[], int)}.
   */
  public void registerActivityForRecovery(
      ActivityRecoveryChannel recoveryChannel,
      long id,
      String reason,
      Date dateTime,
      Element details,
      String[] actions,
      int retryCount) {
    _brc.registerActivityForRecovery(
        recoveryChannel.export(), id, reason, dateTime, details, actions, retryCount);
  }

  /** Proxy to {@link IOContext#registerTimer(String, Date)} . */
  public void registerTimer(TimerResponseChannel timerChannel, Date future) {
    _brc.registerTimer(timerChannel.export(), future);
  }

  /** Proxy to {@link VariableContext#readVariableProperty(Variable, QName)}. */
  public String readProperty(VariableInstance variable, OProcess.OProperty property)
      throws FaultException {
    try {
      return _brc.readVariableProperty(variable, property.name);
    } catch (UninitializedVariableException e) {
      throw new FaultException(_runtime._oprocess.constants.qnUninitializedVariable);
    }
  }

  /** Proxy to {@link OdeRTInstanceContext#genId() }. */
  public long genId() {
    return _brc.genId();
  }

  /**
   * Proxy to {@link OdeRTInstanceContext#initializeVariable(Variable, Node)} then write properties.
   */
  public Node initializeVariable(VariableInstance var, ScopeFrame scopeFrame, Node val)
      throws ExternalVariableModuleException {
    try {
      if (var.declaration.extVar != null) /* external variable */ {
        if (__log.isDebugEnabled())
          __log.debug(
              "Initialize external variable: name="
                  + var.declaration
                  + " value="
                  + DOMUtils.domToString(val));
        Node reference = null;
        try {
          reference = fetchVariableData(var, scopeFrame, true);
        } catch (FaultException fe) {
          // In this context this is not necessarily a problem, since the assignment may re-init the
          // related var
        }
        if (reference != null) val = _brc.readExtVar(var, reference);
        return val;
      } else /* normal variable */ {
        if (__log.isDebugEnabled())
          __log.debug(
              "Initialize variable: name="
                  + var.declaration
                  + " value="
                  + DOMUtils.domToString(val));
        return _brc.initializeVariable(var, val);
      }
    } finally {
      writeProperties(var, val);
    }
  }

  /** Proxy to {@link VariableContext#fetchMyRoleEndpointReferenceData(PartnerLink)}. */
  public Node fetchMyRoleEndpointReferenceData(PartnerLinkInstance link) {
    return _brc.fetchMyRoleEndpointReferenceData(link);
  }

  public Node fetchPartnerRoleEndpointReferenceData(PartnerLinkInstance link)
      throws FaultException {
    Element epr = _brc.fetchPartnerRoleEndpointReferenceData(link);
    if (epr == null) {
      throw new FaultException(_runtime._oprocess.constants.qnUninitializedPartnerRole);
    }

    return epr;
  }

  /** Proxy to {@link OdeRTInstanceContext#convertEndpointReference(Element, Node) }. */
  public Node convertEndpointReference(Element epr, Node lvaluePtr) {
    return _brc.convertEndpointReference(epr, lvaluePtr);
  }

  public void commitChanges(VariableInstance var, ScopeFrame scopeFrame, Node value)
      throws ExternalVariableModuleException {
    if (var.declaration.extVar != null) /* external variable */ {
      if (__log.isDebugEnabled())
        __log.debug(
            "Write external variable: name="
                + var.declaration
                + " value="
                + DOMUtils.domToString(value));
      VariableInstance related = scopeFrame.resolve(var.declaration.extVar.related);
      Node reference = null;
      try {
        reference = fetchVariableData(var, scopeFrame, true);
      } catch (FaultException fe) {
        // In this context this is not necessarily a problem, since the assignment may re-init the
        // related var
      }
      VariableContext.ValueReferencePair vrp = _brc.writeExtVar(var, reference, value);
      commitChanges(related, scopeFrame, vrp.reference);
    } else /* normal variable */ {
      if (__log.isDebugEnabled())
        __log.debug(
            "Write variable: name=" + var.declaration + " value=" + DOMUtils.domToString(value));
      _brc.commitChanges(var, value);
    }
    writeProperties(var, value);
  }

  /** Proxy to {@link BpelRuntimeContext# }. */
  public void writeEndpointReference(PartnerLinkInstance plval, Element element) {
    _brc.writeEndpointReference(plval, element);
  }

  /** Proxy to {@link OdeRTInstanceContext#createScopeInstance(Long, String, int)}. */
  public Long createScopeInstance(Long scopeInstanceId, OScope scopedef) {
    return _brc.createScopeInstance(scopeInstanceId, scopedef.name, scopedef.getId());
  }

  /** Proxy to {@link BpelRuntimeContext# }. */
  public String fetchMySessionId(PartnerLinkInstance linkInstance) {
    return _brc.fetchMySessionId(linkInstance);
  }

  /** Proxy to {@link BpelRuntimeContext# }. */
  public void cancel(PickResponseChannel responseChannel) {
    final String id = responseChannel.export();
    _brc.cancelSelect(id);

    getORM().cancel(id);

    _vpu.inject(
        new JacobRunnable() {
          private static final long serialVersionUID = 6157913683737696396L;

          public void run() {
            TimerResponseChannel responseChannel = importChannel(id, TimerResponseChannel.class);
            responseChannel.onCancel();
          }
        });
  }

  /** Proxy to {@link BpelRuntimeContext# }. */
  public Element getMyRequest(String mexId) {
    return _brc.getMyRequest(mexId);
  }

  /** Proxy to {@link BpelRuntimeContext# }. */
  public void initializePartnersSessionId(PartnerLinkInstance instance, String partnersSessionId) {
    _brc.initializePartnersSessionId(instance, partnersSessionId);
  }

  /** Proxy to {@link IOContext#getSourceSessionId(String) }. */
  public String getSourceSessionId(String mexId) {
    return _brc.getSourceSessionId(mexId);
  }

  public Node getSourceEPR(String mexId) {
    return _brc.getSourceEPR(mexId);
  }

  public ExtensionOperation createExtensionActivityImplementation(QName name) {
    if (name == null) return null;
    ExtensionBundleRuntime bundle = _runtime._extensionRegistry.get(name.getNamespaceURI());
    if (bundle == null) {
      return null;
    } else {
      try {
        return bundle.getExtensionOperationInstance(name.getLocalPart());
      } catch (Exception e) {
        return null;
      }
    }
  }

  /** Proxy to {@link ProcessControlContext# }. */
  public Long getPid() {
    return _brc.getPid();
  }

  /** Proxy to {@link IOContext#getPartnerResponse(String)}. */
  public Element getPartnerResponse(String mexId) {
    return _brc.getPartnerResponse(mexId);
  }

  /** Proxy to {@link IOContext#releasePartnerMex(String) }. */
  public void releasePartnerMex(String mexId) {
    _brc.releasePartnerMex(mexId);
  }

  /** Proxy to {@link IOContext#getPartnerFault(String) }. */
  public QName getPartnerFault(String mexId) {
    return _brc.getPartnerFault(mexId);
  }

  /** Proxy to {@link IOContext#getPartnerResponseType(String) }. */
  public QName getPartnerResponseType(String mexId) {
    return _brc.getPartnerResponseType(mexId);
  }

  /** Proxy to {@link IOContext#getPartnerFaultExplanation(String) }. */
  public String getPartnerFaultExplanation(String mexId) {
    return _brc.getPartnerFaultExplanation(mexId);
  }

  /**
   * Proxy to {@link OdeRTInstanceContext#sendEvent(org.apache.ode.bpel.evt.ProcessInstanceEvent) }.
   */
  public void sendEvent(ProcessInstanceStartedEvent evt) {
    _brc.sendEvent(evt);
  }

  /** Proxy to {@link IOContext#reply(PartnerLink, String, String, Element, QName) }. */
  public void reply(
      PartnerLinkInstance plink, String opName, String bpelmex, Element element, QName fault)
      throws FaultException {
    String mexid = getORM().release(plink, opName, bpelmex);
    if (mexid == null) throw new FaultException(_runtime._oprocess.constants.qnMissingRequest);

    try {
      _brc.reply(mexid, plink, opName, element, fault);
    } catch (NoSuchOperationException e) {
      // reply to operation that is either not defined or one-way. Perhaps this should be detected
      // at compile time?
      throw new FaultException(
          _runtime._oprocess.constants.qnMissingRequest,
          "Undefined two-way operation \"" + opName + "\".");
    }
  }

  /** Proxy to {@link ProcessControlContext#forceFlush() }. */
  public void forceFlush() {
    _brc.forceFlush();
  }

  /** Proxy to {@link ProcessControlContext#forceRollback() }. */
  public void forceRollback() {
    _brc.forceRollback();
  }

  /** Proxy to {@link ProcessControlContext#terminate()}. */
  public void terminate() {
    cleanupOutstandingMyRoleExchanges(null);
    _brc.terminate();
  }

  /** Record all values of properties of a 'MessageType' variable for efficient lookup. */
  private void writeProperties(VariableInstance variable, Node value) {
    if (variable.declaration.type instanceof OMessageVarType) {
      for (OProcess.OProperty property : variable.declaration.getOwner().properties) {
        OProcess.OPropertyAlias alias = property.getAlias(variable.declaration.type);
        if (alias != null) {
          try {
            String val =
                extractProperty((Element) value, alias, variable.declaration.getDescription());
            if (val != null) _brc.writeVariableProperty(variable, property.name, val);
          } catch (UninitializedVariableException uve) {
            // This really should not happen, since we are writing to a variable that we just
            // modified.
            __log.fatal(
                "Couldn't extract property '"
                    + property.toString()
                    + "' in property pre-extraction: "
                    + uve);
            throw new RuntimeException(uve);
          } catch (FaultException e) {
            // This will fail as we're basically trying to extract properties on all received
            // messages
            // for optimization purposes.
            if (__log.isDebugEnabled())
              __log.debug(
                  "Couldn't extract property '"
                      + property.toString()
                      + "' in property pre-extraction: "
                      + e.toString());
          }
        }
      }
    }
  }

  /**
   * Extract the value of a BPEL property from a BPEL messsage variable.
   *
   * @param msgData message variable data
   * @param alias alias to apply
   * @param target description of the data (for error logging only)
   * @return value of the property
   * @throws FaultException
   */
  String extractProperty(Element msgData, OProcess.OPropertyAlias alias, String target)
      throws FaultException {
    PropertyAliasEvaluationContext ectx = new PropertyAliasEvaluationContext(msgData, alias);
    Node lValue = ectx.getRootNode();

    if (alias.location != null)
      lValue = _runtime._expLangRuntimeRegistry.evaluateNode(alias.location, ectx);

    if (lValue == null) {
      String errmsg = __msgs.msgPropertyAliasReturnedNullSet(alias.getDescription(), target);
      if (__log.isErrorEnabled()) {
        __log.error(errmsg);
      }
      throw new FaultException(_runtime._oprocess.constants.qnSelectionFailure, errmsg);
    }

    if (lValue.getNodeType() == Node.ELEMENT_NODE) {
      // This is a bit hokey, we concatenate all the children's values; we really should be checking
      // to make sure that we are only dealing with text and attribute nodes.
      StringBuffer val = new StringBuffer();
      NodeList nl = lValue.getChildNodes();
      for (int i = 0; i < nl.getLength(); ++i) {
        Node n = nl.item(i);
        val.append(n.getNodeValue());
      }
      return val.toString();
    } else if (lValue.getNodeType() == Node.TEXT_NODE) {
      return ((Text) lValue).getWholeText();
    } else return null;
  }

  public Node getPartData(Element message, OMessageVarType.Part part) {
    // borrowed from ASSIGN.evalQuery()
    Node ret = DOMUtils.findChildByName(message, new QName(null, part.name));
    if (part.type instanceof OElementVarType) {
      QName elName = ((OElementVarType) part.type).elementType;
      ret = DOMUtils.findChildByName((Element) ret, elName);
    } else if (part.type == null) {
      // Special case of header parts never referenced in the WSDL def
      if (ret != null
          && ret.getNodeType() == Node.ELEMENT_NODE
          && ((Element) ret).getAttribute("headerPart") != null
          && DOMUtils.getTextContent(ret) == null)
        ret = DOMUtils.getFirstChildElement((Element) ret);
      // The needed part isn't there, dynamically creating it
      if (ret == null) {
        ret = message.getOwnerDocument().createElementNS(null, part.name);
        ((Element) ret).setAttribute("headerPart", "true");
        message.appendChild(ret);
      }
    }

    return ret;
  }

  /**
   * @param instance
   * @param operation
   * @param outboundMsg
   * @param object
   */
  public String invoke(
      String invokeId,
      PartnerLinkInstance instance,
      Operation operation,
      Element outboundMsg,
      Object object)
      throws FaultException {
    try {
      return _brc.invoke(invokeId, instance, operation, outboundMsg);
    } catch (UninitializedPartnerEPR e) {
      throw new FaultException(_runtime._oprocess.constants.qnUninitializedPartnerRole);
    }
  }

  /** @return */
  public ExpressionLanguageRuntimeRegistry getExpLangRuntime() {
    return _runtime._expLangRuntimeRegistry;
  }

  /*
   * (non-Javadoc)
   *
   * @see org.apache.ode.bpel.engine.rapi.OdeInternalInstance#onMyRoleMessageExchange(java.lang.String, java.lang.String)
   */
  public void onSelectEvent(
      final String selectId, final String messageExchangeId, final int selectorIdx) {
    getORM().associate(selectId, messageExchangeId);

    _vpu.inject(
        new JacobRunnable() {
          private static final long serialVersionUID = 3168964409165899533L;

          public void run() {
            // NOTE: we chose the selectId to be the exported representation of the pick response
            // channel!
            PickResponseChannel responseChannel =
                importChannel(selectId, PickResponseChannel.class);
            responseChannel.onRequestRcvd(selectorIdx, messageExchangeId);
          }
        });
  }

  /*
   * (non-Javadoc)
   *
   * @see org.apache.ode.bpel.engine.rapi.OdeInternalInstance#onTimerEvent(java.lang.String)
   */
  public void onTimerEvent(final String timerId) {
    getORM().cancel(timerId);

    _vpu.inject(
        new JacobRunnable() {
          private static final long serialVersionUID = -7767141033611036745L;

          public void run() {
            // NOTE: note short cut, we chose timer id to be the same as the exported channel
            // representation.
            TimerResponseChannel responseChannel =
                importChannel(timerId, TimerResponseChannel.class);
            responseChannel.onTimeout();
          }
        });
  }

  /*
   * (non-Javadoc)
   *
   * @see org.apache.ode.bpel.engine.rapi.OdeInternalInstance#execute()
   */
  public boolean execute() {
    return _vpu.execute();
  }

  /*
   * (non-Javadoc)
   *
   * @see org.apache.ode.bpel.engine.rapi.OdeInternalInstance#onInvokeResponse(java.lang.String, java.lang.String)
   */
  public void onInvokeResponse(final String invokeId, InvokeResponseType irt, final String mexid) {
    // NOTE: do the switch outside the inject, since we don't want to end up serializing
    // InvokeResponseType objects!
    switch (irt) {
      case REPLY:
        _vpu.inject(
            new BpelJacobRunnable() {
              private static final long serialVersionUID = -1095444335740879981L;

              public void run() {
                importChannel(invokeId, InvokeResponseChannel.class).onResponse();
              }
            });
        break;
      case FAULT:
        _vpu.inject(
            new BpelJacobRunnable() {
              private static final long serialVersionUID = -1095444335740879981L;

              public void run() {
                importChannel(invokeId, InvokeResponseChannel.class).onFault();
              }
            });
        break;
      case FAILURE:
        _vpu.inject(
            new BpelJacobRunnable() {
              private static final long serialVersionUID = -1095444335740879981L;

              public void run() {
                importChannel(invokeId, InvokeResponseChannel.class).onFailure();
              }
            });
        break;
    }
  }

  public void recoverActivity(
      final String channel, final long activityId, final String action, FaultInfo fault) {
    // TODO: better translation here?
    final FaultData fdata =
        (fault != null) ? new FaultData(fault.getFaultName(), null, fault.getExplanation()) : null;

    _vpu.inject(
        new JacobRunnable() {
          private static final long serialVersionUID = 3168964409165899533L;

          public void run() {
            ActivityRecoveryChannel recovery =
                importChannel(channel, ActivityRecoveryChannel.class);
            __log.info(
                "ActivityRecovery: Recovering activity "
                    + activityId
                    + " with action "
                    + action
                    + " on channel "
                    + recovery);
            if (recovery != null) {
              if ("cancel".equals(action)) recovery.cancel();
              else if ("retry".equals(action)) recovery.retry();
              else if ("fault".equals(action)) recovery.fault(fdata);
            }
          }
        });
  }

  private OutstandingRequestManager getORM() {
    return (OutstandingRequestManager) _soup.getGlobalData();
  }

  /** Called when the process completes to clean up any outstanding message exchanges. */
  private void cleanupOutstandingMyRoleExchanges(FaultInfo optionalFaultData) {
    // TODO: all this should be moved into the engine. We don't really need the ORM to find
    // these mexs, we can just scan the database
    String[] mexRefs = getORM().releaseAll();
    for (String mexId : mexRefs) {
      _brc.noreply(mexId, optionalFaultData);
    }
  }

  /* (non-Javadoc)
   * @see org.apache.ode.bpel.engine.rapi.OdeInternalInstance#saveState()
   */
  public Object saveState(OutputStream bos) throws IOException {
    if (bos != null) _soup.write(bos);
    return _soup;
  }

  /* (non-Javadoc)
   * @see org.apache.ode.bpel.engine.rapi.OdeInternalInstance#createInstance(java.lang.String)
   */
  public void onCreateInstance(String messageExchangeId) {
    _vpu.inject(new PROCESS(_runtime._oprocess));
  }

  /* (non-Javadoc)
   * @see org.apache.ode.bpel.engine.rapi.OdeInternalInstance#setContext(org.apache.ode.bpel.engine.rapi.OdeRTInstanceContext)
   */
  public void setContext(OdeRTInstanceContext ctx) {
    _brc = ctx;
  }

  public URI getBaseResourceURI() {
    return _runtime._pconf.getBaseURI();
  }

  public int getRetryDelay() {
    return _brc.getAtomicScopeRetryDelay();
  }

  public boolean isFirstTry() {
    return _brc.isAtomicScopeFirstTry();
  }

  public boolean isRetryable() {
    return _brc.isAtomicScopeRetryable();
  }

  public void setRetriedOnce() {
    _brc.setAtomicScopeRetriedOnce();
  }

  public void setRetriesDone() {
    _brc.setAtomicScopeRetriesDone();
  }

  public void setAtomicScope(boolean atomicScope) {
    _brc.setAtomicScope(atomicScope);
  }

  public Node getProcessProperty(QName propertyName) {
    return _brc.getProcessProperty(propertyName);
  }
}
Пример #8
0
/**
 * JDBC-based implementation of a process store. Also provides an "in-memory" store by way of H2
 * database.
 *
 * <p>The philsophy here is to keep things simple. Process store operations are relatively
 * infrequent. Performance of the public methods is not a concern. However, note that the {@link
 * org.apache.ode.bpel.iapi.ProcessConf} objects returned by the class are going to be used from
 * within the engine runtime, and hence their performance needs to be very good. Similarly, these
 * objects should be immutable so as not to confuse the engine.
 *
 * <p>Note the way that the database is used in this class, it is more akin to a recovery log, this
 * is intentional: we want to start up, load stuff from the database and then pretty much forget
 * about it when it comes to reads.
 *
 * @author Maciej Szefler <mszefler at gmail dot com>
 * @author mriou <mriou at apache dot org>
 */
public class ProcessStoreImpl implements ProcessStore {

  private static final Log __log = LogFactory.getLog(ProcessStoreImpl.class);

  private static final Messages __msgs = MessageBundle.getMessages(Messages.class);

  private final CopyOnWriteArrayList<ProcessStoreListener> _listeners =
      new CopyOnWriteArrayList<ProcessStoreListener>();

  private Map<QName, ProcessConfImpl> _processes = new HashMap<QName, ProcessConfImpl>();

  private Map<String, DeploymentUnitDir> _deploymentUnits =
      new HashMap<String, DeploymentUnitDir>();

  /** Guards access to the _processes and _deploymentUnits */
  private final ReadWriteLock _rw = new ReentrantReadWriteLock();

  private ConfStoreConnectionFactory _cf;

  private EndpointReferenceContext eprContext;

  private boolean generateProcessEventsAll;

  protected File _deployDir;

  protected File _configDir;

  /**
   * Executor used to process DB transactions. Allows us to isolate the TX context, and to ensure
   * that only one TX gets executed a time. We don't really care to parallelize these operations
   * because: i) HSQL does not isolate transactions and we don't want to get confused ii) we're
   * already serializing all the operations with a read/write lock. iii) we don't care about
   * performance, these are infrequent operations.
   */
  private ExecutorService _executor = Executors.newSingleThreadExecutor(new SimpleThreadFactory());

  /**
   * In-memory DataSource, or <code>null</code> if we are using a real DS. We need this to shutdown
   * the DB.
   */
  private DataSource _inMemDs;

  private static final ThreadLocal<Long> _currentVersion = new ThreadLocal<Long>();

  public ProcessStoreImpl() {
    this(null, null, "", new OdeConfigProperties(new Properties(), ""), true);
  }

  public ProcessStoreImpl(
      EndpointReferenceContext eprContext,
      DataSource ds,
      String persistenceType,
      OdeConfigProperties props,
      boolean createDatamodel) {
    this.eprContext = eprContext;
    this.generateProcessEventsAll = props.getProperty("generateProcessEvents", "all").equals("all");
    if (ds != null) {
      // ugly hack
      if (persistenceType.toLowerCase().indexOf("hib") != -1) {
        _cf =
            new org.apache.ode.store.hib.DbConfStoreConnectionFactory(
                ds, props.getProperties(), createDatamodel, props.getTxFactoryClass());
      } else {
        _cf =
            new org.apache.ode.store.jpa.DbConfStoreConnectionFactory(
                ds, props.getProperties(), createDatamodel, props.getTxFactoryClass());
      }
    } else {
      // If the datasource is not provided, then we create a H2-based
      // in-memory database. Makes testing a bit simpler.
      DataSource h2 = createInternalDS(new GUID().toString());
      if ("hibernate".equalsIgnoreCase(persistenceType)) {
        _cf =
            new org.apache.ode.store.hib.DbConfStoreConnectionFactory(
                h2, props.getProperties(), createDatamodel, props.getTxFactoryClass());
      } else {
        _cf =
            new org.apache.ode.store.jpa.DbConfStoreConnectionFactory(
                h2, props.getProperties(), createDatamodel, props.getTxFactoryClass());
      }
      _inMemDs = h2;
    }
  }

  /** Constructor that hardwires OpenJPA on a new in-memory database. Suitable for tests. */
  public ProcessStoreImpl(EndpointReferenceContext eprContext, DataSource inMemDs) {
    this.eprContext = eprContext;
    DataSource h2 = createInternalDS(new GUID().toString());
    // when in memory we always create the model as we are starting from scratch
    _cf =
        new org.apache.ode.store.jpa.DbConfStoreConnectionFactory(
            h2, true, OdeConfigProperties.DEFAULT_TX_FACTORY_CLASS_NAME);
    _inMemDs = h2;
  }

  public void shutdown() {
    if (_inMemDs != null) {
      shutdownInternalDB(_inMemDs);
      _inMemDs = null;
    }
    if (_executor != null) {
      _executor.shutdownNow();
      _executor = null;
    }
  }

  @Override
  protected void finalize() throws Throwable {
    // force a shutdown so that HSQL cleans up its mess.
    try {
      shutdown();
    } catch (Throwable t) {; // we tried, no worries.
    }
    super.finalize();
  }

  /** Deploys a process. */
  public Collection<QName> deploy(
      final File deploymentUnitDirectory, boolean autoincrementVersion) {
    return deploy(deploymentUnitDirectory, true, null, autoincrementVersion);
  }

  public Collection<QName> deploy(final File deploymentUnitDirectory) {
    return deploy(deploymentUnitDirectory, true, null, OdeGlobalConfig.autoincrementVersion());
  }

  /** Deploys a process. */
  public Collection<QName> deploy(
      final File deploymentUnitDirectory,
      boolean activate,
      String duName,
      boolean autoincrementVersion) {
    __log.info(__msgs.msgDeployStarting(deploymentUnitDirectory));

    final Date deployDate = new Date();

    // Create the DU and compile/scan it before acquiring lock.
    final DeploymentUnitDir du = new DeploymentUnitDir(deploymentUnitDirectory);
    if (duName != null) {
      // Override the package name if given from the parameter
      du.setName(duName);
    }

    long version;
    if (autoincrementVersion || du.getStaticVersion() == -1) {
      // Process and DU use a monotonically increased single version number by default.
      try {
        version = getCurrentVersion();
      } finally {
        // we need to reset the current version thread local value.
        _currentVersion.set(null);
      }
    } else {
      version = du.getStaticVersion();
    }
    du.setVersion(version);

    try {
      du.compile();
    } catch (CompilationException ce) {
      String errmsg = __msgs.msgDeployFailCompileErrors(ce);
      __log.error(errmsg, ce);
      throw new ContextException(errmsg, ce);
    }

    du.scan();
    final DeployDocument dd = du.getDeploymentDescriptor();
    final ArrayList<ProcessConfImpl> processes = new ArrayList<ProcessConfImpl>();
    Collection<QName> deployed;

    _rw.writeLock().lock();

    try {
      if (_deploymentUnits.containsKey(du.getName())) {
        String errmsg = __msgs.msgDeployFailDuplicateDU(du.getName());
        __log.error(errmsg);
        throw new ContextException(errmsg);
      }

      retirePreviousPackageVersions(du);

      for (TDeployment.Process processDD : dd.getDeploy().getProcessArray()) {
        QName pid = toPid(processDD.getName(), version);

        if (_processes.containsKey(pid)) {
          String errmsg = __msgs.msgDeployFailDuplicatePID(processDD.getName(), du.getName());
          __log.error(errmsg);
          throw new ContextException(errmsg);
        }

        QName type = processDD.getType() != null ? processDD.getType() : processDD.getName();

        CBPInfo cbpInfo = du.getCBPInfo(type);
        if (cbpInfo == null) {
          String errmsg = __msgs.msgDeployFailedProcessNotFound(processDD.getName(), du.getName());
          __log.error(errmsg);
          throw new ContextException(errmsg);
        }

        ProcessConfImpl pconf =
            new ProcessConfImpl(
                pid,
                processDD.getName(),
                version,
                du,
                processDD,
                deployDate,
                calcInitialProperties(du.getProperties(), processDD),
                calcInitialState(processDD),
                eprContext,
                _configDir,
                generateProcessEventsAll);
        processes.add(pconf);
      }

      _deploymentUnits.put(du.getName(), du);

      for (ProcessConfImpl process : processes) {
        __log.info(__msgs.msgProcessDeployed(du.getDeployDir(), process.getProcessId()));
        _processes.put(process.getProcessId(), process);
      }

    } finally {
      _rw.writeLock().unlock();
    }

    // Do the deployment in the DB. We need this so that we remember deployments across system
    // shutdowns.
    // We don't fail if there is a DB error, simply print some errors.
    deployed =
        exec(
            new Callable<Collection<QName>>() {
              public Collection<QName> call(ConfStoreConnection conn) {
                // Check that this deployment unit is not deployed.
                DeploymentUnitDAO dudao = conn.getDeploymentUnit(du.getName());
                if (dudao != null) {
                  String errmsg = "Database out of synch for DU " + du.getName();
                  __log.warn(errmsg);
                  dudao.delete();
                }

                dudao = conn.createDeploymentUnit(du.getName());
                try {
                  dudao.setDeploymentUnitDir(deploymentUnitDirectory.getCanonicalPath());
                } catch (IOException e1) {
                  String errmsg =
                      "Error getting canonical path for "
                          + du.getName()
                          + "; deployment unit will not be available after restart!";
                  __log.error(errmsg);
                }

                ArrayList<QName> deployed = new ArrayList<QName>();
                // Going trough each process declared in the dd
                for (ProcessConfImpl pc : processes) {
                  try {
                    ProcessConfDAO newDao =
                        dudao.createProcess(pc.getProcessId(), pc.getType(), pc.getVersion());
                    newDao.setState(pc.getState());
                    for (Map.Entry<QName, Node> prop : pc.getProcessProperties().entrySet()) {
                      newDao.setProperty(prop.getKey(), DOMUtils.domToString(prop.getValue()));
                    }
                    deployed.add(pc.getProcessId());
                  } catch (Throwable e) {
                    String errmsg =
                        "Error persisting deployment record for "
                            + pc.getProcessId()
                            + "; process will not be available after restart!";
                    __log.error(errmsg, e);
                  }
                }
                return deployed;
              }
            });

    _rw.readLock().lock();
    boolean readLockHeld = true;
    try {
      for (ProcessConfImpl process : processes) {
        fireEvent(
            new ProcessStoreEvent(
                ProcessStoreEvent.Type.DEPLOYED,
                process.getProcessId(),
                process.getDeploymentUnit().getName()));
        fireStateChange(
            process.getProcessId(), process.getState(), process.getDeploymentUnit().getName());
      }
    } catch (Exception e) {
      // need to unlock as undeploy operation will need a writeLock
      _rw.readLock().unlock();
      readLockHeld = false;

      // A problem at that point means that engine deployment failed, we don't want the store to
      // keep the du
      __log.warn("Deployment failed within the engine, store undeploying process.", e);
      undeploy(deploymentUnitDirectory);
      if (e instanceof ContextException) throw (ContextException) e;
      else throw new ContextException("Deployment failed within the engine. " + e.getMessage(), e);
    } finally {
      if (readLockHeld) _rw.readLock().unlock();
    }

    return deployed;
  }

  /**
   * Retire all the other versions of the same DU: first take the DU name and insert version regexp,
   * than try to match the this string against names of already deployed DUs. For instance if we are
   * deploying DU "AbsenceRequest-2/AbsenceRequest.ode" and there's already version 2 than regexp
   * "AbsenceRequest([-\\.](\d)+)?/AbsenceRequest.ode" will be matched against
   * "AbsenceRequest-2/AbsenceRequest.ode" and setRetirePackage() will be called accordingly.
   */
  private void retirePreviousPackageVersions(DeploymentUnitDir du) {
    // retire all the other versions of the same DU
    String[] nameParts = du.getName().split("/");
    /* Replace the version number (if any) with regexp to match any version number */
    nameParts[0] = nameParts[0].replaceAll("([-\\Q.\\E](\\d)+)?\\z", "");
    nameParts[0] += "([-\\Q.\\E](\\d)+)?";
    StringBuilder duNameRegExp = new StringBuilder(du.getName().length() * 2);
    for (int i = 0, n = nameParts.length; i < n; i++) {
      if (i > 0) duNameRegExp.append("/");
      duNameRegExp.append(nameParts[i]);
    }

    Pattern duNamePattern = Pattern.compile(duNameRegExp.toString());
    for (String deployedDUname : _deploymentUnits.keySet()) {
      Matcher matcher = duNamePattern.matcher(deployedDUname);
      if (matcher.matches()) {
        setRetiredPackage(deployedDUname, true);
      }
    }
  }

  public Collection<QName> undeploy(final File dir) {
    return undeploy(dir.getName());
  }

  public Collection<QName> undeploy(final String duName) {
    try {
      exec(
          new Callable<Collection<QName>>() {
            public Collection<QName> call(ConfStoreConnection conn) {
              DeploymentUnitDAO dudao = conn.getDeploymentUnit(duName);
              if (dudao != null) dudao.delete();
              return null;
            }
          });
    } catch (Exception ex) {
      __log.error(
          "Error synchronizing with data store; " + duName + " may be reappear after restart!");
    }

    Collection<QName> undeployed = Collections.emptyList();
    DeploymentUnitDir du;
    _rw.writeLock().lock();
    try {
      du = _deploymentUnits.remove(duName);
      if (du != null) {
        undeployed = toPids(du.getProcessNames(), du.getVersion());
      }

      for (QName pn : undeployed) {
        fireEvent(new ProcessStoreEvent(ProcessStoreEvent.Type.UNDEPLOYED, pn, du.getName()));
        __log.info(__msgs.msgProcessUndeployed(pn));
      }

      _processes.keySet().removeAll(undeployed);
    } finally {
      _rw.writeLock().unlock();
    }

    return undeployed;
  }

  public Collection<String> getPackages() {
    _rw.readLock().lock();
    try {
      return new ArrayList<String>(_deploymentUnits.keySet());
    } finally {
      _rw.readLock().unlock();
    }
  }

  public List<QName> listProcesses(String packageName) {
    _rw.readLock().lock();
    try {
      DeploymentUnitDir du = _deploymentUnits.get(packageName);
      if (du == null) return null;
      return toPids(du.getProcessNames(), du.getVersion());
    } finally {
      _rw.readLock().unlock();
    }
  }

  public void setState(final QName pid, final ProcessState state) {
    __log.debug("Changing process state for " + pid + " to " + state);

    final ProcessConfImpl pconf;

    _rw.readLock().lock();
    try {
      pconf = _processes.get(pid);
      if (pconf == null) {
        String msg = __msgs.msgProcessNotFound(pid);
        __log.info(msg);
        throw new ContextException(msg);
      }
    } finally {
      _rw.readLock().unlock();
    }

    final DeploymentUnitDir dudir = pconf.getDeploymentUnit();

    // Update in the database.
    ProcessState old =
        exec(
            new Callable<ProcessState>() {
              public ProcessState call(ConfStoreConnection conn) {
                DeploymentUnitDAO dudao = conn.getDeploymentUnit(dudir.getName());
                if (dudao == null) {
                  String errmsg = __msgs.msgProcessNotFound(pid);
                  __log.error(errmsg);
                  throw new ContextException(errmsg);
                }

                ProcessConfDAO dao = dudao.getProcess(pid);
                if (dao == null) {
                  String errmsg = __msgs.msgProcessNotFound(pid);
                  __log.error(errmsg);
                  throw new ContextException(errmsg);
                }

                Set processKeys = _processes.keySet();
                Iterator processConfQNameItr = processKeys.iterator();

                while (processConfQNameItr.hasNext()) {
                  ProcessConf cachedProcessConf = _processes.get(processConfQNameItr.next());
                  if (dao.getType().equals(cachedProcessConf.getType())) {
                    if (ProcessState.ACTIVE == cachedProcessConf.getState()
                        && ProcessState.RETIRED == dao.getState()
                        && ProcessState.ACTIVE == state) {
                      String errorMsg =
                          "Can't activate the process with PID: "
                              + dao.getPID()
                              + " with version "
                              + dao.getVersion()
                              + ", as another version of the process with PID : "
                              + cachedProcessConf.getProcessId()
                              + " with version "
                              + cachedProcessConf.getVersion()
                              + " is already active.";
                      __log.error(errorMsg);
                      throw new ContextException(errorMsg);
                    }
                  }
                }

                ProcessState old = dao.getState();
                dao.setState(state);
                pconf.setState(state);
                return old;
              }
            });

    pconf.setState(state);
    if (old != null && old != state)
      fireStateChange(pid, state, pconf.getDeploymentUnit().getName());
  }

  public void setRetiredPackage(String packageName, boolean retired) {
    DeploymentUnitDir duDir = _deploymentUnits.get(packageName);
    if (duDir == null) throw new ContextException("Could not find package " + packageName);
    for (QName processName : duDir.getProcessNames()) {
      setState(
          toPid(processName, duDir.getVersion()),
          retired ? ProcessState.RETIRED : ProcessState.ACTIVE);
    }
  }

  public ProcessConf getProcessConfiguration(final QName processId) {
    _rw.readLock().lock();
    try {
      return _processes.get(processId);
    } finally {
      _rw.readLock().unlock();
    }
  }

  public void setProperty(final QName pid, final QName propName, final Node value) {
    setProperty(pid, propName, DOMUtils.domToStringLevel2(value));
  }

  public void setProperty(final QName pid, final QName propName, final String value) {
    if (__log.isDebugEnabled()) __log.debug("Setting property " + propName + " on process " + pid);

    ProcessConfImpl pconf = _processes.get(pid);
    if (pconf == null) {
      String msg = __msgs.msgProcessNotFound(pid);
      __log.info(msg);
      throw new ContextException(msg);
    }

    final DeploymentUnitDir dudir = pconf.getDeploymentUnit();
    exec(
        new ProcessStoreImpl.Callable<Object>() {
          public Object call(ConfStoreConnection conn) {
            DeploymentUnitDAO dudao = conn.getDeploymentUnit(dudir.getName());
            if (dudao == null) return null;
            ProcessConfDAO proc = dudao.getProcess(pid);
            if (proc == null) return null;
            proc.setProperty(propName, value);
            return null;
          }
        });

    fireEvent(new ProcessStoreEvent(ProcessStoreEvent.Type.PROPERTY_CHANGED, pid, dudir.getName()));
  }

  /** Load all the deployment units out of the store. Called on start-up. */
  public void loadAll() {
    final ArrayList<ProcessConfImpl> loaded = new ArrayList<ProcessConfImpl>();
    exec(
        new Callable<Object>() {
          public Object call(ConfStoreConnection conn) {
            Collection<DeploymentUnitDAO> dus = conn.getDeploymentUnits();
            for (DeploymentUnitDAO du : dus)
              try {
                loaded.addAll(load(du));
              } catch (Exception ex) {
                __log.error("Error loading DU from store: " + du.getName(), ex);
              }
            return null;
          }
        });

    // Dispatch DISABLED, RETIRED and ACTIVE events in that order
    Collections.sort(
        loaded,
        new Comparator<ProcessConf>() {
          public int compare(ProcessConf o1, ProcessConf o2) {
            return stateValue(o1.getState()) - stateValue(o2.getState());
          }

          int stateValue(ProcessState state) {
            if (ProcessState.DISABLED.equals(state)) return 0;
            if (ProcessState.RETIRED.equals(state)) return 1;
            if (ProcessState.ACTIVE.equals(state)) return 2;
            throw new IllegalStateException("Unexpected process state: " + state);
          }
        });
    for (ProcessConfImpl p : loaded) {
      try {
        fireStateChange(p.getProcessId(), p.getState(), p.getDeploymentUnit().getName());
      } catch (Exception except) {
        __log.error(
            "Error while activating process: pid="
                + p.getProcessId()
                + " package="
                + p.getDeploymentUnit().getName(),
            except);
      }
    }
  }

  public List<QName> getProcesses() {
    _rw.readLock().lock();
    try {
      return new ArrayList<QName>(_processes.keySet());
    } finally {
      _rw.readLock().unlock();
    }
  }

  public long getCurrentVersion() {
    if (_currentVersion.get() != null) {
      return _currentVersion.get();
    }

    long version =
        exec(
            new Callable<Long>() {
              public Long call(ConfStoreConnection conn) {
                return conn.getNextVersion();
              }
            });

    _currentVersion.set(version);
    return _currentVersion.get();
  }

  protected void fireEvent(ProcessStoreEvent pse) {
    __log.debug("firing event: " + pse);
    for (ProcessStoreListener psl : _listeners) psl.onProcessStoreEvent(pse);
  }

  private void fireStateChange(QName processId, ProcessState state, String duname) {
    switch (state) {
      case ACTIVE:
        fireEvent(new ProcessStoreEvent(ProcessStoreEvent.Type.ACTVIATED, processId, duname));
        break;
      case DISABLED:
        fireEvent(new ProcessStoreEvent(ProcessStoreEvent.Type.DISABLED, processId, duname));
        break;
      case RETIRED:
        fireEvent(new ProcessStoreEvent(ProcessStoreEvent.Type.RETIRED, processId, duname));
        break;
    }
  }

  public void registerListener(ProcessStoreListener psl) {
    __log.debug("Registering listener " + psl);
    _listeners.add(psl);
  }

  public void unregisterListener(ProcessStoreListener psl) {
    __log.debug("Unregistering listener " + psl);
    _listeners.remove(psl);
  }

  /**
   * Execute database transactions in an isolated context.
   *
   * @param <T> return type
   * @param callable transaction
   * @return
   */
  synchronized <T> T exec(Callable<T> callable) {
    // We want to submit db jobs to an executor to isolate
    // them from the current thread,
    Future<T> future = _executor.submit(callable);
    try {
      return future.get();
    } catch (Exception e) {
      throw new ContextException("DbError", e);
    }
  }

  private ConfStoreConnection getConnection() {
    return _cf.getConnection();
  }

  /**
   * Create a property mapping based on the initial values in the deployment descriptor.
   *
   * @param dd
   * @return
   */
  public static Map<QName, Node> calcInitialProperties(
      Properties properties, TDeployment.Process dd) {
    HashMap<QName, Node> ret = new HashMap<QName, Node>();

    for (Object key1 : properties.keySet()) {
      String key = (String) key1;
      Document doc = DOMUtils.newDocument();
      doc.appendChild(doc.createElementNS(null, "temporary-simple-type-wrapper"));
      doc.getDocumentElement().appendChild(doc.createTextNode(properties.getProperty(key)));

      ret.put(new QName(key), doc.getDocumentElement());
    }

    for (TDeployment.Process.Property property : dd.getPropertyArray()) {
      Element elmtContent = DOMUtils.getElementContent(property.getDomNode());
      if (elmtContent != null) {
        // We'll need DOM Level 3
        Document doc = DOMUtils.newDocument();
        doc.appendChild(doc.importNode(elmtContent, true));
        ret.put(property.getName(), doc.getDocumentElement());
      } else ret.put(property.getName(), property.getDomNode().getFirstChild());
    }
    return ret;
  }

  /**
   * Figure out the initial process state from the state in the deployment descriptor.
   *
   * @param dd deployment descriptor
   * @return
   */
  private static ProcessState calcInitialState(TDeployment.Process dd) {
    ProcessState state = ProcessState.ACTIVE;

    if (dd.isSetActive() && dd.getActive() == false) state = ProcessState.DISABLED;
    if (dd.isSetRetired() && dd.getRetired() == true) state = ProcessState.RETIRED;

    return state;
  }

  /**
   * Load a deployment unit record stored in the db into memory.
   *
   * @param dudao
   */
  protected List<ProcessConfImpl> load(DeploymentUnitDAO dudao) {
    __log.debug("Loading deployment unit record from db: " + dudao.getName());

    File dudir = findDeployDir(dudao);

    if (dudir == null || !dudir.exists())
      throw new ContextException(
          "Deployed directory " + (dudir == null ? "(unknown)" : dudir) + " no longer there!");
    DeploymentUnitDir dud = new DeploymentUnitDir(dudir);
    // set the name with the one from database
    dud.setName(dudao.getName());
    dud.scan();

    ArrayList<ProcessConfImpl> loaded = new ArrayList<ProcessConfImpl>();

    _rw.writeLock().lock();
    try {
      _deploymentUnits.put(dud.getName(), dud);

      long version = 0;
      for (ProcessConfDAO p : dudao.getProcesses()) {
        TDeployment.Process pinfo = dud.getProcessDeployInfo(p.getType());
        if (pinfo == null) {
          __log.warn("Cannot load " + p.getPID() + "; cannot find descriptor.");
          continue;
        }

        Map<QName, Node> props = calcInitialProperties(dud.getProperties(), pinfo);
        // TODO: update the props based on the values in the DB.

        ProcessConfImpl pconf =
            new ProcessConfImpl(
                p.getPID(),
                p.getType(),
                p.getVersion(),
                dud,
                pinfo,
                dudao.getDeployDate(),
                props,
                p.getState(),
                eprContext,
                _configDir,
                generateProcessEventsAll);
        version = p.getVersion();

        _processes.put(pconf.getProcessId(), pconf);
        loaded.add(pconf);
      }

      // All processes and the DU have the same version
      dud.setVersion(version);
    } finally {
      _rw.writeLock().unlock();
    }

    return loaded;
  }

  protected File findDeployDir(DeploymentUnitDAO dudao) {
    File f = new File(dudao.getDeploymentUnitDir());
    if (f.exists()) return f;
    f = new File(_deployDir, dudao.getName());
    if (f.exists()) {
      try {
        dudao.setDeploymentUnitDir(f.getCanonicalPath());
      } catch (IOException e) {
        __log.warn("Could not update deployment unit directory for " + dudao.getName(), e);
      }
      return f;
    }

    return null;
  }

  /**
   * Make sure that the deployment unit is loaded.
   *
   * @param duName deployment unit name
   */
  protected boolean load(final String duName) {
    _rw.writeLock().lock();
    try {
      if (_deploymentUnits.containsKey(duName)) return true;
    } finally {
      _rw.writeLock().unlock();
    }

    try {
      return exec(
          new Callable<Boolean>() {
            public Boolean call(ConfStoreConnection conn) {
              DeploymentUnitDAO dudao = conn.getDeploymentUnit(duName);
              if (dudao == null) return false;
              load(dudao);
              return true;
            }
          });
    } catch (Exception ex) {
      __log.error("Error loading deployment unit: " + duName);
      return false;
    }
  }

  /**
   * Wrapper for database transactions.
   *
   * @author Maciej Szefler
   * @param <V> return type
   */
  abstract class Callable<V> implements java.util.concurrent.Callable<V> {
    public V call() {
      boolean success = false;
      // in JTA, transaction is bigger than the session
      _cf.beginTransaction();
      ConfStoreConnection conn = getConnection();
      try {
        V r = call(conn);
        _cf.commitTransaction();
        success = true;
        return r;
      } finally {
        if (!success)
          try {
            _cf.rollbackTransaction();
          } catch (Exception ex) {
            __log.error("DbError", ex);
          }
      }
      // session is closed automatically when committed or rolled back under JTA
    }

    abstract V call(ConfStoreConnection conn);
  }

  public void setDeployDir(File depDir) {
    if (depDir != null) {
      if (!depDir.exists()) {
        depDir.mkdirs();
        __log.warn(
            "Deploy directory: " + depDir.getAbsolutePath() + " does not exist; created it.");
      } else if (!depDir.isDirectory()) {
        throw new IllegalArgumentException("Deploy directory is not a directory:  " + depDir);
      }
    }

    _deployDir = depDir;
  }

  public File getDeployDir() {
    return _deployDir;
  }

  public File getConfigDir() {
    return _configDir;
  }

  public void setConfigDir(File configDir) {
    if (configDir != null && !configDir.isDirectory())
      throw new IllegalArgumentException(
          "Config directory is not a directory or does not exist: " + configDir);
    this._configDir = configDir;
  }

  public static DataSource createInternalDS(String guid) {
    JdbcDataSource h2 = new JdbcDataSource();
    h2.setURL("jdbc:h2:mem:" + new GUID().toString() + ";DB_CLOSE_DELAY=-1");
    h2.setUser("sa");
    return h2;
  }

  public static void shutdownInternalDB(DataSource ds) {
    try {
      ds.getConnection().createStatement().execute("SHUTDOWN;");
    } catch (SQLException e) {
      __log.error("Error shutting down.", e);
    }
  }

  private List<QName> toPids(Collection<QName> processTypes, long version) {
    ArrayList<QName> result = new ArrayList<QName>();
    for (QName pqName : processTypes) {
      result.add(toPid(pqName, version));
    }
    return result;
  }

  private QName toPid(QName processType, long version) {
    return new QName(processType.getNamespaceURI(), processType.getLocalPart() + "-" + version);
  }

  private class SimpleThreadFactory implements ThreadFactory {
    int threadNumber = 0;

    public Thread newThread(Runnable r) {
      threadNumber += 1;
      Thread t = new Thread(r, "ProcessStoreImpl-" + threadNumber);
      t.setDaemon(true);
      return t;
    }
  }

  public void refreshSchedules(String packageName) {
    List<QName> pids = listProcesses(packageName);
    if (pids != null) {
      for (QName pid : pids) {
        fireEvent(
            new ProcessStoreEvent(
                ProcessStoreEvent.Type.SCHEDULE_SETTINGS_CHANGED, pid, packageName));
      }
    }
  }
}