/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2009 Sun Microsystems, Inc. All rights reserved.
* Copyright (c) Ericsson AB, 2004-2008. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can obtain
* a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
* or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [year]
* [name of copyright owner]"
*
* Contributor(s):
*
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package com.ericsson.ssa.container;
import com.ericsson.ssa.config.annotations.Configuration;
import com.ericsson.ssa.config.annotations.UpdatePolicy;
import com.ericsson.ssa.config.annotations.UsagePolicy;
import com.ericsson.ssa.sip.Header;
import java.util.Collections;
import java.util.HashSet;
import java.util.ListIterator;
import java.util.Set;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.ericsson.ssa.sip.Layer;
import com.ericsson.ssa.sip.LayerHelper;
import com.ericsson.ssa.sip.SipServletRequestImpl;
import com.ericsson.ssa.sip.SipServletResponseImpl;
import com.ericsson.ssa.sip.ViaHeaderValidator;
import com.ericsson.ssa.sip.ViaImpl;
import com.ericsson.ssa.sip.dns.TargetResolver;
import com.ericsson.ssa.sip.dns.TargetTuple;
import java.util.concurrent.atomic.AtomicLong;
import com.ericsson.ssa.container.reporter.Reporter;
import com.ericsson.ssa.container.reporter.ReporterResolver;
import java.util.logging.Level;
import org.jvnet.glassfish.comms.util.LogUtil;
import java.util.logging.Logger;
public abstract class NetworkManager implements Layer {
private static NetworkManager instance = null;
private static final String GRIZZLY_ENABLE_PROPERTY = "sip.network.grizzly";
private AtomicLong _EasInvalidSipMessages = new AtomicLong();
private AtomicLong _EasReceivedSipRequests = new AtomicLong();
private AtomicLong _EasReceivedSipResponses = new AtomicLong();
private AtomicLong _EasSentSipRequests = new AtomicLong();
private AtomicLong _EasSentSipResponses = new AtomicLong();
private AtomicLong _Eas420Responses = new AtomicLong();
private Map<String,AtomicLong> reqInMap;
private Map<String,AtomicLong> reqOutMap;
private Map<String,AtomicLong> respInMap;
private Map<String,AtomicLong> respOutMap;
private Map<String,AtomicLong> _EasInvalidSipMessagesPerListener;
private Map<String,AtomicLong> _EasReceivedSipRequestsPerListener;
private Map<String,AtomicLong> _EasReceivedSipResponsesPerListener;
private Map<String,AtomicLong> _EasSentSipRequestsPerListener;
private Map<String,AtomicLong> _EasSentSipResponsesPerListener;
private Map<String, String> _listenerTupleMap;
private Map<String, String> _listenerAddressMap;
private Reporter _reporter;
public void setReporters(String reporters){
_reporter = ReporterResolver.getInstance().getReporter(reporters);
}
public Reporter getReporter(){
return _reporter;
}
private void initStatsCounters() {
_EasInvalidSipMessagesPerListener = new ConcurrentHashMap<String, AtomicLong>(3);
_EasReceivedSipRequestsPerListener = new ConcurrentHashMap<String, AtomicLong>(3);
_EasReceivedSipResponsesPerListener = new ConcurrentHashMap<String, AtomicLong>(3);
_EasSentSipRequestsPerListener = new ConcurrentHashMap<String, AtomicLong>(3);
_EasSentSipResponsesPerListener = new ConcurrentHashMap<String, AtomicLong>(3);
_listenerTupleMap = new ConcurrentHashMap<String, String>(3);
_listenerAddressMap = new ConcurrentHashMap<String, String>(3);
}
protected void registerForMonitoring(String ctx, TargetTuple tt) {
if (ctx == null || tt == null) {
return;
}
String simpletuple = normalizeTT(tt.getIP(), tt.getPort());
_listenerTupleMap.put(ctx, simpletuple);
_EasInvalidSipMessagesPerListener.put(simpletuple, new AtomicLong(0));
_EasReceivedSipRequestsPerListener.put(simpletuple, new AtomicLong(0));
_EasReceivedSipResponsesPerListener.put(simpletuple, new AtomicLong(0));
_EasSentSipRequestsPerListener.put(simpletuple, new AtomicLong(0));
_EasSentSipResponsesPerListener.put(simpletuple, new AtomicLong(0));
// only the last listener for that IP will be stored here
_listenerAddressMap.put(tt.getIP(), simpletuple);
}
protected String normalizeTT(String host , int port){
if (port == -1) {
return _listenerAddressMap.get(host);
}else {
return host + ":" + port;
}
}
private Logger log = LogUtil.SIP_LOGGER.getLogger();
private int _SipLinkTimeout = 10; // ms
private int _SipLinkTimeoutRetries = 25; // iterations
private int _SipLinkMaxQueueLen = 50;
private int _SipLinkWaitLockTimeout = 5000;
private long _SipLinkAliveTimeout = 120;
private boolean _errorResponseEnabled = false;
private Set<String> additionalHeaders = Collections.emptySet();
public abstract void next(SipServletRequestImpl req);
public abstract void next(SipServletResponseImpl resp);
public abstract void registerNext(Layer layer);
public abstract void dispatch(SipServletRequestImpl req);
public abstract void dispatch(SipServletResponseImpl resp);
public abstract Set<TargetTuple> getConnectionView();
public synchronized static NetworkManager getInstance() {
if (instance == null) {
//TODO remove in final version so only one Network manager is present
String grizzlyEnabledPropertyValue = System.getProperty(GRIZZLY_ENABLE_PROPERTY);
if (grizzlyEnabledPropertyValue != null &&
!Boolean.parseBoolean(grizzlyEnabledPropertyValue)) {
instance = new OLDNetworkManager();
} else {
instance = new GrizzlyNetworkManager();
}
instance.initMethodCounters();
instance.initStatsCounters();
}
return instance;
}
public boolean isRunning() {
return true;
}
// package access
void incrEasInvalidSipMessages() {
_EasInvalidSipMessages.incrementAndGet();
}
void incrEasReceivedSipRequests() {
_EasReceivedSipRequests.incrementAndGet();
}
void incrEasReceivedSipResponses() {
_EasReceivedSipResponses.incrementAndGet();
}
void incrEasSentSipRequests() {
_EasSentSipRequests.incrementAndGet();
}
void incrEasSentSipResponses() {
_EasSentSipResponses.incrementAndGet();
}
public long getEasInvalidSipMessages() {
return _EasInvalidSipMessages.longValue();
}
public long getEasReceivedSipRequests() {
return _EasReceivedSipRequests.longValue();
}
public long getEasReceivedSipResponses() {
return _EasReceivedSipResponses.longValue();
}
public long getEasSentSipRequests() {
return _EasSentSipRequests.longValue();
}
public long getEasSentSipResponses() {
return _EasSentSipResponses.longValue();
}
public long getEas420ResOutSipMessages(){
return _Eas420Responses.longValue();
}
void incrSipMessageCount(Map<String, AtomicLong> msgmap, String host, int port) {
String tt = normalizeTT(host, port);
/* If tt is null then we are using
* an IP that is not configured as a listener. If 0.0.0.0 is used
* as the listener address then listener specific monitoring
* values will not be available.
*/
if (tt != null) {
AtomicLong l = msgmap.get(tt);
if (l != null) {
l.incrementAndGet();
}
}
}
void incrEasInvalidSipMessages(String host, int port) {
incrEasInvalidSipMessages();
incrSipMessageCount (_EasInvalidSipMessagesPerListener, host, port);
}
void incrEasReceivedSipRequests(String host, int port) {
incrEasReceivedSipRequests();
incrSipMessageCount(_EasReceivedSipRequestsPerListener, host, port);
}
void incrEasReceivedSipResponses(String host, int port) {
incrEasReceivedSipResponses();
incrSipMessageCount(_EasReceivedSipResponsesPerListener, host, port);
}
void incrEasSentSipRequests(String host, int port) {
incrEasSentSipRequests();
incrSipMessageCount(_EasSentSipRequestsPerListener, host, port);
}
void incrEasSentSipResponses(String host, int port) {
incrEasSentSipResponses();
incrSipMessageCount(_EasSentSipResponsesPerListener, host, port);
}
public long getEasInvalidSipMessages(String id) {
return _EasInvalidSipMessagesPerListener.get(_listenerTupleMap.get(id)).longValue();
}
public long getEasReceivedSipRequests(String id) {
return _EasReceivedSipRequestsPerListener.get(_listenerTupleMap.get(id)).longValue();
}
public long getEasReceivedSipResponses(String id) {
return _EasReceivedSipResponsesPerListener.get(_listenerTupleMap.get(id)).longValue();
}
public long getEasSentSipRequests(String id) {
return _EasSentSipRequestsPerListener.get(_listenerTupleMap.get(id)).longValue();
}
public long getEasSentSipResponses(String id) {
return _EasSentSipResponsesPerListener.get(_listenerTupleMap.get(id)).longValue();
}
void initMethodCounters() {
reqInMap = new ConcurrentHashMap<String,AtomicLong>(14);
reqOutMap = new ConcurrentHashMap<String,AtomicLong>(14);
respInMap = new ConcurrentHashMap<String,AtomicLong>(6);
respOutMap = new ConcurrentHashMap<String,AtomicLong>(6);
reqInMap.put("INVITE",new AtomicLong(0));
reqInMap.put("ACK",new AtomicLong(0));
reqInMap.put("PRACK",new AtomicLong(0));
reqInMap.put("BYE",new AtomicLong(0));
reqInMap.put("CANCEL",new AtomicLong(0));
reqInMap.put("SUBSCRIBE",new AtomicLong(0));
reqInMap.put("NOTIFY",new AtomicLong(0));
reqInMap.put("MESSAGE",new AtomicLong(0));
reqInMap.put("REGISTER",new AtomicLong(0));
reqInMap.put("OPTIONS",new AtomicLong(0));
reqInMap.put("UPDATE",new AtomicLong(0));
reqInMap.put("REFER",new AtomicLong(0));
reqInMap.put("INFO",new AtomicLong(0));
reqInMap.put("OTHER",new AtomicLong(0));
reqInMap.put("PUBLISH",new AtomicLong(0));
reqOutMap.put("INVITE",new AtomicLong(0));
reqOutMap.put("ACK",new AtomicLong(0));
reqOutMap.put("PRACK",new AtomicLong(0));
reqOutMap.put("BYE",new AtomicLong(0));
reqOutMap.put("CANCEL",new AtomicLong(0));
reqOutMap.put("SUBSCRIBE",new AtomicLong(0));
reqOutMap.put("NOTIFY",new AtomicLong(0));
reqOutMap.put("MESSAGE",new AtomicLong(0));
reqOutMap.put("REGISTER",new AtomicLong(0));
reqOutMap.put("OPTIONS",new AtomicLong(0));
reqOutMap.put("UPDATE",new AtomicLong(0));
reqOutMap.put("REFER",new AtomicLong(0));
reqOutMap.put("INFO",new AtomicLong(0));
reqOutMap.put("OTHER",new AtomicLong(0));
reqOutMap.put("PUBLISH",new AtomicLong(0));
respInMap.put("1XX",new AtomicLong(0));
respInMap.put("2XX",new AtomicLong(0));
respInMap.put("3XX",new AtomicLong(0));
respInMap.put("4XX",new AtomicLong(0));
respInMap.put("5XX",new AtomicLong(0));
respInMap.put("6XX",new AtomicLong(0));
respOutMap.put("1XX",new AtomicLong(0));
respOutMap.put("2XX",new AtomicLong(0));
respOutMap.put("3XX",new AtomicLong(0));
respOutMap.put("4XX",new AtomicLong(0));
respOutMap.put("5XX",new AtomicLong(0));
respOutMap.put("6XX",new AtomicLong(0));
}
public Map<String, AtomicLong> getReqInMethodCounters() {
return new ConcurrentHashMap<String, AtomicLong>(reqInMap);
}
public Map<String, AtomicLong> getReqOutMethodCounters() {
return new ConcurrentHashMap<String, AtomicLong>(reqOutMap);
}
public Map<String, AtomicLong> getRespInStatCodeCounters() {
return new ConcurrentHashMap<String, AtomicLong>(respInMap);
}
public Map<String, AtomicLong> getRespOutStatCodeCounters() {
return new ConcurrentHashMap<String, AtomicLong>(respOutMap);
}
void incrReqInMethodCounter(String method) {
if (method == null || "".equals(method))
return;
String mthd = method.toUpperCase();
if (reqInMap.get(mthd) == null) {
reqInMap.get("OTHER").incrementAndGet();
return;
}
reqInMap.get(mthd).incrementAndGet();
}
void incrReqOutMethodCounter(String method) {
if (method == null || "".equals(method))
return;
String mthd = method.toUpperCase();
if (reqOutMap.get(mthd) == null) {
reqOutMap.get("OTHER").incrementAndGet();
return;
}
reqOutMap.get(mthd).incrementAndGet();
}
void incrRespInStatCodeCounter(int code) {
if (code < 100 || code > 699)
return;
int statCode = code / 100;
switch (statCode) {
case 1:
respInMap.get("1XX").incrementAndGet();
break;
case 2:
respInMap.get("2XX").incrementAndGet();
break;
case 3:
respInMap.get("3XX").incrementAndGet();
break;
case 4:
respInMap.get("4XX").incrementAndGet();
if (code == 420){
_Eas420Responses.incrementAndGet();
}
break;
case 5:
respInMap.get("5XX").incrementAndGet();
break;
case 6:
respInMap.get("6XX").incrementAndGet();
break;
}
}
void incrRespOutStatCodeCounter(int code) {
if (code < 100 || code > 699)
return;
int statCode = code / 100;
switch (statCode) {
case 1:
respOutMap.get("1XX").incrementAndGet();
break;
case 2:
respOutMap.get("2XX").incrementAndGet();
break;
case 3:
respOutMap.get("3XX").incrementAndGet();
break;
case 4:
respOutMap.get("4XX").incrementAndGet();
break;
case 5:
respOutMap.get("5XX").incrementAndGet();
break;
case 6:
respOutMap.get("6XX").incrementAndGet();
break;
}
}
/**
* Defines timeout value in milliseconds for a single write operation of a
* sip link.
* </p>
* Range: 1-50ms
*
* @param timeout
*/
@Configuration (key="WriteTimeoutInMillis", node="/SipService/SipProtocol/SipLink")
public void setSipLinkTimeout(int timeout) {
if ((timeout > 0) && (timeout <= 50)) {
_SipLinkTimeout = timeout;
} else {
if (log.isLoggable(Level.WARNING)) {
log.log(Level.WARNING, "sip.stack.network.bad_sip_link_timeout", new Object[] { timeout });
}
}
}
/**
* Defines timeout value in milliseconds for a single write operation of a
* sip link.
*
* @return timeout for write operation of a sip link
*/
public int getSipLinkTimeout() {
return _SipLinkTimeout;
}
/**
* Defines timeout value in seconds for keeping an inactive link. If no
* traffic has been generated on that link for that time period the link is
* closed.
*
* @return timeout value in seconds for keeping an inactive link
*/
public long getSipLinkAliveTimeout() {
return _SipLinkAliveTimeout;
}
// this code should be refactored; the code should be moved to the Header class
// and the header class should get a configuration hook (e.g., register itself for configuration).
// Also the way it is used here the ExtraMultiLineHeaderNames is stuck to being a property and will not
// be resolved if ever changed to a attribute. This might be OK.
@Configuration (usage=UsagePolicy.IGNORE, key="ExtraMultiLineHeaderNames", node="/SipService/SipProtocol")
public void addHeaders(String configuredHeaders) {
HeaderSet additionalHeaderSet = new HeaderSet(configuredHeaders);
Set<String> tempAdditionalHeaders = additionalHeaderSet.getSet();
Set<String> newHeaders = new HashSet<String>(tempAdditionalHeaders);
newHeaders.removeAll(this.additionalHeaders);
Set<String> removedHeaders = new HashSet<String>(this.additionalHeaders);
removedHeaders.removeAll(tempAdditionalHeaders);
for (String header : removedHeaders) {
Header.MULTI_LINE_HEADER_SET.remove(header);
}
for (String header : newHeaders) {
Header.MULTI_LINE_HEADER_SET.add(header);
}
this.additionalHeaders = tempAdditionalHeaders;
if (log.isLoggable(Level.FINE)) {
StringBuffer additionalHeadersString = new StringBuffer();
additionalHeadersString.append("extra Multiline Headers: <");
boolean first = true;
for (String header : tempAdditionalHeaders) {
if (first) {
first = false;
} else {
additionalHeadersString.append(",");
}
additionalHeadersString.append(header);
}
additionalHeadersString.append(">");
log.fine(additionalHeadersString.toString());
}
}
/**
* Inner class to enable automatic conversion of
* a comma seperated string of headernames to a set
* of properly formatted names.
*
*/
public class HeaderSet {
HashSet<String> headerSet = new HashSet<String>();
public HeaderSet(String aCommaSeparatedHeaderSet) {
String[] headers = aCommaSeparatedHeaderSet.split(",");
for (int i = 0; i < headers.length; i++) {
String header = headers[i].trim();
if (!"".equals(header)) {
headerSet.add(Header.format(header));
}
}
}
public Set<String> getSet() {
return headerSet;
}
}
/**
* Defines timeout value in seconds for keeping an inactive link. If no
* traffic has been generated on that link for that time period the link is
* closed.
*
* @param timeout
* value in seconds for keeping an inactive link
*/
@Configuration (key="ConnectionAliveTimeoutInSeconds", node="/SipService/SipProtocol/SipLink")
public void setSipLinkAliveTimeout(long timeout) {
_SipLinkAliveTimeout = timeout;
}
/**
* Defines the number of retries to perform of a single write operation of a
* sip link.
* </p>
* Range: 1-25
* </p>
* The maximum time a link will try for success of a write operation is
* defined by SipLinkTimeout * SipLinkTimeoutRetries milliseconds.
*
* @param retries
*/
@Configuration (key="WriteTimeoutRetries", node="/SipService/SipProtocol/SipLink")
public void setSipLinkTimeoutRetries(int retries) {
if ((retries > 0) && (retries <= 25)) {
_SipLinkTimeoutRetries = retries;
} else {
if (log.isLoggable(Level.WARNING)) {
log.log(Level.WARNING, "sip.stack.network.bad_sip_link_timeout_retries", new Object[] { retries });
}
}
_SipLinkTimeoutRetries = retries;
}
/**
* Defines the number of retries to perform of a single write operation of a
* sip link.
* </p>
* The maximum time a link will try for success of a write operation is
* defined by SipLinkTimeout * SipLinkTimeoutRetries milliseconds.
*
* @return the number of retries to perform of a single write operation of a
* sip link.
*/
public int getSipLinkTimeoutRetries() {
return _SipLinkTimeoutRetries;
}
/**
* Returns the maximum number of connect/write tasks that can be queued in a
* link.
*
* @return the maximum number of connect/write tasks that can be queued in a
* link
*/
public int getSipLinkMaxQueueLength() {
return _SipLinkMaxQueueLen;
}
/**
* Returns the maximum number of connect/write tasks that can be queued in a
* link.
*
* @param length
* the maximum number of connect/write tasks that can be queued in
* a link
*/
@Configuration (key="MaxQueueLength", node="/SipService/SipProtocol/SipLink")
public void setSipLinkMaxQueueLength(int length) {
_SipLinkMaxQueueLen = length;
}
/**
* Returns the maximum time a thread will wait to get an exclusive lock for a
* sip link.
*
* @return the maximum time a thread will wait to get an exclusive lock for a
* sip link.
*/
public int getSipLinkWaitLockTimeout() {
return _SipLinkWaitLockTimeout;
}
/**
* Sets the maximum time a thread will wait to get an exclusive lock for a
* sip link.
*
* @param timeout
*/
@Configuration (key="SipLinkWaitLockTimeout", node="/SipService/SipProtocol/SipLink",
usage=UsagePolicy.IGNORE, update=UpdatePolicy.STARTUP) //This was never before configurable!
public void setSipLinkWaitLockTimeout(int timeout) {
_SipLinkWaitLockTimeout = timeout;
}
/**
* Returns whether a malformed request should be handled by sending a proper
* informational error response.
*
* @return
*/
public Boolean isErrorResponseEnabled() {
return _errorResponseEnabled;
}
/**
* Setter of the <code>ErrorResponseEnabled</code> property.
*
* @param responseEnabled
*/
@Configuration (key="ErrorResponseEnabled", node="/SipService/SipProtocol")
public void setErrorResponseEnabled(Boolean responseEnabled) {
_errorResponseEnabled = responseEnabled;
}
public SipServletResponseImpl validateAndModifyIncomingVia(SipServletRequestImpl req) {
String vstr = req.getHeader(Header.VIA);
if (vstr == null) {
SipServletResponseImpl resp = req.createTerminatingResponse(400, "Missing via in request");
return resp;
}
ViaImpl via = null;
try {
via = new ViaImpl(vstr);
}
catch(Throwable t){
SipServletResponseImpl resp = req.createTerminatingResponse(400, "Via cannot be parsed");
return resp;
}
String viaTransport = via.getTransport();
String remoteTransport = req.getRemote().getProtocol().name();
if ( viaTransport!= null && remoteTransport != null && !viaTransport.equalsIgnoreCase(remoteTransport)) {
SipServletResponseImpl resp = req.createTerminatingResponse(400, "Mismatch in VIA Transport");
return resp;
}
TargetResolver.updateVia(req, via);
return null;
}
}