FileDocCategorySizeDatePackage
UDPMessageChannel.javaAPI DocphoneME MR2 API (J2ME)29988Wed May 02 18:00:42 BST 2007gov.nist.siplite.stack

UDPMessageChannel

public class UDPMessageChannel extends MessageChannel implements ParseExceptionListener, Runnable
This 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.
see
gov.nist.siplite.parser.StringMsgParser
see
gov.nist.siplite.stack.SIPServerRequestInterface
since
v1.0

Fields Summary
protected SIPMessageStack
stack
SIP Stack structure for this channel.
private String
myAddress
Sender address (from getPeerName())
private int
myPort
Local port number.
private String
peerAddress
Remote host address.
private int
peerPort
Remote port number.
private String
peerProtocol
Remote connection transport.
private byte[]
msgBytes
Raw message contents.
private int
packetLength
Input data length.
private DatagramConnection
datagramConnection
Datagram connection for transport.
private Datagram
incomingPacket
Current serevr request message.
protected static Hashtable
duplicates
Pool of duplicate messages.
private long
receptionTime
Timestamp 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.

param
packet is the UDP Packet that contains the request.
param
sipStack stack is the shared SipStack structure
param
messageProcessor the UDP input message channel

        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.

param
targetAddr internet address of the place where we want to send messages.
param
port target port (where we want to send the message).
param
sipStack our SIP Stack.
param
processor inbound message processor


        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 voidclose()
Closes the message channel.

    
public booleanequals(java.lang.Object other)
Compares two UDP Message channels for equality.

param
other The other message channel with which to compare oursleves.
return
true if the objects match

        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.StringgetHost()
Gets the stack address for the stack that received this message.

return
The stack address for our stack.

        return stack.stackAddress;
    
public java.lang.StringgetKey()
Gets the key.

return
the key

        return myAddress + ":" + myPort + "/UDP";
    
public java.lang.StringgetPeerAddress()
Gets the peer address of the machine that sent us this message.

return
a string contianing the ip address or host name of the sender of the message.

        return this.peerAddress;
    
public intgetPeerPort()
Gets the sender port (the port of the other end that sent me the message).

return
the remote port number

        return this.peerPort;
    
public intgetPort()
Gets the port.

return
Our port (on which we are getting datagram packets).

        return this.myPort;
    
public SIPMessageStackgetSIPStack()
Gets the stack pointer.

return
The sip stack for this channel.

        return stack;
    
public java.lang.StringgetTransport()
Returns a transport string.

return
the string "udp" in this case.

        return "udp";
    
public java.lang.StringgetViaHost()
Gets the logical originator of the message (from the top via header).

return
topmost via header sentby field

        return this.myAddress;
    
public intgetViaPort()
Gets the logical port of the message orginator (from the top via hdr).

return
the via port from the topmost via header.

        return this.myPort;
    
public voidhandleException(SIPServerException ex)
Handles an exception - construct a sip reply and send it back to the caller.

param
ex The exception thrown at us by our application.

        // 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 voidhandleException(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

param
ex parse exception being processed.
param
sipMessage sip message being processed.
param
hdrClass header parsing class
param
headerText header/RL/SL text being parsed.
param
messageText message where this header was detected.


        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 booleanisReliable()
Checks if the transport is reliable

return
always false

        return false;
    
public booleanisSecure()
Returns true if this is a secure channel.

return
always false


        return false;
    
public voidrun()
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 voidsendMessage(java.lang.String msg)
Sends the message.

param
msg the message to be processed.
exception
IOException if the send can not be processed

        sendMessage(msg.getBytes(), peerAddress,
                    peerPort, peerProtocol);
    
private voidsendMessage(byte[] msg)
Sends the message.

param
msg the message to be processed.
exception
IOException if the send can not be processed

        sendMessage(msg, peerAddress, peerPort, peerProtocol);
    
public voidsendMessage(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.

param
sipMessage Message string to send.
throws
IOException If there is a problem with sending the message.

        byte[] msg = sipMessage.encodeAsBytes();
        sendMessage(msg, peerAddress, peerPort, peerProtocol);
    
protected voidsendMessage(byte[] msg, java.lang.String receiverAddress, int receiverPort)
Sends a message to a specified receiver address.

param
msg message string to send.
param
receiverAddress Address of the place to send it to.
param
receiverPort the port to send it to.
throws
IOException If there is trouble sending this message.

        // 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 voidsendMessage(byte[] msg, java.lang.String receiverAddress, int receiverPort, java.lang.String receiverProtocol)
Sends a message to a specified receiver address.

param
msg message string to send.
param
receiverAddress Address of the place to send it to.
param
receiverPort the port to send it to.
param
receiverProtocol protocol to use to send.
throws
IOException If there is trouble sending this message.

        // 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");
        }