org.smpp.util.ProcessingThread Maven / Gradle / Ivy
/*
* Copyright (c) 1996-2001
* Logica Mobile Networks Limited
* All rights reserved.
*
* This software is distributed under Logica Open Source License Version 1.0
* ("Licence Agreement"). You shall use it and distribute only in accordance
* with the terms of the License Agreement.
*
*/
package org.smpp.util;
import org.smpp.SmppObject;
/**
* Implements Runnable
which does a repetitive processing in
* a cycle and can be started in a thread and stopped in more
* controlled manner than Thread
class.
* Provides final exception reporting (if there is any).
*
* @author Logica Mobile Networks SMPP Open Source Team
* @version $Revision: 1.1 $
*/
public abstract class ProcessingThread extends SmppObject implements Runnable {
/**
* The name for the thread as displayed in the debug file.
*/
private static final String PROCESSING_THREAD_NAME = "ProcThread";
/**
* The instancies of the class are indexed with this index.
*/
private static int threadIndex = 0;
/**
* Tells the thread that it should continue processing.
* Controls while-cycle in the run method. Can be assigned directly from
* the code in case of exceptional situation as oppose to the controlled
* stopping of the thread using the stop
method.
* @see #run()
*/
private boolean keepProcessing = true;
/**
* State variable indicating status of the thread. Don't confuse with
* keepProcessing
which stops the main loop, but
* can be set to false
far before the end of the loop
* using stopProcessing
method.
*
* @see #keepProcessing
* @see #run()
* @see #start()
* @see #stop()
* @see #setProcessingStatus(byte)
* @see #isInitialising()
* @see #isProcessing()
* @see #isFinished()
*/
private byte processingStatus = PROC_INITIALISING;
/**
* The processing thread is in initialisation phase.
* It's the phase after calling start
* but before entering while-loop in the run
method.
*
* @see #processingStatus
* @see #isInitialising()
*/
private static final byte PROC_INITIALISING = 0;
/**
* The processing thread is in running phase.
* It's the phase when the thread is in the while-loop
* in the process
method or method called from the loop
* that method.
*
* @see #processingStatus
* @see #isProcessing()
* @see #run()
*/
private static final byte PROC_RECEIVING = 1;
/**
* The processing thread is finished.
* The finished phase is phase when the thread has exited the while-loop
* in the run
method. It is possible to run it again
* by calling start
method again.
*
* @see #processingStatus
* @see #isFinished()
*/
private static final byte PROC_FINISHED = 2;
/**
* Object for monitoring the access to the processingStatus
* variable.
*/
private Object processingStatusLock = new Object();
/**
* Contains the last caught exception.
* As there is no means how to catch an exception thrown from the
* run
method, for case that it's necessary to examine an
* exception thrown in run
it is stored to this variable.
* Descendants of ProcessingThread
will use this variable
* to store the exception which will cause termination of the thread.
* It is also set by run
method if it'll catch an
* exception when calling process
.
* The exception is also set by the stopProcessing
* method.
*
* @see #stopProcessing(Exception)
* @see #setTermException(Exception)
* @see #getTermException()
*/
private Exception termException = null;
/**
* The thread which runs the code of this class.
*/
private Thread processingThread = null;
/**
* The method which is repeatedly called from the run
* method. This is supposed the hearth of the actual processing. The
* derived classes should implement their code in this method.
*/
public abstract void process();
/**
* Creates new thread and passes it this
as
* the Runnable
to run. Resets private variables to defaults.
* Starts the newly created thread.
*
* @see Thread
*/
public void start() {
debug.enter(DUTL, this, "start()");
if (!isProcessing()) { // i.e. is initialising or finished
setProcessingStatus(PROC_INITIALISING);
termException = null;
keepProcessing = true;
processingThread = new Thread(this);
processingThread.setName(generateIndexedThreadName());
processingThread.start();
while (isInitialising()) {
Thread.yield(); // we're waiting for the proc thread to start
}
}
debug.exit(DUTL, this);
}
/**
* Stops the receiving by setting flag keepProcessing
to false.
* Waits until the receiving is really stopped.
*/
public void stop() {
debug.enter(DUTL, this, "stop()");
if (isProcessing()) {
stopProcessing(null);
while (!isFinished()) {
Thread.yield(); // we're waiting for the proc thread to stop
}
}
debug.exit(DUTL, this);
}
/**
* Causes stoping of the while-loop in the run
method.
* Called from stop
method or can be used to terminate
* the processing thread in case of an exceptional situation while
* processing (from process
method.)
*/
protected void stopProcessing(Exception e) {
setTermException(e);
keepProcessing = false;
}
/**
* Calls process
in cycle until stopped.
* This method is called from Thread
's code as the code which
* has to be executed by the thread.
*
* @see Thread
*/
public void run() {
debug.enter(DUTL, this, "run()");
try {
setProcessingStatus(PROC_RECEIVING);
while (keepProcessing) {
process();
Thread.yield();
}
} catch (Exception e) {
setTermException(e);
debug.write("ProcessingThread.run() caught unhadled exception " + e);
event.write(e, "ProcessingThread.run() unhadled exception");
} finally {
setProcessingStatus(PROC_FINISHED);
debug.exit(DUTL, this);
}
}
/**
* Should return the name for the thread. Derived classes are expected
* to return specific name here from this method.
*/
public String getThreadName() {
return PROCESSING_THREAD_NAME;
}
/**
* In case there are multiple instancies of the class this generates
* and returns instance index.
*/
public int getThreadIndex() {
return ++threadIndex;
}
/**
* Uses getThreadName
and getThreadIndex
* to generate unique name for the thread. Called during initialisation
* of the thread.
*/
public String generateIndexedThreadName() {
return getThreadName() + "-" + getThreadIndex();
}
/**
* As there is no means how to catch an exception thrown from
* run
method, in case that it's necessary to throw an
* exception it's rather remembered by calling of this method.
*
* @param e the exception to remember
*/
protected void setTermException(Exception e) {
termException = e;
}
/**
* Returns the last exception caught during processing.
*
* @return the last exception caught during processing
*/
public Exception getTermException() {
return termException;
}
/**
* Sets the processingStatus
to value provided.
*/
private void setProcessingStatus(byte value) {
synchronized (processingStatusLock) {
processingStatus = value;
}
}
/**
* Returns if the processingStatus
indicates that
* the receiving is in initialisation stage, i.e. the processing loop
* has not been entered yet.
*/
private boolean isInitialising() {
synchronized (processingStatusLock) {
return processingStatus == PROC_INITIALISING;
}
}
/**
* Returns if the processingStatus
indicates that
* the receiving has started, but it didn't finished yet, i.e. the
* the thread is still in the processing loop.
*/
private boolean isProcessing() {
synchronized (processingStatusLock) {
return processingStatus == PROC_RECEIVING;
}
}
/**
* Returns if the processingStatus
indicates that
* the receiving has finished, i.e. the processing loop
* has finished.
*/
private boolean isFinished() {
synchronized (processingStatusLock) {
return processingStatus == PROC_FINISHED;
}
}
}
/*
* $Log: not supported by cvs2svn $
*/