/* * Copyright (c) 2015 EMC Corporation * All Rights Reserved */ package com.emc.sa.engine.scheduler; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.emc.sa.engine.ExecutionEngineDispatcher; import com.emc.storageos.db.client.model.uimodels.Order; @Component public class Scheduler implements Runnable { private static final Logger LOG = Logger.getLogger(Scheduler.class); private static final long ONE_MINUTE = 60000; private int threadCount = 5; private volatile Thread thread; private ExecutorService pool; @Autowired private SchedulerDataManager dataManager; @Autowired private ExecutionEngineDispatcher dispatcher; public int getThreadCount() { return threadCount; } public void setThreadCount(int threadCount) { this.threadCount = threadCount; } /** * Runs the scheduler. */ @Override public void run() { startProcessor(); try { pollActiveWindows(); } finally { stopProcessor(); } } /** * Stops the scheduler. */ public void stop() { Thread t = thread; thread = null; stopProcessor(); if (t != null) { t.interrupt(); try { t.join(); } catch (InterruptedException e) { LOG.info("Interrupted while waiting for completion", e); Thread.currentThread().interrupt(); } } } /** * Determines if the scheduler is running. * * @return true if the scheduler is running. */ private boolean isRunning() { return thread != null; } /** * Starts the order processor. */ protected void startProcessor() { thread = Thread.currentThread(); pool = Executors.newFixedThreadPool(threadCount); for (int i = 0; i < threadCount; i++) { pool.execute(new OrderProcessor()); } } /** * Stops processing orders and wait for processing to finish. */ protected void stopProcessor() { thread = null; pool.shutdownNow(); try { if (!pool.awaitTermination(1, TimeUnit.MINUTES)) { LOG.warn("Failed to gracefully shutdown"); } } catch (InterruptedException e) { LOG.info("Interrupted while waiting for stop", e); Thread.currentThread().interrupt(); } } /** * Polls the active windows every minute until the scheduler is stopped. */ protected void pollActiveWindows() { try { while (isRunning()) { long nextTime = System.currentTimeMillis() + ONE_MINUTE; dataManager.updateActiveWindows(); long sleepTime = nextTime - System.currentTimeMillis(); if (sleepTime > 0) { Thread.sleep(sleepTime); } } } catch (InterruptedException e) { LOG.info("Interrupted waiting for an execution window"); Thread.currentThread().interrupt(); } } /** * Runnable that handles processing orders. * * @author jonnymiller */ private class OrderProcessor implements Runnable { @Override public void run() { LOG.info("Started OrderProcessor"); try { while (isRunning()) { Order order = dataManager.lockNextScheduledOrder(); try { processOrder(order); } finally { dataManager.unlockOrder(order); } } } catch (InterruptedException e) { LOG.info("Interruped waiting for an order"); Thread.currentThread().interrupt(); } LOG.info("Finished OrderProcessor"); } protected void processOrder(Order order) { try { LOG.info("Processing scheduled order: " + order.getId()); if (!isRunning()) { LOG.info("Processing shutdown, terminating"); return; } dispatcher.processOrder(order); } catch (Exception e) { LOG.error("Unexpected exception processing order: " + order.getId(), e); } } } }