Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
jadex.base.service.message.streams.AbstractConnectionHandler Maven / Gradle / Ivy
package jadex.base.service.message.streams;
import jadex.base.service.message.AbstractSendTask;
import jadex.base.service.message.MessageService;
import jadex.base.service.message.MessageService.SendManager;
import jadex.base.service.message.transport.ITransport;
import jadex.bridge.IComponentIdentifier;
import jadex.bridge.IComponentStep;
import jadex.bridge.IInternalAccess;
import jadex.bridge.service.types.message.ICodec;
import jadex.commons.future.DelegationResultListener;
import jadex.commons.future.ExceptionDelegationResultListener;
import jadex.commons.future.Future;
import jadex.commons.future.IFuture;
import jadex.commons.future.IResultListener;
import jadex.commons.gui.PropertiesPanel;
import jadex.commons.gui.SGUI;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;
import java.util.Map;
import java.util.TimerTask;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.Timer;
/**
* Abstract base class for connection handlers.
*
* Is called from the message service and the connection.
*
* Ensures that all calls from threads other than the message service component
* are scheduled on this thread to avoid multithreading issues in this class.
*/
public class AbstractConnectionHandler implements IAbstractConnectionHandler
{
//-------- attributes --------
/** The message service. */
protected MessageService ms;
/** The connection. */
protected AbstractConnection con;
/** The latest alive time. */
protected long alivetime;
/** The lease time. */
protected long leasetime;
/** The unacknowledged messages. */
protected Map unacked;
/** The maximum number of resends. */
protected int maxresends;
/** The max delay before an acknowledgement is received. */
protected long acktimeout;
/** The non-functional properties. */
protected Map nonfunc;
//-------- constructors --------
/**
* Create a new connection handler.
*/
public AbstractConnectionHandler(MessageService ms, Map nonfunc)
{
this(ms, nonfunc, 3, 10000, 15000);
}
/**
* Create a new connection handler.
*/
public AbstractConnectionHandler(MessageService ms, Map nonfunc, int maxresends, long acktimeout, long leasetime)
{
this.ms = ms;
this.nonfunc = nonfunc;
this.maxresends = maxresends;
this.acktimeout = acktimeout;
this.leasetime = leasetime;
this.alivetime = System.currentTimeMillis();
this.unacked = new HashMap();
// createGui();
}
/**
* Set the connection (needed as connection and handler need each other).
* The connections uses this method to set itself as connection in their constructor.
*/
// Cannot be decoupled, otherwise connection may still be null
public void setConnection(final AbstractConnection con)
{
// scheduleStep(new IComponentStep()
// {
// public IFuture execute(IInternalAccess ia)
// {
AbstractConnectionHandler.this.con = con;
// return IFuture.DONE;
// }
// });
}
//-------- methods called from message service --------
// All calls from the message service may occur on different threads
// Hence they are all routed to the same component thread
/**
* Received the init message.
*/
public void initReceived()
{
scheduleStep(new IComponentStep()
{
public IFuture execute(IInternalAccess ia)
{
con.setInited();
sendTask(createTask(StreamSendTask.ACKINIT, null, null, nonfunc));
return IFuture.DONE;
}
});
}
/**
* Called when an ack was received.
* The id is used to identify the original message task.
* This will cancel the timer (resending) task
* and call the future of sendAcknowledgedMessage().
*/
public void ackReceived(final Object id, final Object content)
{
scheduleStep(new IComponentStep()
{
public IFuture execute(IInternalAccess ia)
{
SendInfo si = unacked.remove(id);
if(si!=null)
{
// System.out.println("received ack: "+si.getId()+" "+si.getTryCnt());
si.getTimer().cancel();
si.getResult().setResult(content);
}
return IFuture.DONE;
}
});
}
/**
* Close the connection.
* Notifies the other side that the connection has been closed.
*/
public void close()
{
scheduleStep(new IComponentStep()
{
public IFuture execute(IInternalAccess ia)
{
con.close();
return IFuture.DONE;
}
});
}
/**
* Set the alive time of the other connection side.
* @param alivetime The alive time.
*/
public void setAliveTime(final long alivetime)
{
scheduleStep(new IComponentStep()
{
public IFuture execute(IInternalAccess ia)
{
// System.out.println("new lease: "+alivetime);
AbstractConnectionHandler.this.alivetime = alivetime;
return IFuture.DONE;
}
});
}
/**
* Test if the connection is alive.
* @return True, if is alive.
*/
// Is already called from correct message service thread
public boolean isConnectionAlive()
{
boolean isalive = System.currentTimeMillis() doClose()
{
return IFuture.DONE;
}
/**
* Send init message.
*/
public IFuture sendInit()
{
return scheduleStep(new IComponentStep()
{
public IFuture execute(IInternalAccess ia)
{
final Future ret = new Future();
try
{
InitInfo ii = new InitInfo(getConnection().getInitiator(), getConnection().getParticipant(), nonfunc);
AbstractSendTask task = createTask(StreamSendTask.INIT, ii, true, null, nonfunc);
sendAcknowledgedMessage(task, StreamSendTask.INIT).addResultListener(new ExceptionDelegationResultListener(ret)
{
public void customResultAvailable(Object result)
{
ret.setResult(null);
}
});
}
catch(Exception e)
{
ret.setException(e);
}
return ret;
}
});
}
/**
* Send alive message.
*/
public IFuture sendAlive()
{
return scheduleStep(new IComponentStep()
{
public IFuture execute(IInternalAccess ia)
{
final Future ret = new Future();
try
{
AbstractSendTask task = createTask(StreamSendTask.ALIVE, null, null, null);
sendTask(task);
task.getFuture().addResultListener(new DelegationResultListener(ret));
}
catch(Exception e)
{
ret.setException(e);
}
return ret;
}
});
}
/**
*
*/
public void notifyInited()
{
}
//-------- internal methods --------
/**
* Get the connection.
* @return The connection.
*/
protected AbstractConnection getConnection()
{
return con;
}
/**
* Get the codecs.
* @return The codecs.
*/
protected ICodec[] getCodecs()
{
return ms.getMessageCodecs(ms.getCodecFactory().getDefaultCodecIds());
}
/**
* Get the codec ids.
* @return The codec ids.
*/
protected byte[] getCodecIds()
{
return ms.getCodecFactory().getDefaultCodecIds();
}
/**
* Get the transports.
* @return The transports.
*/
protected ITransport[] getTransports()
{
return ms.getTransports();
}
/**
* Get the non-functional properties.
* @return The non-functional properties.
*/
public Map getNonFunctionalProperties()
{
return nonfunc;
}
/**
* Set the non-functional properties.
* @param nonfunc The non-functional properties.
*/
public void setNonFunctionalProperties(Map nonfunc)
{
this.nonfunc = nonfunc;
}
/**
* Get the message type for a given type, e.g. init, close (defined in StreamSendTask).
* @param type The message type.
* @return The message type for message sending.
*/
protected byte getMessageType(String type)
{
// Connection type is determined by initiator and is constant per connection
boolean contype = getConnection().isInitiatorSide()? getConnection().isInputConnection(): !getConnection().isInputConnection();
return StreamSendTask.getMessageType(type, contype, getConnection().isInitiatorSide());
}
/**
* Create a new task.
* @param type The message type.
* @param content The content.
* @param seqnumber The sequence number.
* @return The task for sending the message.
*/
protected AbstractSendTask createTask(String type, byte[] content, Integer seqnumber, Map nonfunc)
{
return createTask(type, content, false, seqnumber, nonfunc);
}
/**
* Create a new task.
* @param type The message type.
* @param content The content.
* @param usecodecs Flag if codecs should be used to encode the content.
* @param seqnumber The sequence number.
* @return The task for sending the message.
*/
protected AbstractSendTask createTask(String type, Object content, boolean usecodecs, Integer seqnumber, Map nonfunc)
{
return new StreamSendTask(getMessageType(type), content==null? StreamSendTask.EMPTY_BYTE_ARRAY: content,
getConnectionId(), getConnection().isInitiatorSide()? new IComponentIdentifier[]{getConnection().getParticipant()}: new IComponentIdentifier[]{getConnection().getInitiator()},
getTransports(), usecodecs? getCodecs(): null, seqnumber, nonfunc);
}
/**
* Send a task.
* @param task The task.
*/
protected void sendTask(final AbstractSendTask task)
{
// System.out.println("sendTask: "+SUtil.arrayToString(task.getProlog())+" "+task.getData().length);
IComponentIdentifier[] recs = task.getReceivers();
if(recs.length!=1)
throw new RuntimeException("Must have exactly one receiver.");
SendManager sm = ms.getSendManager(recs[0]);
sm.addMessage(task);
task.getFuture().addResultListener(new IResultListener()
{
public void resultAvailable(Void result)
{
// if(task instanceof StreamSendTask)
// {
// System.out.println("ack send fini: "+((StreamSendTask)task).getSequenceNumber()+" "+System.currentTimeMillis());
// }
// System.out.println("sent: "+SUtil.arrayToString(task.getProlog()));
// nop if could be sent
}
public void exceptionOccurred(Exception exception)
{
// close connection in case of send error.
// getConnection().setClosed();
}
});
}
/**
* Send a message and wait for an ack.
* Automatically resends messages () if not is ack is received within acktimeout.
* @param task The send task.
* @param id The id of the call (e.g. sequence number).
* @return Exception if sending and resending failed.
*/
protected IFuture sendAcknowledgedMessage(AbstractSendTask task, Object id)
{
SendInfo si = unacked.get(id);
Future ret;
sendTask(task); // .addResultListener(new DelegationResultListener(fut));
if(si==null)
{
ret = new Future();
si = new SendInfo(task, id, 1, createAckTimer(id), ret);
unacked.put(id, si);
}
else
{
ret = si.getResult();
si.setTryCnt(si.getTryCnt()+1);
si.setTimer(createAckTimer(id));
}
// System.out.println("sent msg: "+si.getId()+" "+si.getTryCnt()+" "+SUtil.arrayToString(task.getReceivers()));
return ret;
}
/**
* Get the leasetime.
* @return the leasetime.
*/
protected long getLeasetime()
{
return leasetime;
}
/**
* Schedule a step on the message service component.
*/
protected IFuture scheduleStep(IComponentStep step)
{
return ms.getComponent().scheduleStep(step);
}
/**
* Triggers resends of packets if no ack has been received in acktimeout.
* @param id The message id.
* @return The timer.
*/
protected TimerTask createAckTimer(final Object id)
{
// Test if packets have been sent till last timer was inited
return ms.waitForRealDelay(acktimeout, new IComponentStep()
{
public IFuture execute(IInternalAccess ia)
{
SendInfo si = unacked.get(id);
if(si!=null)
{
if(si.getTryCnt()>=maxresends)
{
// System.out.println("Message could not be sent.");
si.getResult().setException(new RuntimeException("Message could not be sent."));
unacked.remove(id);
}
else
{
sendAcknowledgedMessage(si.getTask(), id);
createAckTimer(id);
}
}
return IFuture.DONE;
}
});
// ITimer ret = ms.getClockService().createTimer(, new ITimedObject()
// {
// public void timeEventOccurred(long currenttime)
// {
// try
// {
// scheduleStep(new IComponentStep()
// {
// public IFuture execute(IInternalAccess ia)
// {
// SendInfo si = unacked.get(id);
// if(si!=null)
// {
// if(si.getTryCnt()>=maxresends)
// {
// System.out.println("Message could not be sent.");
// si.getResult().setException(new RuntimeException("Message could not be sent."));
// unacked.remove(id);
// }
// else
// {
// sendAcknowledgedMessage(si.getTask(), id);
// createAckTimer(id);
// }
// }
// return IFuture.DONE;
// }
// });
// }
// catch(ComponentTerminatedException e)
// {
// // nop
// }
// }
// });
// return ret;
}
/**
* Struct class that holds send information.
*/
public static class SendInfo
{
//-------- attributes --------
/** The send task. */
protected AbstractSendTask task;
/** The id. */
protected Object id;
/** The number of timer this task has been executed. */
protected int trycnt;
/** The timer for triggering resends. */
protected TimerTask timer;
/** The result future of the call. */
protected Future result;
//-------- constructors --------
/**
* Create a new send info.
*/
public SendInfo(AbstractSendTask task, Object id, int trycnt,
TimerTask timer, Future result)
{
this.task = task;
this.id = id;
this.trycnt = trycnt;
this.timer = timer;
this.result = result;
}
//-------- methods --------
/**
* Get the task.
* @return the task.
*/
public AbstractSendTask getTask()
{
return task;
}
/**
* Set the task.
* @param task The task to set.
*/
public void setTask(AbstractSendTask task)
{
this.task = task;
}
/**
* Get the id.
* @return the id.
*/
public Object getId()
{
return id;
}
/**
* Set the id.
* @param id The id to set.
*/
public void setId(Object id)
{
this.id = id;
}
/**
* Get the trycnt.
* @return the trycnt.
*/
public int getTryCnt()
{
return trycnt;
}
/**
* Set the trycnt.
* @param trycnt The trycnt to set.
*/
public void setTryCnt(int trycnt)
{
this.trycnt = trycnt;
}
/**
* Get the timer.
* @return the timer.
*/
public TimerTask getTimer()
{
return timer;
}
/**
* Set the timer.
* @param timer The timer to set.
*/
public void setTimer(TimerTask timer)
{
this.timer = timer;
}
/**
* Get the result.
* @return the result.
*/
public Future getResult()
{
return result;
}
/**
* Set the result.
* @param result The result to set.
*/
public void setResult(Future result)
{
this.result = result;
}
}
/**
*
*/
public void createGui()
{
JFrame f = new JFrame();
f.setLayout(new BorderLayout());
f.add(createPanel(), BorderLayout.CENTER);
f.setLocation(SGUI.calculateMiddlePosition(f));
f.pack();
f.setVisible(true);
}
/**
*
*/
protected JPanel createPanel()
{
return new ConnectionPanel();
}
/**
*
*/
public class ConnectionPanel extends JPanel
{
/**
*
*/
public ConnectionPanel()
{
PropertiesPanel pp = new PropertiesPanel("Connection properties");
final JTextField tfalivetime = pp.createTextField("alivetime");
final JTextField tfleasetime = pp.createTextField("leasetime");
final JTextField tfunacked = pp.createTextField("unacked");
final JTextField tfmaxresends = pp.createTextField("maxresends");
final JTextField tfacktimeout = pp.createTextField("acktimeout");
Timer t = new Timer(1000, new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
tfalivetime.setText(""+alivetime);
tfleasetime.setText(""+leasetime);
tfunacked.setText(""+unacked);
tfmaxresends.setText(""+maxresends);
tfacktimeout.setText(""+acktimeout);
}
});
t.start();
setLayout(new BorderLayout());
add(pp, BorderLayout.CENTER);
}
}
}