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

org.ow2.bonita.runtime.event.JobExecutor Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (C) 2102-2013 BonitaSoft S.A.
 * BonitaSoft, 31 rue Gustave Eiffel - 38000 Grenoble
 * This library is free software; you can redistribute it and/or modify it under the terms
 * of the GNU Lesser General Public License as published by the Free Software Foundation
 * version 2.1 of the License.
 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Lesser General Public License for more details.
 * You should have received a copy of the GNU Lesser General Public License along with this
 * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA  02110-1301, USA.
 **/
package org.ow2.bonita.runtime.event;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;

import org.ow2.bonita.runtime.event.EventExecutor.EventRejectionHandler;
import org.ow2.bonita.util.Command;

/**
 * 
 * @author Matthieu Chaffotte
 * 
 */
public abstract class JobExecutor extends EventExecutorThread {

  private final int locksToQuery;
  private final int lockIdleTime;
  private final int nbrOfThreads;

  private final Map> runningThreads = new ConcurrentHashMap>();
  private final Map lockedProcessUUIDs = new ConcurrentHashMap();

  private transient ThreadPoolExecutor threadPool;

  JobExecutor(final EventExecutor executor, final String name, final int nbrOfThreads, final int locksToQuery,
      final int lockIdleTime) {
    super(executor, name);
    this.locksToQuery = locksToQuery;
    this.lockIdleTime = lockIdleTime;
    this.nbrOfThreads = nbrOfThreads;
    threadPool = new ThreadPoolExecutor(nbrOfThreads, nbrOfThreads, 0L, TimeUnit.MILLISECONDS,
        new ArrayBlockingQueue(nbrOfThreads * 1000), EventRejectionHandler.INSTANCE);
  }

  @Override
  protected final void activate() {

  }

  public int getLockIdleTime() {
    return lockIdleTime;
  }

  public int getLocksToQuery() {
    return locksToQuery;
  }

  @Override
  public void deactivate(final boolean join) {
    if (LOG.isLoggable(Level.INFO)) {
      LOG.info("Event executor: shutdown threadpool...");
    }
    threadPool.shutdown();
    if (LOG.isLoggable(Level.INFO)) {
      LOG.info("Event executor: threadpool shutdowned.");
    }
    if (join) {
      try {
        if (LOG.isLoggable(Level.INFO)) {
          LOG.info("Event executor: waiting for threadPool termination...");
        }
        threadPool.awaitTermination(1000 * 60 * 5, TimeUnit.MILLISECONDS);
        if (LOG.isLoggable(Level.INFO)) {
          LOG.info("Event executor: threadPool termination OK...");
        }
      } catch (final InterruptedException e) {
        LOG.severe("joining got interrupted");
      }
    }
    super.deactivate(join);
  }

  protected abstract Command> getNonlockedProcessUUIDsCommand(final Set processUUIDsToExclude);

  protected abstract boolean lockJob(final String processUUID);

  protected abstract List getLockedJobs(final String processUUID);

  protected abstract void releaseLock(final String processUUID);

  protected void notifyThreadFinished(final String processUUID) {
    runningThreads.remove(processUUID);
    refresh();
  }

  @Override
  protected void execute() throws InterruptedException {
    cleanRunningThreads();
    cleanLockedProcessUUIDs();

    final List nonLockedProcessUUIDs = getCommandService().execute(
        getNonlockedProcessUUIDsCommand(getProcessUUIDsToExclude()));
    if (LOG.isLoggable(Level.FINE)) {
      LOG.fine("On " + getJobExecutorName() + ", nonLockedProcessUUIDs(" + nonLockedProcessUUIDs.size() + ") = "
          + nonLockedProcessUUIDs);
    }

    if (nonLockedProcessUUIDs.size() > 0) {

      // submit one more runnable than the number of available threads to ensure
      // we are not looping for ever waiting for a thread to be available. This
      // way, a call to submit is blocking
      final int availableThreads = nbrOfThreads - threadPool.getActiveCount() + nbrOfThreads * 3;
      final int nbOfElementsTohandle = Math.min(availableThreads, nonLockedProcessUUIDs.size());

      final List processUUIDsToHandleInRandomOrder = new ArrayList(nbOfElementsTohandle);

      if (nonLockedProcessUUIDs.size() < availableThreads) {
        // we are going to process all of them, save the random time
        processUUIDsToHandleInRandomOrder.addAll(nonLockedProcessUUIDs);
      } else {
        final Random random = new Random(System.currentTimeMillis());
        for (int i = 0; i < nbOfElementsTohandle; i++) {
          final int indexToTake = random.nextInt(nonLockedProcessUUIDs.size());
          if (LOG.isLoggable(Level.FINE)) {
            LOG.fine("On " + getJobExecutorName() + ", selecting a processUUID for thread nb (loop): " + i
                + ", indexToTake: " + indexToTake);
          }
          final String selectedUUID = nonLockedProcessUUIDs.remove(indexToTake);
          if (!processUUIDsToHandleInRandomOrder.contains(selectedUUID)) {
            if (LOG.isLoggable(Level.FINE)) {
              LOG.fine("On " + getJobExecutorName() + ", adding processUUID for thread nb (loop): " + selectedUUID
                  + " to list of processUUIDs to handle");
            }
            processUUIDsToHandleInRandomOrder.add(selectedUUID);
          }
        }
      }

      final List processUUIDsToHandle = removeDuplicates(processUUIDsToHandleInRandomOrder);
      for (final String processUUID : processUUIDsToHandle) {
        final JobExecutorThread thread = new JobExecutorThread(getEventExecutor(), new ExecuteJobsCommand(
            getEventExecutor(), this, processUUID));
        if (LOG.isLoggable(Level.FINE)) {
          LOG.fine("On " + getJobExecutorName() + ", trying to process processUUID = " + processUUID);
        }
        // this call is blocking if we have reached the max capacity
        final Future runningThread = threadPool.submit(thread);
        runningThreads.put(processUUID, runningThread);
      }
    }
  }

  private List removeDuplicates(final List original) {
    final List result = new ArrayList();
    for (final String s : original) {
      if (!result.contains(s)) {
        result.add(s);
      }
    }
    return result;
  }

  private void cleanRunningThreads() {
    final List processUUIDsToRemove = new ArrayList();
    for (final Map.Entry> runningThread : runningThreads.entrySet()) {
      if (runningThread.getValue().isDone()) {
        processUUIDsToRemove.add(runningThread.getKey());
      }
    }
    for (final String processUUIDToRemove : processUUIDsToRemove) {
      runningThreads.remove(processUUIDToRemove);
    }
  }

  private void cleanLockedProcessUUIDs() {
    final long now = System.currentTimeMillis();
    final List processUUIDsToRemove = new ArrayList();
    for (final Map.Entry lockedProcessUUID : lockedProcessUUIDs.entrySet()) {
      if (lockedProcessUUID.getValue() < now) {
        processUUIDsToRemove.add(lockedProcessUUID.getKey());
      }
    }
    for (final String processUUIDToRemove : processUUIDsToRemove) {
      lockedProcessUUIDs.remove(processUUIDToRemove);
    }
  }

  protected Set getProcessUUIDsToExclude() {
    final Set processUUIDsToExclude = new HashSet();
    processUUIDsToExclude.addAll(runningThreads.keySet());
    processUUIDsToExclude.addAll(lockedProcessUUIDs.keySet());
    return processUUIDsToExclude;
  }

  protected void addLockedProcessUUID(final String processUUID) {
    final long now = System.currentTimeMillis();
    lockedProcessUUIDs.put(processUUID, now + lockIdleTime);
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy