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

SipStack.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 java.util.*;
import gov.nist.siplite.stack.*;
import gov.nist.siplite.message.*;
import gov.nist.siplite.address.*;
import gov.nist.core.*;
import gov.nist.microedition.sip.*;
import com.sun.midp.security.SecurityToken;

import com.sun.midp.log.Logging;
import com.sun.midp.log.LogChannels;
import gov.nist.siplite.SIPConstants;

/**
 * Implementation of SipStack.
 *
 * The JAIN-SIP stack is initialized by a set of properties (see the JAIN
 * SIP documentation for an explanation of these properties).
 * In addition to these, the following are meaningful properties for
 * the NIST SIP stack (specify these in the property array when you create
 * the JAIN-SIP statck).:
 * <ul>
 *
 * <li><b>gov.nist.javax.sip.TRACE_LEVEL = integer </b><br>
 * Currently only 16 and 32 is meaningful.
 * If this is set to 16 or above, then incoming
 * valid messages are  logged in SERVER_LOG. If you set this to 32 and
 * specify a DEBUG_LOG then vast amounts of trace information will be dumped
 * in to the specified DEBUG_LOG.  The server log accumulates the signaling
 * trace.
 * This can be viewed using the trace viewer tool .
 * Please send us both the server log and debug log
 * when reporting non-obvious problems.</li>
 *
 * @version  JAIN-SIP-1.1
 *
 *
 * <a href="{@docRoot}/uncopyright.html">This code is in the public domain.</a>
 *
 *
 */
public class SipStack
        extends SIPTransactionStack {
    /** Current listening points. */
    private Hashtable listeningPoints;
    /** Vector of SIP providers. */
    private Vector sipProviders;
    /** Flag indicating the provider has been initialized. */
    protected boolean stackInitialized;
    /** Pathto router. */
    protected String routerPath;
    /** Current eventscanner. */
    protected EventScanner  eventScanner;
    /** Current SIP listener. */
    protected SipListener sipListener;
    /** Connector for SIP stack. */
    protected StackConnector sipStackConnector;
    /** Name of the logfile used to log messages */
    private String logFilename = null;
    /** Outbound proxy for this stack */
    protected String outboundProxy = null;
    
    /** Outbound proxy port for this stack */
    protected int outboundPort = -1;

    /**
     * Constructor.
     * @param stackConnector connection to use for SIP Stack
     */
    public void setStackConnector(StackConnector stackConnector) {
        this.sipStackConnector = stackConnector;
        
    }
    
    /**
     * Creates a new instance of SipStack.
     */
    protected SipStack() {
        super();
        NistSipMessageFactoryImpl msgFactory =
                new NistSipMessageFactoryImpl(this);
        super.setMessageFactory(msgFactory);
        this.listeningPoints = new Hashtable();
        this.sipProviders = new Vector();
    }
    
    /**
     * Stops the SIP stack processing.
     */
    public void stopStack() {
        super.stopStack();
        this.eventScanner.stop();
        this.sipStackConnector.releaseInstance();
    }
    
    /**
     * Construct a SIP Stack top match requested configuration.
     * @param configurationProperties selectors for SIP Stack
     * @param classSecurityToken security token for saving
     */
    public SipStack(ConfigurationProperties configurationProperties,
                    SecurityToken classSecurityToken)
    throws PeerUnavailableException {
        this();
        this.eventScanner = new EventScanner(this);
        this.eventScanner.start();
        String address = configurationProperties.getProperty
                ("javax.sip.IP_ADDRESS");
        
        /** Retrieve the stack IP address */
        if (address == null)
            throw new PeerUnavailableException("address not specified");
        super.setHostAddress(address);
        
        
        /** Retrieve the stack name */
        String name = configurationProperties.getProperty
                ("javax.sip.STACK_NAME");
        if (name == null)
            throw new PeerUnavailableException("stack name is missing");
        super.setStackName(name);
        
        
        routerPath = "gov.nist.siplite.stack.DefaultRouter";
        outboundProxy =
                configurationProperties.getProperty
                ("javax.sip.OUTBOUND_PROXY");

        Exception ex = null;
        try {
            Class routerClass = Class.forName(routerPath);
            Router router = (Router) routerClass.newInstance();
            if (outboundProxy != null)
                router.setOutboundProxy(outboundProxy);
            router.setSipStack(this);
            super.setRouter(router);
            
        } catch (ClassNotFoundException cnfe) {
            ex = cnfe;
        } catch (InstantiationException ie) {
           ex = ie;
        } catch (IllegalAccessException iae) {
            ex = iae;
        }
        if (ex != null) {
           throw new PeerUnavailableException("Could not instantiate router");
        }
        if (outboundProxy != null) {
            Hop hop = new Hop(outboundProxy);
            this.outboundProxy = hop.getHost();
            this.outboundPort =  hop.getPort();
        }
        
        
        
        /**
         * Retrieve the EXTENSION Methods. These are used for instantiation
         * of Dialogs.
         */
        String extensionMethods =
                configurationProperties.getProperty
                ("javax.sip.EXTENSION_METHODS");
        
        if (extensionMethods != null) {
            gov.nist.core.StringTokenizer st = new
                    gov.nist.core.StringTokenizer(extensionMethods, ':');
            
            while (st.hasMoreChars()) {
                String em = st.nextToken();
                if (em.toUpperCase().equals(Request.BYE)
                || em.toUpperCase().equals(Request.ACK)
                || em.toUpperCase().equals(Request.OPTIONS))
                    throw new PeerUnavailableException
                            ("Bad extension method " + em);
                else this.addExtensionMethod(em.toUpperCase());
            }
        }
        
        /* Set the retransmission filter. For SIPLite this is always true */
        this.retransmissionFilter = true;
        

        String maxConnections =
                configurationProperties.getProperty
                ("gov.nist.javax.sip.MAX_CONNECTIONS");
        if (maxConnections != null) {
            try {
                this.maxConnections = Integer.parseInt(maxConnections);
            } catch (NumberFormatException nfe)  {
                if (Logging.REPORT_LEVEL <= Logging.ERROR) {
                    Logging.report(Logging.ERROR, LogChannels.LC_JSR180,
                        "max connections - bad value " + nfe.getMessage());
                }
            }
        }
        
        String threadPoolSize = configurationProperties.getProperty
                ("gov.nist.javax.sip.THREAD_POOL_SIZE");
        if (threadPoolSize != null) {
            try {
                this.threadPoolSize = Integer.parseInt(threadPoolSize);
            } catch (NumberFormatException nfe)  {
                if (Logging.REPORT_LEVEL <= Logging.ERROR) {
                    Logging.report(Logging.ERROR, LogChannels.LC_JSR180,
                        "thread pool size - bad value " + ex.getMessage());
                }
            }
        }
        
        String transactionTableSize = configurationProperties.getProperty
                ("gov.nist.javax.sip.MAX_SERVER_TRANSACTIONS");
        if (transactionTableSize != null) {
            try {
                this.transactionTableSize =
                        Integer.parseInt(transactionTableSize);
            } catch (NumberFormatException nfe)  {
                if (Logging.REPORT_LEVEL <= Logging.ERROR) {
                    Logging.report(Logging.ERROR, LogChannels.LC_JSR180,
                        "transaction table size - bad value " + 
                         ex.getMessage());
                }
            }
        }
        this.setSecurityToken(classSecurityToken);
    }
    
    
    /**
     * Gets the sip listener for the stack.
     * @return the SIP listener
     */
    public SipListener getSipListener() {
        return this.sipListener;
    }
    
    
    /**
     * Creates a new peer ListeningPoint on this SipStack on a specified
     * host, port and transport and returns a reference to the newly created
     * ListeningPoint object. The newly created ListeningPoint is implicitly
     * attached to this SipStack upon execution of this method, by adding the
     * ListeningPoint to the {@link SipStack#getListeningPoints()} of this
     * SipStack, once it has been successfully created.
     *
     * @param port the port of the new ListeningPoint.
     * @param transport the transport of the new ListeningPoint.
     * SipStack.
     * @return The peer ListeningPoint attached to this SipStack.
     */
    public synchronized ListeningPoint createListeningPoint(int port,
            String transport)
            throws TransportNotSupportedException, IllegalArgumentException {
        if (transport == null)
            throw new NullPointerException("null transport");
        if (port <= 0)
            throw new IllegalArgumentException("bad port");
        if (!Utils.equalsIgnoreCase(transport, "UDP") &&
                !Utils.equalsIgnoreCase(transport, "TCP"))
            throw new TransportNotSupportedException
                    ("bad transport " + transport);
       
        if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
            Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                "createListeningPoint " + transport + " / " + port);
        }
        
        String key = ListeningPoint.makeKey
                (super.stackAddress, port, transport);
        
        ListeningPoint lip = (ListeningPoint)listeningPoints.get(key);
        if (lip != null) {
            return lip;
        } else {
            try {
                MessageProcessor messageProcessor =
                        this.createMessageProcessor(port, transport);
                lip = new ListeningPoint(this, this.getIPAddress(), port, 
                                         transport);
                lip.messageProcessor = messageProcessor;
                messageProcessor.setListeningPoint(lip);
                this.listeningPoints.put(key, lip);
                return  lip;
            } catch (java.io.IOException ex) {
                throw new IllegalArgumentException(ex.getMessage());
            }
        }
    }
    /**
     * Creates a new peer SipProvider on this SipStack on a specified
     * ListeningPoint and returns a reference to the newly created SipProvider
     * object. The newly created SipProvider is implicitly attached to this
     * SipStack upon execution of this method, by adding the SipProvider to the
     * {@link SipStack#getSipProviders()} of this SipStack, once it has been
     * successfully created.
     *
     * @param listeningPoint the ListeningPoint the SipProvider is to
     * be attached to in order to send and Receive messages.
     * @return The peer SipProvider attached to this SipStack on the specified
     * ListeningPoint.
     * @throws ListeningPointUnavailableException thrown if another
     * SipProvider is already using the ListeningPoint.
     */
    public SipProvider createSipProvider(ListeningPoint listeningPoint)
    throws ObjectInUseException {
        if (listeningPoint == null)
            throw new NullPointerException("null listeningPoint");
        
	if (listeningPoint.sipProviderImpl != null) {
	    throw new ObjectInUseException
                    ("Provider already attached!");
        }
        
        SipProvider provider = new SipProvider(this);
        provider.setListeningPoint(listeningPoint);
        this.sipProviders.addElement(provider);
        return provider;
    }
        
    /**
     * Deletes the specified peer ListeningPoint attached to this SipStack. The
     * specified ListeningPoint is implicitly detached from this SipStack upon
     * execution of this method, by removing the ListeningPoint from the
     * {@link SipStack#getListeningPoints()} of this SipStack.
     *
     * @param listeningPoint the peer SipProvider to be deleted from
     * this SipStack.
     * @exception ObjectInUseException thrown if the specified peer
     * ListeningPoint cannot be deleted because the peer ListeningPoint is
     * currently in use.
     *
     * @since v1.1
     */
    public void deleteListeningPoint(ListeningPoint listeningPoint)
    throws ObjectInUseException {
        if (listeningPoint == null)
            throw new NullPointerException("null listeningPoint arg");
        ListeningPoint lip = (ListeningPoint) listeningPoint;
        // Stop the message processing thread in the listening point.
        lip.messageProcessor.stop();
        String key = lip.getKey();
        this.listeningPoints.remove(key);
    }
    
    /**
     * Deletes the specified peer SipProvider attached to this SipStack. The
     * specified SipProvider is implicitly detached from this SipStack upon
     * execution of this method, by removing the SipProvider from the
     * {@link SipStack#getSipProviders()} of this SipStack. Deletion of a
     * SipProvider does not automatically delete the ListeningPoint from the
     * SipStack.
     *
     * @param sipProvider the peer SipProvider to be deleted from
     * this SipStack.
     * @exception ObjectInUseException thrown if the specified peer
     * SipProvider cannot be deleted because the peer SipProvider is currently
     * in use.
     *
     */
    public void deleteSipProvider(SipProvider sipProvider)
    throws ObjectInUseException {
        
        if (sipProvider == null)
            throw new NullPointerException("null provider arg");
        SipProvider sipProviderImpl = (SipProvider) sipProvider;
        if (sipProviderImpl.listeningPoint.messageProcessor.inUse()) {
            throw new ObjectInUseException("Provider in use");
        }
        sipProviderImpl.sipListener = null;
        sipProviders.removeElement(sipProvider);
    }
    
    /**
     * Gets the IP Address that identifies this SipStack instance. Every Sip
     * Stack object must have an IP Address and only a single SipStack object
     * can service a single IP Address. This value is set using the Properties
     * object passed to the {@link
     * SipFactory#createSipStack} method upon
     * creation of the SIP Stack object.
     *
     * @return a string identifing the IP Address
     * @since v1.1
     */
    public String getIPAddress() {
        return super.getHostAddress();
    }
    
    /**
     * Returns an Iterator of existing ListeningPoints created by this
     * SipStack. All of the peer SipProviders of this SipStack will be
     * proprietary objects belonging to the same stack vendor.
     *
     * @return an Iterator containing all existing peer ListeningPoints created
     * by this SipStack. Returns an empty Iterator if no ListeningPoints exist.
     */
    public java.util.Enumeration getListeningPoints() {
        return this.listeningPoints.elements();
    }
    
    /**
     * Gets the listening point for a given transport and port.
     * @param port the communication port
     * @param transport the connection channel
     * @return the listening port
     */
    public ListeningPoint getListeningPoint(int port, String transport) {
        String key = ListeningPoint.makeKey
                (super.stackAddress, port, transport);
                
        if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
            Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                "getListeningPoint " + port + "/" + transport);
        }
        
        return (ListeningPoint) listeningPoints.get(key);
    }
    
    
    /**
     * Gets the outbound proxy specification. Return null if no outbound
     * proxy is specified.
     * @return the outbound proxy address
     */
    public String getOutboundProxy() {
        return this.outboundProxy;
    }
    
    /**
     * This method returns the value of the retransmission filter helper
     * function for User Agent Client and User Agent Server applications. This
     * value is set using the Properties object passed to the
     * {@link SipFactory#createSipStack} method upon
     * creation of the SIP Stack
     * object.
     * <p>
     * The default value of the retransmission filter boolean is
     * <var>false</var>.
     * When retransmissions are handled by the SipProvider the application will
     * not receive {@link Timeout#RETRANSMIT} notifications encapsulated in
     * {@link gov.nist.siplite.TimeoutEvent}'s. However an application will get
     * notified when a the underlying transaction expired with
     * {@link Timeout#TRANSACTION} notifications encapsulated in a
     * {@link gov.nist.siplite.TimeoutEvent}.</p>
     *
     * @return the value of the retransmission filter, true if the filter
     * is set false otherwise.
     * @since v1.1
     */
    public boolean isRetransmissionFilterActive() {
        return this.retransmissionFilter;
    }
    
    /**
     * Gets the Router object that identifies the default
     * Routing policy of this
     * SipStack. It also provides means to set an outbound proxy. This value is
     * set using the Properties object passed to the
     * {@link SipFactory#createSipStack} method upon
     * creation of the SIP Stack object.
     *
     * @return a the Router object identifying the Router policy.
     * @since v1.1
     */
    public Router getRouter() {
        return  super.getRouter();
    }
    
    /**
     * Returns an Iterator of existing peer SipProviders that have been
     * created by this SipStack. All of the peer SipProviders of this
     * SipStack will be proprietary objects belonging to the same stack vendor.
     *
     * @return an Iterator containing all existing peer SipProviders created
     * by this SipStack. Returns an empty Iterator if no SipProviders exist.
     */
    public Enumeration getSipProviders() {
        return this.sipProviders.elements();
    }
    
    /**
     * Gets the user friendly name that identifies this SipStack instance. This
     * value is set using the Properties object passed to the
     * {@link SipFactory#createSipStack} method upon
     * creation of the SIP Stack object.
     *
     * @return a string identifing the stack instance
     */
    public String getStackName() {
        return this.stackName;
    }
    
    
    /**
     * The default transport to use for via headers.
     * @return the default transport
     */
    public String getDefaultTransport() {
        if (isTransportEnabled("udp"))
            return "udp";
        else if (isTransportEnabled("tcp"))
            return "tcp";
        else
            return null;
    }
    
    
    
    /**
     * Invoked when an error has ocurred with a transaction.
     *
     * @param transactionErrorEvent Error event.
     */
    public void transactionErrorEvent
            (SIPTransactionErrorEvent transactionErrorEvent) {
        Transaction transaction =
                (Transaction) transactionErrorEvent.getSource();
        
    }
}