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

PipelinedMsgParser.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.parser;

import gov.nist.siplite.message.*;
import gov.nist.siplite.header.*;
import gov.nist.core.*;
import java.io.*;

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

/**
 * This implements a pipelined message parser suitable for use
 * with a stream - oriented input such as TCP. The client uses
 * this class by instatiating with an input stream from which
 * input is read and fed to a message parser.
 * It keeps reading from the input stream and process messages in a
 * never ending interpreter loop. The message listener interface gets called
 * for processing messages or for processing errors. The payload specified
 * by the content-length header is read directly from the input stream.
 * This can be accessed from the Message using the getContent and
 * getContentBytes methods provided by the Message class.
 *
 * @version JAIN-SIP-1.1
 *
 * <a href="{@docRoot}/uncopyright.html">This code is in the public domain.</a>
 *
 * It was noticed that the parser was
 * blocking so I threw out some cool pipelining which ran fast but only worked
 * when the phase of the full moon matched its mood. Now things are serialized
 * and life goes slower but more reliably.
 *
 * @see SIPMessageListener
 */
public final class PipelinedMsgParser
        implements Runnable {
    /**
     * The message listener that is registered with this parser.
     * (The message listener has methods that can process correct
     * and erroneous messages.)
     */
    protected SIPMessageListener sipMessageListener;
    /** Handle to the preprocssor thread. */
    private Thread mythread;
    /* Current message body contents. */
    // private byte[] messageBody;
    /* Flag to indicate an error was found. */
    // private boolean errorFlag;
    /** Raw data input to be processed. */
    private InputStream rawInputStream;
    
    /** Default constructor. */
    protected PipelinedMsgParser() {
        super();
        
    }
    
    /**
     * Constructor when we are given a message listener and an input stream
     * (could be a TCP connection or a file)
     * @param sipMessageListener Message listener which has
     * methods that get called
     * back from the parser when a parse is complete
     * @param in Input stream from which to read the input.
     * @param debug Enable/disable tracing or lexical analyser switch.
     */
    public PipelinedMsgParser(
            SIPMessageListener sipMessageListener,
            InputStream in,
            boolean debug) {
        this();
        this.sipMessageListener = sipMessageListener;
        rawInputStream = in;
        mythread = new Thread(this);
        
    }
    
    /**
     * This is the constructor for the pipelined parser.
     * @param mhandler a MessageListener implementation that
     * provides the message handlers to
     * handle correctly and incorrectly parsed messages.
     * @param in An input stream to read messages from.
     */
    public PipelinedMsgParser
            (SIPMessageListener mhandler, InputStream in) {
        this(mhandler, in, false);
    }
    
    /**
     * This is the constructor for the pipelined parser.
     * @param in - An input stream to read messages from.
     */
    public PipelinedMsgParser(InputStream in) {
        this(null, in, false);
    }
    
    /**
     * Start reading and processing input.
     */
    public void processInput() {
        mythread.start();
    }
    
    /**
     * Create a new pipelined parser from an existing one.
     * @return A new pipelined parser that reads from the same input
     * stream.
     */
    protected Object clone() {
        PipelinedMsgParser p = new PipelinedMsgParser();
        
        p.rawInputStream = this.rawInputStream;
        p.sipMessageListener = this.sipMessageListener;
        return p;
    }
    
    /**
     * Add a class that implements a MessageListener interface whose
     * methods get called * on successful parse and error conditons.
     * @param mlistener a MessageListener
     * implementation that can react to correct and incorrect
     * pars.
     */
    public void setMessageListener(SIPMessageListener mlistener) {
        sipMessageListener = mlistener;
    }
    
    /**
     * Reads a line of input (I cannot use buffered reader because we
     * may need to switch encodings mid-stream!
     * @param inputStream source for data to be processed
     * @return the line of text retrieved
     * @exception IOException if an error occurs reading from input
     */
    private String readLine(InputStream inputStream)
    throws IOException {
        StringBuffer retval = new StringBuffer("");
        while (true) {
                char ch;
                int i = inputStream.read();
                if (i == -1) {
                    throw new IOException("End of stream");
                } else {
                    ch = (char) i;
                }
                if (ch != '\r') {
                    retval.append(ch);
                }
                if (ch == '\n') {
                    break;
                }
        }
        return retval.toString();
    }
    
    /**
     * Reads to the next break (CRLFCRLF sequence).
     * @param inputStream source for data to be processed
     * @return the text up to the break character sequence
     * @exception IOException if an error occurs reading from input
     */
    private String readToBreak(InputStream inputStream)
    throws IOException {
        StringBuffer retval = new StringBuffer("");
        boolean flag = false;
        while (true) {
                char ch;
                int i = inputStream.read();
                if (i == -1)
                    break;
                else
                    ch = (char) i;
                if (ch != '\r')
                    retval.append(ch);
                if (ch == '\n') {
                    if (flag)
                        break;
                    else
                        flag = true;
                }
        }
        return retval.toString();
    }
    
    /**
     * This is input reading thread for the pipelined parser.
     * You feed it input through the input stream (see the constructor)
     * and it calls back an event listener interface for message
     * processing or error.
     * It cleans up the input - dealing with things like line continuation
     *
     */
    public void run() {
        
        InputStream inputStream = this.rawInputStream;
        
        // I cannot use buffered reader here because we may need to switch
        // encodings to read the message body.
        try {
            while (true) {
                StringBuffer inputBuffer = new StringBuffer();
                
                if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                    Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                        "Starting parse!");
                }
                
                String line1;
                String line2 = null;
                
                // ignore blank lines.
                while (true) {
                    line1 = readLine(inputStream);

                    if (line1.equals("\n")) {
                        if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                            Logging.report(Logging.INFORMATION,
                                LogChannels.LC_JSR180,
                                "Discarding " + line1);
                        }
                        continue;
                    } else {
                        break;
                    }
                }
                if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                    Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                        "line1 = " + line1);
                }
                
                inputBuffer.append(line1);
                
                while (true) {
                    line2 = readLine(inputStream);
                    inputBuffer.append(line2);
                    if (line2.trim().equals("")) break;
                }

                inputBuffer.append(line2);
                StringMsgParser smp =
                        new StringMsgParser(sipMessageListener);
                smp.readBody = false;
                Message sipMessage = null;

                try {
                    sipMessage =
                            smp.parseSIPMessage(inputBuffer.toString());
                    if (sipMessage == null) continue;
                } catch (ParseException ex) {
                    // Just ignore the parse exception.
                    continue;
                }
                
                if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                    Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                        "Completed parsing message");
                }
                
                ContentLengthHeader cl =
                        sipMessage.getContentLengthHeader();
                int contentLength = 0;
                if (cl != null) {
                    contentLength = cl.getContentLength();
                } else {
                    contentLength = 0;
                }
                
                if (contentLength == 0) {
                    if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                        Logging.report(Logging.INFORMATION,
                            LogChannels.LC_JSR180,
                            "content length " + contentLength);
                    }
                    sipMessage.removeContent();
                } else { // deal with the message body.
                    contentLength = cl.getContentLength();
                    
                    if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                        Logging.report(Logging.INFORMATION,
                            LogChannels.LC_JSR180,
                            "content length " + contentLength);
                    }
                    
                    byte[] message_body =  new byte[contentLength];
                    int nread = 0;
                    
                    while (nread < contentLength) {
                        int readlength =
                                inputStream.read
                                (message_body, nread,
                                contentLength -
                                nread);
                                
                        if (readlength > 0) {
                            nread += readlength;
                            
                            if (Logging.REPORT_LEVEL <=
                                    Logging.INFORMATION) {
                                Logging.report(Logging.INFORMATION,
                                    LogChannels.LC_JSR180,
                                    "read " + nread);
                            }
                        } else {
                            break;
                        }
                    }
                    
                    sipMessage.setMessageContent(message_body);
                }
                
                if (sipMessageListener != null) {
                    sipMessageListener.processMessage(sipMessage);
                }
            }
        } catch (IOException ex) {
            if (sipMessageListener != null) {
                sipMessageListener.handleIOException();
            }
        } finally {
        }
    }
}