package edu.sc.seis.sod.status;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import edu.iris.Fissures.model.TimeInterval;
import edu.iris.Fissures.model.UnitImpl;
import edu.sc.seis.fissuresUtil.database.ConnMgr;
import edu.sc.seis.fissuresUtil.exceptionHandler.GlobalExceptionHandler;
import edu.sc.seis.sod.Arm;
import edu.sc.seis.sod.ArmListener;
import edu.sc.seis.sod.ConfigurationException;
import edu.sc.seis.sod.NetworkArm;
import edu.sc.seis.sod.PeriodicCheckpointer;
import edu.sc.seis.sod.Start;
import edu.sc.seis.sod.hibernate.SodDB;
public class OutputScheduler extends Thread implements ArmListener {
private OutputScheduler() {
super("OutputScheduler");
Start.add(this);
}
public void starting(Arm arm) {
synchronized(arms) {
arms.add(arm);
// Wake up when we get our first arm
if(arms.size() == 1) {
synchronized(this) {
notify();
}
}
}
if(arm instanceof NetworkArm) {
((NetworkArm)arm).add(this);
}
}
public void finished(Arm arm) {
synchronized(this) {
notifyAll();
}
}
public void started() throws ConfigurationException {}
public void schedule(Runnable a) {
synchronized(runnables) {
runnables.add(a);
}
}
public void scheduleForExit(Runnable a) {
synchronized(onExitRunnables) {
onExitRunnables.add(a);
}
}
public void run() {
// Waiting for the initial arm to come into starting and wake us
try {
synchronized(this) {
wait();
}
} catch(InterruptedException e) {}
while(true) {
runAll(runnables);
if(Start.isArmFailure() || !anyArmsActive()) {
runAll(runnables);
runAll(onExitRunnables);
if (ConnMgr.getDB_TYPE().equals(ConnMgr.HSQL) && Start.getRunProps().checkpointPeriodically()) {
new PeriodicCheckpointer().run();
}
logger.debug("Output Scheduler done.");
logger.info("Lo! I am weary of my wisdom, like the bee that hath gathered too much\n"
+ "honey; I need hands outstretched to take it.");
String lcOSName = System.getProperty("os.name").toLowerCase();
boolean MAC_OS_X = lcOSName.startsWith("mac os x");
if(MAC_OS_X) {
// hopefully everything is done!
try {
Connection conn = ConnMgr.createConnection();
conn.createStatement().execute("shutdown");
conn.close();
} catch(SQLException e) {
GlobalExceptionHandler.handle(e);
}
logger.debug("Using System.exit(0) only on the mac due to AWT thread not exiting.");
System.exit(0);
}
return;
}
try {
synchronized(this) {
if ( ! Start.isArmFailure()) {
wait(ACTION_INTERVAL_MILLIS);
}
}
} catch(InterruptedException e) {}
}
}
private void runAll(Set toRun) {
Runnable[] currentRunnables = new Runnable[0];
synchronized(toRun) {
currentRunnables = (Runnable[])toRun.toArray(currentRunnables);
toRun.clear();
}
for(int i = 0; i < currentRunnables.length && ! Start.isArmFailure(); i++) {
try {
currentRunnables[i].run();
} catch(Throwable t) {
GlobalExceptionHandler.handle(t);
}
}
// just make sure any read-only database connections are closed
SodDB.rollback();
}
private boolean anyArmsActive() {
Arm[] curArms = new Arm[0];
synchronized(arms) {
curArms = (Arm[])arms.toArray(curArms);
}
boolean active = false;
for(int i = 0; i < curArms.length; i++) {
if(curArms[i].isActive()) {
active = true;
break;
}
}
return active;
}
public synchronized static OutputScheduler getDefault() {
if(DEFAULT == null) {
DEFAULT = new OutputScheduler();
DEFAULT.start();
}
return DEFAULT;
}
private static final TimeInterval ACTION_INTERVAL = new TimeInterval(10,
UnitImpl.SECOND);
private static final long ACTION_INTERVAL_MILLIS = (long)ACTION_INTERVAL.convertTo(UnitImpl.MILLISECOND)
.get_value();
private static OutputScheduler DEFAULT = null;
private Set arms = Collections.synchronizedSet(new HashSet());
private Set runnables = Collections.synchronizedSet(new HashSet());
private Set onExitRunnables = Collections.synchronizedSet(new HashSet());
private static final Logger logger = LoggerFactory.getLogger(OutputScheduler.class);
}