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