/*******************************************************************************
* ALMA - Atacama Large Millimiter Array
* (c) European Southern Observatory, 2002
* Copyright by ESO (in the framework of the ALMA collaboration)
* and Cosylab 2002, All rights reserved
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package alma.ACS.jbaci;
import java.util.ArrayList;
import java.util.LinkedList;
import alma.ACS.CBDescIn;
import alma.ACS.CBDescOut;
import alma.ACS.CBvoid;
import alma.ACS.Callback;
import alma.ACSErr.Completion;
/**
* BACI dispatch action.
* @author <a href="mailto:matej.sekoranjaATcosylab.com">Matej Sekoranja</a>
* @version $id$
*/
public class BACIDispatchAction implements PrioritizedRunnable {
/**
* Dispatch failed listener interface.
*/
public interface DispatchFailedListener
{
/**
* Called when dispatching of particulat request failed.
* @param action instance of <code>BACIDispatchAction</code> dispatching request.
* @param failedRequest failed request.
*/
public void dispatchFailed(BACIDispatchAction action, DispatchRequest failedRequest);
}
/**
* Class containing request data.
*/
public class DispatchRequest
{
/**
* No value constructor.
* @param type type of callback (working/done).
* @param completion completion.
*/
public DispatchRequest(int type, Completion completion)
{
this(type, completion, null);
}
/**
* No value constructor.
* @param type type of callback (working/done).
* @param completion completion.
* @param value value.
*/
public DispatchRequest(int type, Completion completion, Object value)
{
this.type = type;
this.completion = completion;
this.value = value;
}
/**
* Return value.
*/
public Object value;
/**
* Completion.
*/
public Completion completion;
/**
* Callback type.
* @see CallbackDispatcher#DONE_TYPE
* @see CallbackDispatcher#WORKING_TYPE
*/
public int type;
}
/**
* Action priority.
*/
protected BACIPriority priority;
/**
* Callback out descriptor.
*/
protected CBDescOut descOut;
/**
* Callback.
*/
protected Callback callback;
/**
* Callback dispatcher (used to dispatch non-CBvoid callbacks).
*/
protected CallbackDispatcher callbackDispatcher;
/**
* Failure limit.
*/
protected static final int DEFAULT_FAILURE_COUNT_LIMIT = 3;
/**
* Failure limit (number of retries), 0 means until successfull.
*/
protected int failureCountLimit;
/**
* Failure count.
*/
protected int failureCount = 0;
/**
* Ordered dispatch queue.
*/
protected LinkedList queue = new LinkedList();
/**
* Pending request.
* NOTE: synchronized on <code>queue</code>'s monitor.
*/
protected DispatchRequest pendingRequest = null;
/**
* Flag indicating pending submit.
* NOTE: synchronized on <code>queue</code>'s monitor.
*/
protected boolean submitPending = false;
/**
* Override policy for request queue.
* If <code>true</code> newer request will override request in the queue.
* If <code>false</code> (default) requests will form a linked list.
*/
protected boolean overridePolicy = false;
/**
* List of registered listeners.
*/
protected ArrayList<DispatchFailedListener> listeners = new ArrayList<DispatchFailedListener>();
/**
* Constructor of NORMAL priority action (CBvoid callback).
* @param callback action callback.
* @param descIn action callback in descriptor.
*/
public BACIDispatchAction(CBvoid callback, CBDescIn descIn)
{
this(callback, descIn, (CallbackDispatcher)null);
}
/**
* Constructor of NORMAL priority action.
* @param callback action callback.
* @param descIn action callback in descriptor.
* @param callbackDispatcher callback dispatcher (value dependend).
*/
public BACIDispatchAction(Callback callback, CBDescIn descIn,
CallbackDispatcher callbackDispatcher)
{
this(callback, descIn, callbackDispatcher, BACIPriority.NORMAL);
}
/**
* Constructor.
* @param callback action callback.
* @param descIn action in descriptor.
* @param priority action priority.
*/
public BACIDispatchAction(CBvoid callback, CBDescIn descIn, BACIPriority priority)
{
this(callback, descIn, null, priority);
}
/**
* Constructor.
* @param callback action callback.
* @param descIn action in descriptor.
* @param callbackDispatcher callback dispatcher (value dependend).
* @param priority action priority.
*/
public BACIDispatchAction(Callback callback, CBDescIn descIn,
CallbackDispatcher callbackDispatcher, BACIPriority priority)
{
this.callback = callback;
this.callbackDispatcher = callbackDispatcher;
this.priority = priority;
descOut = generateCBDescOut(descIn);
failureCountLimit = DEFAULT_FAILURE_COUNT_LIMIT;
}
/**
* Added working callback request to dispatch queue.
* @param completion completion.
* @param value value.
*/
public void dispatchWorkingRequest(Completion completion)
{
dispatchRequest(CallbackDispatcher.WORKING_TYPE, completion, null);
}
/**
* Added working callback request to dispatch queue.
* @param completion completion.
*/
public void dispatchWorkingRequest(Completion completion, Object value)
{
dispatchRequest(CallbackDispatcher.WORKING_TYPE, completion, value);
}
/**
* Added done callback request to dispatch queue.
* @param completion completion.
*/
public void dispatchDoneRequest(Completion completion)
{
dispatchRequest(CallbackDispatcher.DONE_TYPE, completion, null);
}
/**
* Added done callback request to dispatch queue.
* @param completion completion.
* @param value value.
*/
public void dispatchDoneRequest(Completion completion, Object value)
{
dispatchRequest(CallbackDispatcher.DONE_TYPE, completion, value);
}
/**
* Added request to dispatch queue.
*/
public void dispatchRequest(int type, Completion completion, Object value)
{
dispatchRequest(new DispatchRequest(type, completion, value));
}
/**
* Added request to dispatch queue (internal).
*/
protected void dispatchRequest(DispatchRequest request)
{
synchronized (queue)
{
// override previous requests in the queue, if override policy is set
// (there should be max. one request in the queue anyway)
if (overridePolicy)
queue.clear();
// add to queue
queue.add(request);
// initiate submit to dispatcher queue, if necessary
if (!submitPending)
submit();
}
}
/**
* @see alma.ACS.jbaci.PrioritizedRunnable#getPriority()
*/
public BACIPriority getPriority() {
return priority;
}
/**
* Generates callback out descriptor from in descriptor.
* @param descIn callback in descriptor.
* @return generated callback out descriptor.
*/
protected static CBDescOut generateCBDescOut(CBDescIn descIn)
{
return new CBDescOut(0, descIn.id_tag);
}
/**
* NOT TO BE CHANGED.
* Dispatching is done one by one - to achieve better fairness.
*/
public final void run()
{
// get request
synchronized (queue)
{
if (pendingRequest != null)
// TODO log error
// TODO tmp
System.err.println("IllegalStateException(pendingRequest != null)");
if (!submitPending)
// TODO log error
// TODO tmp
System.err.println("IllegalStateException(!submitPending)");
pendingRequest = (DispatchRequest)queue.removeFirst();
}
// resubmit?
if (!dispatch())
{
if (failed())
{
// TODO log dispatch failure here
// notify
if (listeners.size() != 0)
{
DispatchFailedListener[] listenerArray = null;
synchronized (listeners)
{
listenerArray = new DispatchFailedListener[listeners.size()];
listeners.toArray(listenerArray);
}
for (int i = 0; i < listenerArray.length; i++)
{
try
{
listenerArray[i].dispatchFailed(this, pendingRequest);
}
catch (Throwable th)
{
// TODO or even log here
th.printStackTrace();
}
}
}
}
else
{
// retry...
synchronized (queue)
{
// add again to the queue, but not if override policy is set
// (if set, older request should not override newer)
if (!isOverridePolicy() || queue.isEmpty())
queue.addFirst(pendingRequest);
}
}
}
else
{
// reset failure counter on success
failureCount = 0;
}
// this line will always be reached - up code is (has to be) exception safe
synchronized (queue)
{
// reset
pendingRequest = null;
submitPending = false;
// submit (BACIDispatcher.execute()) could block...
// if non-blocking policy was used, would not be OK - there is a StackOutOfBounds risk...
// But since there is abort policy used, this will create a new thread which will block...
if (!queue.isEmpty())
submit();
}
}
/**
* Call this metod to notify dispatch failure.
* @return <code>true</code>, if dispaching is to be canceled (e.g. failure counter reached its failure limit)
*/
protected boolean failed()
{
if (failureCountLimit == 0)
return false;
else
return (++failureCount >= failureCountLimit);
}
/**
* Dispatch method.
* Dispatches <code>pendingRequest</code>, should be non-<code>null</code>.
* @return <code>true</code> if successfully dispatched.
*/
protected boolean dispatch()
{
try
{
if (callbackDispatcher == null)
{
((CBvoid)callback).done(pendingRequest.completion, descOut);
return true;
}
else
return callbackDispatcher.dispatchCallback(pendingRequest.type,
pendingRequest.value, callback,
pendingRequest.completion, descOut);
}
catch (Throwable th)
{
// TODO log
th.printStackTrace();
return false;
}
}
/**
* Sumbit action to be <code>BACIExecutor<code> to be executed.
*/
protected void submit()
{
synchronized (queue)
{
if (submitPending)
return;
// non blocking...
submitPending = BACIFramework.INSTANCE.getDispatcher().execute(this);
}
}
/**
* Get callback out-descriptor.
* @return callback out-descriptor.
*/
public CBDescOut getDescOut() {
return descOut;
}
/**
* Get current override policy.
* @return current override policy.
*/
public boolean isOverridePolicy() {
return overridePolicy;
}
/**
* Set current override policy.
* @param b override policy to set.
*/
public void setOverridePolicy(boolean b) {
overridePolicy = b;
}
/**
* Set dispatching priority.
* @param priority dispatching priority to set.
*/
public void setPriority(BACIPriority priority) {
this.priority = priority;
}
/**
* Add dispatch failed listener.
* @param listener listener to listen failed dispatch notifications.
*/
public void addDispatchFailedListener(DispatchFailedListener listener)
{
synchronized (listeners)
{
if (!listeners.contains(listener))
listeners.add(listener);
}
}
/**
* Remove dispatch failed listener.
* @param listener listener to remove.
*/
public void removeDispatchFailedListener(DispatchFailedListener listener)
{
synchronized (listeners)
{
listeners.remove(listener);
}
}
}