package iax.protocol.peer;
import iax.protocol.call.Call;
import iax.protocol.call.command.send.CallCommandSendFacade;
import iax.protocol.connection.Connection;
import iax.protocol.frame.Frame;
import iax.protocol.frame.FrameException;
import iax.protocol.frame.FullFrame;
import iax.protocol.frame.MiniFrame;
import iax.protocol.frame.ProtocolControlFrame;
import iax.protocol.peer.command.send.PeerCommandSendFacade;
import iax.protocol.peer.state.PeerState;
import iax.protocol.peer.state.Waiting;
import iax.protocol.peer.state.Registered;
import iax.protocol.peer.state.Unregistered;
import iax.protocol.util.FrameUtil;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Timer;
import java.util.TimerTask;
/**
* Peer tha controls phone calls.
*/
public class Peer {
/**
* Peer's source call number
*/
public final static int PEER_SRCCALLNO = 1;
/**
* Peer's register refresh in seconds
*/
public final static int REGISTER_REFRESH = 60;
//Peer's retry refresh in seconds
private final int RETRY_REFRESH = 1;
//Peer's retry max
private final int RETRY_MAXCOUNT = 5;
//Registration user name.
private String userName;
//Registration password.
private String password;
//Remote host.
private String host;
//Connection to send data to host.
private Connection connection;
//Hashmap to store initiated calls.
private HashMap calls;
//Mapping from destination number to source number.
private HashMap srcCallNoFromDestCallNo;
//Mapping from called number to source number.
private HashMap srcCallNoFromCalledNumber;
//HashMap with the frames that are waiting for an ack
private HashMap framesWaitingAck;
//HashMap with the frames that are waiting for a reply
private HashMap framesWaitingReply;
//Local identifier for a given call.
private int newLocalSrcCallNo;
//Peer listener.
private PeerListener peerListener;
//Peer's state
private PeerState state;
//Source timestamp for full frames from the first full frame sent by this call
private long srcTimestamp;
//Register timer task
private Timer registerTimer;
//Retry timer task to retry frames not commited whith a specific frame or an ack frame
private Timer retryTimer;
//Flag for determining if the peer is proceessing the exit
private boolean processingExit;
//Max calls
private int maxCalls;
//Audio Streams
private OutputStream outStream;
private InputStream inStream;
/**
* Constructor. Initializes the peer with the given values.
* @param peerListener Peer listener.
* @param userName Registration user name.
* @param password Registration password.
* @param host Remote host.
* @param register true if the peer is going to register, or false if not
* @param maxCalls maximun number of concurrent calls
*/
public Peer(PeerListener peerListener, String userName, String password,
String host, boolean register, int maxCalls,
OutputStream outStream, InputStream inStream) {
this.userName = userName;
this.password = password;
this.host = host;
this.maxCalls = maxCalls;
this.newLocalSrcCallNo = 2;
this.peerListener = peerListener;
this.connection = new Connection(this, host);
this.calls = new HashMap();
this.srcCallNoFromDestCallNo = new HashMap();
this.srcCallNoFromCalledNumber = new HashMap();
this.framesWaitingAck = new HashMap();
this.framesWaitingReply = new HashMap();
this.connection.start();
this.state = Unregistered.getInstance();
this.processingExit = false;
this.retryTimer = new Timer();
this.outStream = outStream;
this.inStream = inStream;
TimerTask retryTimerTask = new TimerTask() {
public void run() {
retryFramesWaiting();
}
};
retryTimer.schedule(retryTimerTask, RETRY_REFRESH * 1000,
RETRY_REFRESH * 1000);
this.registerTimer = new Timer();
if (register) {
TimerTask registerTimerTask = new TimerTask() {
public void run() {
register();
}
};
registerTimer.schedule(registerTimerTask, 0,
REGISTER_REFRESH * 1000);
}
}
/**
* Gets peer user name.
* @return String with user name.
*/
public String getUserName() {
return userName;
}
/**
* Gets peer password.
* @return String with password.
*/
public String getPassword() {
return password;
}
/**
* Gets peer remote host.
* @return String with remote host address.
*/
public String getHost() {
return host;
}
/**
* Gets peer's state
* @return peer's state
*/
public PeerState getState() {
return state;
}
/**
* Gets the timestamp from the first full frame sent
* @return the timestamp from the first full frame sent
*/
public long getTimestamp() {
long now = System.currentTimeMillis();
return now - srcTimestamp;
}
public boolean isProcessingExit() {
return processingExit;
}
/**
* Sets peer's state and notifies this state to the phone controller
* @param state peer's state
*/
public void setState(PeerState state) {
this.state = state;
if (state instanceof Registered)
peerListener.registered();
else if (state instanceof Waiting)
peerListener.waiting();
else if (state instanceof Unregistered)
peerListener.unregistered();
}
/**
* Ackes a full frame waiting for that removing it from the list of full frames waiting for ack
* @param timeStamp the timestamp of the full frame acked
*/
public synchronized void ackedFrame(int seqNumber) {
framesWaitingAck.remove(seqNumber);
}
/**
* Replied a full frame waiting for that removing it from the list of full frames waiting for reply
* @param id the id of the full frame reply
*/
public synchronized void repliedFrame(int id) {
framesWaitingReply.remove(id);
}
/**
* Registers the peer
*/
public void register() {
srcTimestamp = System.currentTimeMillis();
PeerCommandSendFacade.regreq(this);
}
/**
* Exits from the system and notifies the peer listener
*/
public void exit() {
processingExit = true;
registerTimer.cancel();
retryTimer.cancel();
PeerCommandSendFacade.regrel(this);
//Ends all calls
Iterator iterator = calls.values().iterator();
while (iterator.hasNext()) {
CallCommandSendFacade.hangup(((Call) iterator.next()));
}
//Wait for responses
try {
Thread.sleep(2000);
} catch (Exception e) {}
//Notifies the peer listener
peerListener.exited();
}
/**
* Gets a local call from destination call number.
* @param localDestCallNo Destination call number
* @return The call.
* @throws PeerException
*/
public synchronized Call getCall(int localDestCallNo) throws PeerException {
if (srcCallNoFromDestCallNo.containsKey(localDestCallNo)) {
int localSrcCallNo = ((Integer) srcCallNoFromDestCallNo.get(
localDestCallNo)).intValue();
return (Call) calls.get(localSrcCallNo);
} else
throw new PeerException(
"Not found any call referenced with the localDestCallNo: " +
localDestCallNo);
}
/**
* Gets a locall call from called number.
* @param calledNumber Called number.
* @return The call.
* @throws PeerException
*/
public synchronized Call getCall(String calledNumber) throws PeerException {
if (srcCallNoFromCalledNumber.containsKey(calledNumber)) {
int localSrcCallNo = ((Integer) srcCallNoFromCalledNumber.get(
calledNumber)).intValue();
return (Call) calls.get(localSrcCallNo);
} else
throw new PeerException(
"Not found any call referenced with the calledNumber: " +
calledNumber);
}
/**
* Handles the reception of a frame from a call or a peer
*
* @param buffer The frame data.
*/
public synchronized void handleRecvFrame(byte buffer[]) {
try {
Frame frame;
frame = FrameUtil.deserialize(buffer);
// Received a FullFrame
if (frame.getFull()) {
FullFrame fullFrame = (FullFrame) frame;
int destCallNo = fullFrame.getDestCallNo();
// FullFrame for a call
if ((destCallNo != 0) && (destCallNo != PEER_SRCCALLNO)) {
Call call = (Call) calls.get(fullFrame.getDestCallNo());
// Checks if there is any call that handles this FullFrame
if (call != null)
call.handleRecvFrame(fullFrame);
else
PeerCommandSendFacade.inval(this, frame);
// FullFrame for the peer
} else {
state.handleRecvFrame(this, frame);
}
// Received a MiniFrame
} else {
int localSrcCallNo;
if (srcCallNoFromDestCallNo.containsKey(frame.getSrcCallNo())) {
localSrcCallNo = ((Integer) srcCallNoFromDestCallNo.get(
frame.getSrcCallNo())).intValue();
Call call = (Call) calls.get(localSrcCallNo);
// Checks if there is any call that handles this MiniFrame
//if (call!=null)
call.handleRecvFrame((MiniFrame) frame);
} else
PeerCommandSendFacade.inval(this, frame);
}
} catch (FrameException e) {
e.printStackTrace();
}
/*} catch (Exception e) {
e.printStackTrace();
}*/
}
/**
* Handles the sending of a frame from a call.
* @param frame The frame object.
*/
public synchronized void handleSendFrame(Frame frame) {
//try {
int srcCallNo = frame.getSrcCallNo();
//Frame for a call
if ((srcCallNo != 0) && (srcCallNo != PEER_SRCCALLNO)) {
Call call = (Call) calls.get(frame.getSrcCallNo());
// Checks if there is any call that handles this Frame
if (call != null)
call.handleSendFrame(frame);
else
PeerCommandSendFacade.inval(this, frame);
// Frame for the peer
} else {
state.handleSendFrame(this, frame);
}
/*} catch (Exception e) {
e.printStackTrace();
}*/
}
/**
* Sends a frame directly.
* @param frame The frame object.
*/
public void sendFrame(Frame frame) {
try {
connection.send(frame.serialize());
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Sends a full frame that needs to be acked. For that added it to the list of frame waiting to be acked and after that
* delegates in the method sendFrame() to send it
* @param fullFrame the full frame to send
*/
public synchronized void sendFullFrameAndWaitForAck(FullFrame fullFrame) {
framesWaitingAck.put(fullFrame.getOseqno(), fullFrame);
sendFrame(fullFrame);
}
/**
* Sends a full frame that needs to be replied. For that added it to the list of frame waiting to be replied and after that
* delegates in the method sendFrame() to send it
* @param fullFrame the full frame to send
*/
public synchronized void sendFullFrameAndWaitForRep(FullFrame fullFrame) {
framesWaitingReply.put(fullFrame.getSubclass(), fullFrame);
sendFrame(fullFrame);
}
/**
* Notifies to the peer listener an answered
* @param call the call
*/
public void answeredCall(Call call) {
peerListener.answered(call.getCalledNumber());
}
/**
* Notifies to the peer listener for playing wait tones
* @param call the call
*/
public void ringingCall(Call call) {
peerListener.playWaitTones(call.getCalledNumber());
}
/**
* Notifies to the peer listener for stopping wait tones
* (STOP SOUNDS DOESN'T EXIST IN THE DRAFT)
* @param call the call
*/
public void stopRingingCall(Call call) {
peerListener.stopWaitTones(call.getCalledNumber());
}
/**
* Maps the destination call number to the source call number and, if is a received call, notifies the
* @param call The call to map.
*/
public synchronized void bindCall(Call call) {
srcCallNoFromDestCallNo.put(call.getDestCallNo(),
new Integer(call.getSrcCallNo()));
}
/**
* Ends a call.
* @param call The call to end.
*/
public synchronized void endCall(Call call) {
peerListener.hungup(call.getCalledNumber());
int localSrcCallNo = ((Integer) srcCallNoFromDestCallNo.get(call.
getDestCallNo())).intValue();
calls.remove(call.getSrcCallNo());
srcCallNoFromDestCallNo.remove(call.getDestCallNo());
}
/**
* Starts a new call.
* @param calledNumber The number to call.
* @return The new call.
* @throws PeerException
*/
public synchronized Call newCall(String calledNumber) throws PeerException {
if (calls.size() <= maxCalls) {
Call newCall = null;
try {
newCall = new Call(this, newLocalSrcCallNo, outStream, inStream);
newCall.startCall(calledNumber);
} catch (Exception e) {
throw new PeerException(e);
}
calls.put(newLocalSrcCallNo, newCall);
srcCallNoFromCalledNumber.put(calledNumber,
new Integer(newLocalSrcCallNo));
newLocalSrcCallNo++;
return newCall;
} else
throw new PeerException("Reached calls maximun");
}
/**
* Starts a call received
* @param recvCallFrame the new call frame received
* @throws PeerException
*/
public synchronized void recvCall(ProtocolControlFrame recvCallFrame) throws
PeerException {
if (calls.size() < maxCalls) {
String callingName = "";
String callingNumber = "";
try {
callingName = recvCallFrame.getCallingName();
} catch (Exception e) {
e.printStackTrace();
}
try {
callingNumber = recvCallFrame.getCallingNumber();
} catch (Exception e) {
e.printStackTrace();
}
Call newCall = null;
try {
newCall = new Call(this, newLocalSrcCallNo, outStream, inStream);
newCall.startCall(callingNumber);
} catch (Exception e) {
throw new PeerException(e);
}
calls.put(newLocalSrcCallNo, newCall);
srcCallNoFromCalledNumber.put(callingNumber,
new Integer(newLocalSrcCallNo));
newLocalSrcCallNo++;
newCall.handleRecvFrame(recvCallFrame);
peerListener.recvCall(callingName, callingNumber);
} else {
PeerCommandSendFacade.ack(this, recvCallFrame);
PeerCommandSendFacade.busy(this, recvCallFrame);
}
}
// Method to retry frames that haven't been commited whith a specific frame or an ack frame
private synchronized void retryFramesWaiting() {
try {
Iterator iterator = framesWaitingAck.values().iterator();
while (iterator.hasNext()) {
FullFrame retryFullFrame = (FullFrame) iterator.next();
retryFullFrame.incRetryCount();
if (retryFullFrame.getRetryCount() < RETRY_MAXCOUNT) {
sendFrame(retryFullFrame);
} else
throw new PeerException(
"Reached retries maximun in the peer for full frame of type " +
retryFullFrame.getFrameType() + ", subclass " +
retryFullFrame.getSubclass());
}
iterator = framesWaitingReply.values().iterator();
while (iterator.hasNext()) {
FullFrame retryFullFrame = (FullFrame) iterator.next();
retryFullFrame.incRetryCount();
if (retryFullFrame.getRetryCount() < RETRY_MAXCOUNT) {
sendFrame(retryFullFrame);
} else
throw new PeerException(
"Reached retries maximun in the peer for full frame of type " +
retryFullFrame.getFrameType() + ", subclass " +
retryFullFrame.getSubclass());
}
} catch (PeerException e) {
framesWaitingAck.clear();
framesWaitingReply.clear();
setState(Unregistered.getInstance());
e.printStackTrace();
}
}
}