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

NistSipMessageHandlerImpl.java

/*
 * Portions Copyright  2000-2007 Sun Microsystems, Inc. All Rights
 * Reserved.  Use is subject to license terms.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version
 * 2 only, as published by the Free Software Foundation.
 * 
 * This program 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
 * General Public License version 2 for more details (a copy is
 * included at /legal/license.txt).
 * 
 * You should have received a copy of the GNU General Public License
 * version 2 along with this work; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 * 
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
 * Clara, CA 95054 or visit www.sun.com if you need additional
 * information or have any questions.
 */
/*
 */
package gov.nist.siplite;

import gov.nist.siplite.message.*;
import gov.nist.siplite.stack.*;
import gov.nist.siplite.header.*;
import gov.nist.core.*;

import com.sun.midp.log.Logging;
import com.sun.midp.log.LogChannels;

/**
 * An adapter class from the JAIN implementation objects to the NIST-SIP stack.
 * This is the class that is instantiated by the NistSipMessageFactory to
 * create a new SIPServerRequest or SIPServerResponse.
 * Note that this is not part of the JAIN-SIP spec (it does not implement
 * a JAIN-SIP interface). This is part of the glue that ties together the
 * NIST-SIP stack and event model with the JAIN-SIP stack. Implementors
 * of JAIN services need not concern themselves with this class.
 *
 * @version  JAIN-SIP-1.1
 *
 * <a href="{@docRoot}/uncopyright.html">This code is in the public domain.</a>
 *
 */
public class NistSipMessageHandlerImpl
        implements SIPServerRequestInterface, SIPServerResponseInterface {
    /** Current transaction channel. */
    protected Transaction transactionChannel;
    /** Raw message channel. */
    protected MessageChannel rawMessageChannel;
    // protected Request sipRequest;
    // protected Response sipResponse;
    /** Current listening filter. */
    protected ListeningPoint listeningPoint;
    /**
     * Process a request.
     * @param sipRequest the request to be processed
     * @param incomingMessageChannel the inbound message connection
     * @exception SIPServerException is thrown when there is an error
     * processing the request.
     */
    public void processRequest(Request sipRequest,
            MessageChannel incomingMessageChannel)
            throws SIPServerException {
        // Generate the wrapper JAIN-SIP object.
        if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
            Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                "PROCESSING INCOMING REQUEST " + sipRequest.getFirstLine());
        }
        this.rawMessageChannel = incomingMessageChannel;
        
        SipStack sipStack = (SipStack) transactionChannel.getSIPStack();
        
        /*
         * What if the listeningPoint of the incomingMessagechannel different
         * than the one that set during initialization in
         * NistSipMesageFactoryImpl?
         * So, initialize it explicitely for the incomingMessageChannel
         */
        listeningPoint =
              incomingMessageChannel.getMessageProcessor().getListeningPoint();
        if (listeningPoint == null) {
            if (Logging.REPORT_LEVEL <= Logging.WARNING) {
                Logging.report(Logging.WARNING, LogChannels.LC_JSR180,
                    "Dropping message: No listening point registered!");
            }
            return;
        }
        
        SipProvider sipProvider = listeningPoint.getProvider();
        
        if (sipProvider == null)  {
            if (Logging.REPORT_LEVEL <= Logging.WARNING) {
                Logging.report(Logging.WARNING, LogChannels.LC_JSR180,
                    "No provider - dropping !!");
            }
            return;
        }

        // SipListener sipListener = sipProvider.sipListener;
        Transaction transaction = transactionChannel;
        
        // Look for the registered SIPListener for the message channel.
        synchronized (sipProvider) {
            String dialogId = sipRequest.getDialogId(true);
            Dialog dialog = sipStack.getDialog(dialogId);
            final ServerTransaction requestTransaction =
                    (ServerTransaction) sipRequest.getTransaction();
            if (null == requestTransaction.getDialog()) {
                requestTransaction.setDialog(dialog);
            }
            if (sipRequest.getMethod().equals(Request.ACK)) {
                // Could not find transaction. Generate an event
                // with a null transaction identifier.
                if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                    Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                        "Processing ACK for dialog " + dialog);
                }
                
                if (dialog == null) {
                    if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                        Logging.report(Logging.INFORMATION,
                            LogChannels.LC_JSR180,
                            "Dialog does not exist "
                            + sipRequest.getFirstLine() +
                            " isServerTransaction = " + true);
                    }

                    transaction =
                            sipStack.findTransaction(sipRequest, true);
                } else if (dialog.getLastAck() != null &&
                        dialog.getLastAck().equals(sipRequest)) {
                    if (sipStack.isRetransmissionFilterActive()) {
                        dialog.ackReceived(sipRequest);
                        transaction.setDialog(dialog);
                        
                        if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                            Logging.report(Logging.INFORMATION,
                                LogChannels.LC_JSR180,
                                "Retransmission Filter enabled -" 
                                + " dropping Ack retransmission");
                        }
                        
                        // filter out retransmitted acks if
                        // retransmission filter is enabled.
                        return;
                    }
                    
                    if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                        Logging.report(Logging.INFORMATION,
                            LogChannels.LC_JSR180,
                            "ACK retransmission for 2XX "
                            + "response "
                            + "Sending ACK to the TU");
                    }
                } else {
                    //	This could be a re-invite processing.
                    // check to see if the ack matches with the last
                    // transaction.
                    
                    Transaction tr = dialog.getLastTransaction();
                    Response sipResponse = tr.getLastResponse();
                    
                    if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                        Logging.report(Logging.INFORMATION,
                            LogChannels.LC_JSR180, "TRANSACTION:" + tr);
                    }
                    
                    if (tr instanceof ServerTransaction &&
                            sipResponse != null &&
                            sipResponse.getStatusCode() / 100 == 2
                            && sipResponse.getCSeqHeader().getSequenceNumber()
                            == sipRequest.getCSeqHeader().getSequenceNumber()) {
                        transaction.setDialog(dialog);
                        dialog.ackReceived(sipRequest);
                        
                        if (sipStack.isRetransmissionFilterActive() &&
                                tr.isAckSeen()) {
                            if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                                Logging.report(Logging.INFORMATION,
                                    LogChannels.LC_JSR180,
                                    "ACK retransmission for"
                                    + " 2XX response --- "
                                    + "Dropping ");
                            }
                            return;
                        } else {
                            // record that we already saw an ACK for
                            // this transaction.
                            tr.setAckSeen();
                            if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                                Logging.report(Logging.INFORMATION,
                                    LogChannels.LC_JSR180,
                                    "ACK retransmission for 2XX "
                                    + "response --- "
                                    + "sending to TU ");
                            }
                        }
                        
                    } else {
                        if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                            Logging.report(Logging.INFORMATION,
                                LogChannels.LC_JSR180,
                                "ACK retransmission for non"
                                + " 2XX response "
                                + "Discarding ACK");
                        }
                        
                        // Could not find a transaction.
                        if (tr == null) {
                            if (Logging.REPORT_LEVEL <= Logging.WARNING) {
                                Logging.report(Logging.WARNING,
                                    LogChannels.LC_JSR180,
                                    "Could not find transaction ACK dropped");
                            }
                            return;
                        }
                        
                        transaction = tr;
                        if (transaction instanceof ClientTransaction) {
                            if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                                Logging.report(Logging.INFORMATION,
                                    LogChannels.LC_JSR180,
                                    "Dropping late ACK");
                            }
                            return;
                        }
                    }
                }
            } else if (sipRequest.getMethod().equals(Request.BYE)) {
                transaction = this.transactionChannel;
                // If the stack has not mapped this transaction because
                // of sequence number problem then just drop the BYE
                if (transaction != null &&
                       ((ServerTransaction)transaction).isTransactionMapped()) {
                    // Get the dialog identifier for the bye request.
                    if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                        Logging.report(Logging.INFORMATION,
                            LogChannels.LC_JSR180, "dialogId = " + dialogId);
                    }
                    
                    // Find the dialog identifier in the SIP stack and
                    // mark it for garbage collection.
                    if (dialog != null) {
                        // Remove dialog marks all
                        dialog.addTransaction(transaction);
                    } else {
                        dialogId = sipRequest.getDialogId(false);
                        
                        if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                            Logging.report(Logging.INFORMATION,
                                LogChannels.LC_JSR180,
                                "dialogId = " + dialogId);
                        }
                        
                        dialog = sipStack.getDialog(dialogId);
                        
                        if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                            Logging.report(Logging.INFORMATION,
                            LogChannels.LC_JSR180, "dialog = " + dialog);
                        }
                        
                        if (dialog != null) {
                            dialog.addTransaction(transaction);
                        } else {
                            transaction = null;
                            // pass up to provider for
                            // stateless handling.
                        }
                    }
                } else if (transaction != null)  {
                    // This is an out of sequence BYE
                    // transaction was allocated but
                    // not mapped to the stack so
                    // just discard it.
                    if (dialog != null) {
                        if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                            Logging.report(Logging.INFORMATION,
                                LogChannels.LC_JSR180,
                                "Dropping out of sequence BYE");
                        }
                        return;
                    } else transaction = null;
                }
                // note that the transaction may be null (which
                // happens when no dialog for the bye was fund.
            } else if (sipRequest.getRequestLine().getMethod().equals
                    (Request.CANCEL)) {
                
                // The ID refers to a previously sent
                // INVITE therefore it refers to the
                // server transaction table.
                // Find the transaction to cancel.
                // Send a 487 for the cancel to inform the
                // other side that we've seen it but do not send the
                // request up to the application layer.
                
                // Get rid of the CANCEL transaction -- we pass the
                // transaciton we are trying to cancel up to the TU.
                
                // Antonis Karydas: Suggestion
                // 'transaction' here refers to the transaction to
                // be cancelled. Do not change
                // it's state because of the CANCEL.
                // Wait, instead for the 487 from TU.
                // transaction.setState(Transaction.TERMINATED_STATE);
                
                ServerTransaction serverTransaction =
                        (ServerTransaction)
                        sipStack.findCancelTransaction(sipRequest, true);
                
                // Generate an event
                // with a null transaction identifier.
                if (serverTransaction == null) {
                    // Could not find the invite transaction.
                    if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                        Logging.report(Logging.INFORMATION,
                            LogChannels.LC_JSR180,
                            "transaction "
                            + " does not exist "
                            + sipRequest.getFirstLine()
                            + "isServerTransaction = "
                            + true);
                    }
                    transaction = null;
                } else {
                    transaction = serverTransaction;
                }
            }
            
            
            if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                    "-----------------");
                Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                    sipRequest.toString());
            }
            
            if (dialog != null &&
                    transaction != null &&
                    ! sipRequest.getMethod().equals(Request.BYE) &&
                    ! sipRequest.getMethod().equals(Request.CANCEL) &&
                    ! sipRequest.getMethod().equals(Request.ACK)) {
                // already dealt with bye above.
                // Note that route updates are only effective until
                // Dialog is in the confirmed state.
                if (dialog.getRemoteSequenceNumber() >=
                        sipRequest.getCSeqHeader().getSequenceNumber()) {
                    if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                        Logging.report(Logging.INFORMATION,
                            LogChannels.LC_JSR180,
                            "Dropping out of sequence message " +
                            dialog.getRemoteSequenceNumber() +
                            " "  + sipRequest.getCSeqHeader());
                    }
                    
                    return;
                }
                
                dialog.addTransaction(transaction);
                dialog.addRoute(sipRequest);
            }
            
            
            RequestEvent sipEvent;
            
            if (dialog == null && sipRequest.getMethod().equals
                    (Request.NOTIFY)) {
                ClientTransaction ct =
                        sipStack.findSubscribeTransaction(sipRequest);
                // From RFC 3265
                // If the server transaction cannot be found or if it
                // aleady has a dialog attached to it then just assign the
                // notify to this dialog and pass it up.
                if (ct != null) {
                    transaction.setDialog(ct.getDialog());
                    if (ct.getDialog().getState() == Dialog.INITIAL_STATE) {
                        sipEvent = new RequestEvent
                                (sipProvider, null, (Request) sipRequest);
                    } else {
                        sipEvent = new RequestEvent
                                (sipProvider,
                                (ServerTransaction)transaction,  sipRequest);
                    }
                } else {
                    // Got a notify out of the blue - just pass it up
                    // for stateless handling by the application.
                    sipEvent = new RequestEvent
                            (sipProvider, null,  sipRequest);
                }
                
            } else {
                // For a dialog creating event - set the transaction to null.
                // The listener can create the dialog if needed.
                if (transaction != null &&
                        ((ServerTransaction) transaction).isTransactionMapped())
                    sipEvent = new RequestEvent(sipProvider,
                            (ServerTransaction) transaction,
                            sipRequest);
                else sipEvent = new RequestEvent
                        (sipProvider, null,  sipRequest);
            }
            sipProvider.handleEvent(sipEvent,  transaction);
        }
        
    }
    
    /**
     * Process the response.
     * @param sipResponse the response message
     * @param incomingMessageChannel  message channel on which the
     * response is received.
     * @exception SIPServerException is thrown when there is an error
     * processing the response
     */
    public void processResponse(Response sipResponse,
            MessageChannel incomingMessageChannel)
            throws SIPServerException {
        try {
            if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                    "PROCESSING INCOMING RESPONSE" + sipResponse.encode());
            }

            /*
             * What if the listeningPoint of the incomingMessagechannel
             * different than the one that set during initialization in
             * NistSipMesageFactoryImpl?
             * So, initialize it explicitely for the incomingMessageChannel.
             */
            listeningPoint =
               incomingMessageChannel.getMessageProcessor().getListeningPoint();

            if (listeningPoint == null) {
                if (Logging.REPORT_LEVEL <= Logging.WARNING) {
                    Logging.report(Logging.WARNING, LogChannels.LC_JSR180,
                        "Dropping message: No listening point registered!");
                }
                return;
            }

            Transaction transaction = this.transactionChannel;
            SipProvider sipProvider = listeningPoint.getProvider();
            
            if (sipProvider == null) {
                if (Logging.REPORT_LEVEL <= Logging.WARNING) {
                    Logging.report(Logging.WARNING, LogChannels.LC_JSR180,
                        "Dropping message:  no provider");
                }
                return;
            }
            
            SipStack sipStack = sipProvider.sipStack;
            
            if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                    "Transaction = " + transaction);
            }
            
            if (this.transactionChannel == null) {
                String dialogId = sipResponse.getDialogId(false);
                Dialog dialog = sipStack.getDialog(dialogId);

                //  Have a dialog but could not find transaction.
                if (sipProvider.sipListener == null) {
                    return;
                } else if (dialog != null) {
                    if (sipResponse.getStatusCode() != Response.OK) {
                        return;
                    } else if (sipStack.isRetransmissionFilterActive()) {
                        // 200  retransmission for the final response.
                        if (sipResponse.getCSeqHeader().equals(
                                dialog.getFirstTransaction().getRequest().
                                    getHeader(Header.CSEQ))) {
                            dialog.resendAck();
                            return;
                        }
                    }
                }
                
                // long receptionTime = System.currentTimeMillis();
                // Pass the response up to the application layer to handle
                // statelessly.
                
                // Dialog is null so this is handled statelessly
                ResponseEvent sipEvent =
                      new ResponseEvent(sipProvider, null, sipResponse);
                sipProvider.handleEvent(sipEvent, transaction);
                // transaction.logResponse(sipResponse,
                //       receptionTime,"Retransmission");
                return;
            }
            
            this.rawMessageChannel = incomingMessageChannel;
            
            String method = sipResponse.getCSeqHeader().getMethod();
            // Retrieve the client transaction for which we are getting
            // this response.
            ClientTransaction clientTransaction =
                    (ClientTransaction) this.transactionChannel;
            
            Dialog dialog = null;
            if (transaction != null) {
                dialog =  transaction.getDialog();
                
                if (Logging.REPORT_LEVEL <= Logging.INFORMATION &&
                        dialog == null) {
                    Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                        "dialog not found for " + sipResponse.getFirstLine());
                }
            }
            
            // SipListener sipListener = sipProvider.sipListener;
            
            ResponseEvent responseEvent = new ResponseEvent
                    (sipProvider, (ClientTransaction)transaction, sipResponse);
            sipProvider.handleEvent(responseEvent, transaction);
        } catch (NullPointerException ex) {
            if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                    "null ptr");
                ex.printStackTrace();
            }
        }
    }

    /**
     * Gets the sender channel.
     * @return the request channel
     */
    public MessageChannel getRequestChannel() {
        return this.transactionChannel;
    }
    
    /**
     * Gets the channel if we want to initiate a new transaction to
     * the sender of  a response.
     * @return a message channel that points to the place from where we got
     * the response.
     */
    public MessageChannel getResponseChannel() {
        if (this.transactionChannel != null)
            return this.transactionChannel;
        else return this.rawMessageChannel;
    }
    
    /**
     * Just a placeholder. This is called from the stack
     * for message logging. Auxiliary processing information can
     * be passed back to be  written into the log file.
     * @return auxiliary information that we may have generated during the
     * message processing which is retrieved by the message logger.
     * (Always returns null)
     */
    public String getProcessingInfo() {
        return null;
    }
}