package org.playorm.util.api.safethread;
import java.util.concurrent.Executor;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*/
public abstract class SafeRunnable implements Runnable
{
private static final Logger log = Logger.getLogger(SafeRunnable.class.getName());
private ExceptionListener listener = NullExceptionListener.singleton();
private Executor executor;
private String id;
public SafeRunnable(Executor exec, String id) {
this(exec, null, id);
}
public SafeRunnable(Executor exec, ExceptionListener listener, String id) {
if(id == null)
throw new IllegalArgumentException("id cannot be null");
if (listener != null)
this.listener = listener;
this.id = id;
this.executor = exec;
}
public void setExceptionListener(ExceptionListener handler)
{
if(handler!=null)
this.listener = handler;
else
listener= NullExceptionListener.singleton();
}
public void run()
{
try {
runImpl();
} catch(Throwable e) {
log.log(Level.WARNING, id+"Exception in media", e);
FireFailureOnClientThread r = new FireFailureOnClientThread(e, id);
//put on event thread so client can't tie up our threads!!!
executor.execute(r);
}
}
//NOTE: Cannot extend SafeRunnable as that could cause stackoverflow
//under the right conditions...ie. if the client keeps throwing an exception
//which is very likely if it threw an exception the first time you fired a failure
//to the client.
private class FireFailureOnClientThread implements Runnable {
private Throwable e;
private String id;
public FireFailureOnClientThread(Throwable e, String id) {
this.e = e;
this.id = id;
}
public void run() {
try {
listener.fireFailure(e, id);
} catch(Throwable e) {
log.log(Level.WARNING, id+"Client Exception", e);
}
}
}
protected abstract void runImpl() throws Throwable;
}