UDPMessageChannelpublic class UDPMessageChannel extends MessageChannel implements ParseExceptionListener, RunnableThis is the UDP Message handler that gets created when a UDP message
needs to be processed. The message is processed by creating a String
Message parser and invoking it on the message read from the UPD socket.
The parsed structure is handed off via a SIP stack request for further
processing. This stack structure isolates the message handling logic
from the mechanics of sending and recieving messages (which could
be either udp or tcp.
Acknowledgement:
Kim Kirby of Keyvoice suggested that duplicate checking should be added
to the stack. |
Fields Summary |
---|
protected SIPMessageStack | stackSIP Stack structure for this channel. | private String | myAddressSender address (from getPeerName()) | private int | myPortLocal port number. | private String | peerAddressRemote host address. | private int | peerPortRemote port number. | private String | peerProtocolRemote connection transport. | private byte[] | msgBytesRaw message contents. | private int | packetLengthInput data length. | private DatagramConnection | datagramConnectionDatagram connection for transport. | private Datagram | incomingPacketCurrent serevr request message. | protected static Hashtable | duplicatesPool of duplicate messages. | private long | receptionTimeTimestamp of current inbound message. |
Constructors Summary |
---|
public UDPMessageChannel(Datagram packet, SIPMessageStack sipStack, MessageProcessor messageProcessor)Constructor - takes a datagram packet and a stack structure
Extracts the address of the other from the datagram packet and
stashes away the pointer to the passed stack structure.
incomingPacket = packet;
stack = sipStack;
// format: "protocol://address:port"
String address = packet.getAddress();
try {
int firstColon = address.indexOf("://");
int secondColon = address.indexOf(":", firstColon+1);
peerAddress = address.substring(firstColon+3, secondColon);
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
"DEBUG, UDPMessageChannel, UDPMessageChannel(), " +
"sender address: " + peerAddress);
}
String portString = address.substring(secondColon+1);
this.peerPort = Integer.parseInt(portString);
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
"DEBUG, UDPMessageChannel, UDPMessageChannel(), " +
"sender port: " + peerPort);
}
} catch (NumberFormatException e) {
if (Logging.REPORT_LEVEL <= Logging.ERROR) {
Logging.report(Logging.ERROR, LogChannels.LC_JSR180,
"DEBUG, UDPMessageChannel, UDPMessageChannel(), " +
"exception raised: " + e);
}
this.peerPort = -1;
}
packetLength = packet.getLength();
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
"DEBUG, UDPMessageChannel, UDPMessageChannel(), " +
"packet length: " + packetLength);
}
byte[] bytes = packet.getData();
msgBytes = new byte[packetLength];
for (int i = 0; i < packetLength; i++) {
msgBytes[i] = bytes[i];
}
String msgString = new String(msgBytes, 0, packetLength);
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
"DEBUG, UDPMessageChannel, UDPMessageChannel(), " +
" message received: " + msgString.trim());
}
this.messageProcessor = messageProcessor;
// Supports only the single threaded model.
this.run();
| public UDPMessageChannel(String targetAddr, int port, SIPMessageStack sipStack, UDPMessageProcessor processor)Constructor. We create one of these when we send out a message.
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
"DEBUG, UDPMessageChannel, UDPMessageChannel(), " +
"Creating message channel on "
+ targetAddr
+ "/" + port);
}
this.peerPort = port;
this.peerAddress = targetAddr;
this.myPort = port;
this.myAddress = processor.getSIPStack().getHostAddress();
this.messageProcessor = processor;
this.peerProtocol = "UDP";
stack = sipStack;
|
Methods Summary |
---|
public void | close()Closes the message channel.
| public boolean | equals(java.lang.Object other)Compares two UDP Message channels for equality.
if (other == null)
return false;
boolean retval;
if (!this.getClass().equals(other.getClass())) {
retval = false;
} else {
UDPMessageChannel that = (UDPMessageChannel) other;
retval = this.peerAddress.equals(that.peerAddress);
}
return retval;
| public java.lang.String | getHost()Gets the stack address for the stack that received this message.
return stack.stackAddress;
| public java.lang.String | getKey()Gets the key.
return myAddress + ":" + myPort + "/UDP";
| public java.lang.String | getPeerAddress()Gets the peer address of the machine that sent us this message.
return this.peerAddress;
| public int | getPeerPort()Gets the sender port (the port of the other end that sent me
the message).
return this.peerPort;
| public int | getPort()Gets the port.
return this.myPort;
| public SIPMessageStack | getSIPStack()Gets the stack pointer.
return stack;
| public java.lang.String | getTransport()Returns a transport string.
return "udp";
| public java.lang.String | getViaHost()Gets the logical originator of the message (from the top via header).
return this.myAddress;
| public int | getViaPort()Gets the logical port of the message orginator (from the top via hdr).
return this.myPort;
| public void | handleException(SIPServerException ex)Handles an exception - construct a sip reply and send it back to the
caller.
// Return a parse error message to the client on the other end
// if he is still alive.
// ex.printStackTrace();
int rc = ex.getRC();
Request request = (Request) ex.getSIPMessage();
Response response;
String msgString = ex.getMessage();
if (rc != 0) {
response = request.createResponse(rc, msgString);
// messageFormatter.newResponse(rc,request,msgString);
try {
sendMessage(response);
} catch (IOException ioex) {
ServerLog.logException(ioex);
}
} else {
// Assume that the message has already been formatted.
try {
sendMessage(msgString);
} catch (IOException ioex) {
ServerLog.logException(ioex);
}
}
| public void | handleException(ParseException ex, Message sipMessage, java.lang.Class hdrClass, java.lang.String headerText, java.lang.String messageText)This gets called from the parser when a parse error is generated.
The handler is supposed to introspect on the error class and
header name to handle the error appropriately. The error can
be handled by :
- 1. Re-throwing an exception and aborting the parse.
- 2. Ignoring the header (attach the unparseable header to
the Message being parsed).
- 3. Re-Parsing the bad header and adding it to the sipMessage
if (Logging.REPORT_LEVEL <= Logging.ERROR) {
Logging.report(Logging.ERROR, LogChannels.LC_JSR180,
ex.getMessage());
}
// Log the bad message for later reference.
if (hdrClass .equals(FromHeader.clazz) ||
hdrClass.equals(ToHeader.clazz) ||
hdrClass.equals(CSeqHeader.clazz) ||
hdrClass.equals(ViaHeader.clazz) ||
hdrClass.equals(CallIdHeader.clazz) ||
hdrClass.equals(RequestLine.clazz) ||
hdrClass.equals(StatusLine.clazz)) {
stack.logBadMessage(messageText);
throw ex;
} else {
sipMessage.addUnparsed(headerText);
}
| public boolean | isReliable()Checks if the transport is reliable
return false;
| public boolean | isSecure()Returns true if this is a secure channel.
return false;
| public void | run()Runs method specified by runnnable.
Message sipMessage = null;
// Create a new string message parser to parse the list of messages.
// This is a huge performance hit -- need to optimize by pre-create
// parser when one is needed....
StringMsgParser myParser = new StringMsgParser();
try {
this.receptionTime = System.currentTimeMillis();
sipMessage = myParser.parseSIPMessage(msgBytes);
} catch (ParseException ex) {
if (Logging.REPORT_LEVEL <= Logging.ERROR) {
Logging.report(Logging.ERROR, LogChannels.LC_JSR180,
"DEBUG, UDPMessageChannel, run(), " +
"Rejecting message during parsing " +
"An exception has been raised: " + ex);
}
// ex.printStackTrace();
return;
}
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
"DEBUG, UDPMEssageChannel, run(), sipMessage parsed: " +
sipMessage.encode());
}
if (sipMessage == null) {
return;
}
ViaList viaList = sipMessage.getViaHeaders();
// For a request first via header tells where the message
// is coming from.
// For response, just get the port from the packet.
// format: address:port
String address = incomingPacket.getAddress();
try {
int firstColon = address.indexOf("://");
int secondColon = address.indexOf(":", firstColon+1);
peerAddress = address.substring(firstColon+3,
secondColon);
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
"DEBUG, UDPMessageChannel, run(), sender address: " +
peerAddress);
}
String senderPortString = address.substring(secondColon+1);
peerPort = Integer.parseInt(senderPortString);
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
"DEBUG, UDPMessageChannel, run(), sender port: " +
peerPort);
}
} catch (NumberFormatException e) {
if (Logging.REPORT_LEVEL <= Logging.ERROR) {
Logging.report(Logging.ERROR, LogChannels.LC_JSR180,
"DEBUG, UDPMessageChannel, run(), " +
"exception raised: " + e);
}
// e.printStackTrace();
peerPort = -1;
}
// Check for the required headers.
if (sipMessage.getFromHeader() == null ||
// sipMessage.getFromHeader().getTag() == null ||
sipMessage.getTo() == null ||
sipMessage.getCallId() == null ||
sipMessage.getCSeqHeader() == null ||
sipMessage.getViaHeaders() == null) {
String badmsg = new String(msgBytes);
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
"bad message " + badmsg);
Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
">>> Dropped Bad Msg " +
"FromHeader = "
+ sipMessage.getFromHeader() +
"ToHeader = " + sipMessage.getTo() +
"CallId = " + sipMessage.getCallId() +
"CSeqHeader = "
+ sipMessage.getCSeqHeader() +
"Via = " + sipMessage.getViaHeaders());
}
stack.logBadMessage(badmsg);
return;
}
// For a request first via header tells where the message
// is coming from.
// For response, just get the port from the packet.
if (sipMessage instanceof Request) {
ViaHeader v = (ViaHeader)viaList.first();
if (v.hasPort()) {
if (sipMessage instanceof Request) {
this.peerPort = v.getPort();
}
} else this.peerPort = SIPConstants.DEFAULT_NONTLS_PORT;
this.peerProtocol = v.getTransport();
Request sipRequest = (Request) sipMessage;
// This is a request - process it.
SIPServerRequestInterface sipServerRequest =
stack.newSIPServerRequest(sipRequest, this);
// Drop it if there is no request returned
if (sipServerRequest == null) {
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION,
LogChannels.LC_JSR180,
"Null request interface returned");
}
return;
}
int stPort = -1;
try {
stPort = stack.getPort(this.getTransport());
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION,
LogChannels.LC_JSR180,
"About to process " +
sipRequest.getFirstLine() + "/" +
sipServerRequest);
}
sipServerRequest.processRequest(sipRequest, this);
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION,
LogChannels.LC_JSR180,
"Done processing " +
sipRequest.getFirstLine() + "/" +
sipServerRequest);
}
// So far so good -- we will commit this message if
// all processing is OK.
if (ServerLog.needsLogging(ServerLog.TRACE_MESSAGES)) {
if (sipServerRequest.getProcessingInfo() == null) {
ServerLog.logMessage(sipMessage,
sipRequest.getViaHost() + ":" +
sipRequest.getViaPort(),
stack.getHostAddress() + ":" +
stPort,
false,
new Long(receptionTime)
.toString());
} else {
ServerLog.logMessage(sipMessage,
sipRequest.getViaHost() + ":" +
sipRequest.getViaPort(),
stack.getHostAddress() + ":" +
stPort,
sipServerRequest
.getProcessingInfo(),
false, new Long(receptionTime)
.toString());
}
}
} catch (Exception ex) {
if (ex instanceof SIPServerException) {
handleException((SIPServerException)ex);
}
}
} else {
// Handle a SIP Reply message.
Response sipResponse = (Response) sipMessage;
SIPServerResponseInterface sipServerResponse =
stack.newSIPServerResponse(sipResponse, this);
try {
if (sipServerResponse != null) {
sipServerResponse.processResponse(sipResponse, this);
// Normal processing of message.
} else {
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION,
LogChannels.LC_JSR180,
"null sipServerResponse!");
}
}
} catch (Exception ex) {
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION,
LogChannels.LC_JSR180,
">>>>>Message = " + new String(msgBytes));
}
if (ServerLog.needsLogging
(ServerLog.TRACE_MESSAGES)) {
this.logResponse(sipResponse,
receptionTime,
ex.getMessage()+ "-- Dropped!");
}
ServerLog.logException(ex);
}
}
| private void | sendMessage(java.lang.String msg)Sends the message.
sendMessage(msg.getBytes(), peerAddress,
peerPort, peerProtocol);
| private void | sendMessage(byte[] msg)Sends the message.
sendMessage(msg, peerAddress, peerPort, peerProtocol);
| public void | sendMessage(Message sipMessage)Returns a reply from a pre-constructed reply. This sends the message
back to the entity who caused us to create this channel in the
first place.
byte[] msg = sipMessage.encodeAsBytes();
sendMessage(msg, peerAddress, peerPort, peerProtocol);
| protected void | sendMessage(byte[] msg, java.lang.String receiverAddress, int receiverPort)Sends a message to a specified receiver address.
// msg += "\r\n\r\n";
// Via is not included in the request so silently drop the reply.
if (receiverPort == -1) {
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
"DEBUG, UDPMessageChannel, sendMessage(), " +
"The message is not sent: the receiverPort=-1");
}
throw new IOException("Receiver port not set ");
}
try {
/*
* Original NIST method for opening the datagram connection
* has been replaced by direct calls to instantiate the protocol
* handler, in order to pass the security token for use of lower
* level transport connection.
* Original NIST sequence is :
*
* //format: "datagram://address:port"
* String url = "datagram://"+peerAddress+":"+peerPort;
* this.datagramConnection =
* (DatagramConnection)Connector.open(url);
*
*/
String url = "//"+peerAddress+":"+peerPort;
com.sun.midp.io.j2me.datagram.Protocol conn =
new com.sun.midp.io.j2me.datagram.Protocol();
this.datagramConnection =
(DatagramConnection)conn.openPrim(
stack.getSecurityToken(), url,
Connector.WRITE, true);
// timeouts are ignored for datagrams
Datagram reply = datagramConnection.newDatagram(msg, msg.length);
datagramConnection.send(reply);
String msgString = new String(msg, 0, msg.length);
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
"DEBUG, UDPMessageChannel, sendMessage(), " +
"Datagram sent on datagram:" + url + ", message sent:\n" +
msgString.trim());
}
datagramConnection.close();
} catch (IOException ex) {
if (Logging.REPORT_LEVEL <= Logging.ERROR) {
Logging.report(Logging.ERROR, LogChannels.LC_JSR180, "toto");
Logging.report(Logging.ERROR, LogChannels.LC_JSR180,
"DEBUG, UDPMessageChannel, sendMessage(), " +
"The message is not sent: exception raised: " + ex);
}
}
| protected void | sendMessage(byte[] msg, java.lang.String receiverAddress, int receiverPort, java.lang.String receiverProtocol)Sends a message to a specified receiver address.
// msg += "\r\n\r\n";
// Via is not included in the request so silently drop the reply.
if (receiverPort == -1) {
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
"DEBUG, UDPMessageChannel, sendMessage(), " +
"The message is not sent: " +
"the receiverPort=-1");
}
throw new IOException("Receiver port not set ");
}
if (Utils.compareToIgnoreCase(receiverProtocol, "UDP") == 0) {
try {
/*
* Original NIST method for opening the datagram connection
* has been replaced by direct calls to instantiate the
* protocol handler, in order to pass the security token
* for use of lower level transport connection.
* Original NIST sequence is :
*
* String url = "datagram://"
* + receiverAddress + ":" + receiverPort;
* // format: "datagram://address:port"
* datagramConnection =
* (DatagramConnection)Connector.open(url);
*
*/
String url = "//" + receiverAddress + ":" + receiverPort;
com.sun.midp.io.j2me.datagram.Protocol conn =
new com.sun.midp.io.j2me.datagram.Protocol();
datagramConnection =
(DatagramConnection)conn.openPrim(
stack.getSecurityToken(), url,
Connector.WRITE, true);
// timeouts are ignored
Datagram datagram = datagramConnection
.newDatagram(msg, msg.length);
datagramConnection.send(datagram);
String msgString = new String(msg, 0, msg.length);
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
"DEBUG, UDPMessageChannel, sendMessage(), " +
" Datagram sent on datagram: " +
url + ", message sent:\n" + msgString.trim());
}
datagramConnection.close();
} catch (IOException ex) {
if (Logging.REPORT_LEVEL <= Logging.ERROR) {
Logging.report(Logging.ERROR, LogChannels.LC_JSR180,
"DEBUG, UDPMessageChannel, sendMessage(), " +
"The message is not sent: exception raised:" + ex);
}
}
} else {
// TCP is not supported
throw new IOException("Unsupported protocol");
}
|
|