// Copyright 2006 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.enterprise.connector.scheduler; import com.google.enterprise.connector.instantiator.Instantiator; import com.google.enterprise.connector.logging.NDC; import com.google.enterprise.connector.persist.ConnectorNotFoundException; import java.util.logging.Level; import java.util.logging.Logger; /** * Scheduler that schedules connector traversal. This class is thread safe. * Must initialize TraversalScheduler before running it. * * <p> This facility includes a schedule thread that runs a loop. * Each iteration it asks the instantiator for the schedule * for each Connector Instance and runs batches for those that * are * <OL> * <LI> scheduled to run. * <LI> have not exhausted their quota for the current time interval. * <LI> are not currently running. * </OL> * The implementation must handle the situation that a Connector * Instance is running. */ /* TODO (bmj): This should be removed and its functionality moved to * ConnectorCoordinator. Connectors should be in charge of their own * scheduling. In particular, the ConnectorCoordinator could leverage * Schedule.nextScheduledInterval(), sleeping when unscheduled rather * than checking all schedules once per second. */ public class TraversalScheduler implements Runnable { public static final String SCHEDULER_CURRENT_TIME = "/Scheduler/currentTime"; private static final Logger LOGGER = Logger.getLogger(TraversalScheduler.class.getName()); private final Instantiator instantiator; private boolean isInitialized; // Protected by instance lock. private boolean isShutdown; // Protected by instance lock. /** * Create a scheduler object. * * @param instantiator used to get schedule for connector instances */ public TraversalScheduler(Instantiator instantiator) { this.instantiator = instantiator; this.isInitialized = false; this.isShutdown = false; } public synchronized void init() { if (isInitialized) { return; } isInitialized = true; isShutdown = false; new Thread(this, "TraversalScheduler").start(); } public synchronized void shutdown() { if (isShutdown) { return; } isInitialized = false; isShutdown = true; } /** * Determines whether scheduler should run. * * @return true if we are in a running state and scheduler should run or * continue running. */ private synchronized boolean isRunningState() { return isInitialized && !isShutdown; } private void scheduleBatches() { for (String connectorName : instantiator.getConnectorNames()) { NDC.pushAppend(connectorName); try { instantiator.startBatch(connectorName); } catch (ConnectorNotFoundException e) { // Looks like the connector just got deleted. Don't schedule it. } finally { NDC.pop(); } } } public void run() { NDC.push("Traverse"); try { while (true) { try { if (!isRunningState()) { LOGGER.info("TraversalScheduler thread is stopping due to " + "shutdown or not being initialized."); return; } scheduleBatches(); // Give someone else a chance to run. try { synchronized (this) { wait(1000); } } catch (InterruptedException e) { // May have been interrupted for shutdown. } } catch (Throwable t) { LOGGER.log(Level.SEVERE, "TraversalScheduler caught unexpected Throwable: ", t); } } } finally { NDC.remove(); } } }