Package com.ericsson.ssa.sip

Source Code of com.ericsson.ssa.sip.SipServletMessageImpl

/*
* 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.sip;

import com.ericsson.ssa.config.ConvergedContextConfig;
import com.ericsson.ssa.dd.ServletMappingCriteria;
import com.ericsson.ssa.sip.PathNode.Type;
import com.ericsson.ssa.sip.dns.TargetTuple;
import com.ericsson.ssa.utils.ParserHelper;
import com.ericsson.ssa.utils.StringDataSource;

import java.io.ByteArrayOutputStream;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.UnsupportedEncodingException;

import java.net.InetSocketAddress;

import java.nio.ByteBuffer;
import java.nio.charset.Charset;

import java.security.Principal;
import java.security.cert.X509Certificate;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Locale;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;

import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.internet.MimeMultipart;
import javax.mail.util.ByteArrayDataSource;

import javax.servlet.sip.Address;
import javax.servlet.sip.Parameterable;
import javax.servlet.sip.ServletParseException;
import javax.servlet.sip.SipApplicationSession;
import javax.servlet.sip.SipSession;

import java.util.logging.Level;
import java.util.logging.Logger;
import org.jvnet.glassfish.comms.util.LogUtil;

/**
* @author ekrigro TODO To change the template for this generated type comment
*         go to Window - Preferences - Java - Code Style - Code Templates
*/
public abstract class SipServletMessageImpl
    implements SipServletMessageInterface, ServletMappingCriteria,
        Externalizable {
    // NOTE! If you ADD | REMOVE check cloneShallowHelper() and update it!
    protected static final EnumerationConverter<String> empty = new EnumerationConverter<String>();
    private static boolean _NoJSR289isCommitted = Boolean.getBoolean(
            "org.glassfish.comms.nojsr289iscommitted");
    private static final ConcurrentHashMap<Long, Boolean> sentMap = new ConcurrentHashMap<Long, Boolean>();
    protected static DialogManager dialogManager = DialogManager.getInstance();
    private static final Logger _logger = LogUtil.SIP_LOGGER.getLogger();

    // protected String protocol;
    protected SipFactoryImpl _sf = SipFactoryImpl.getInstance();
    protected SipSessionBase _session = null;
    protected String _method = "";
    protected String _protocol;
    protected String _default_enc = "UTF8";
    protected String _content_enc = _default_enc;
    protected byte[] _content_byte = null;
    protected Object _content_obj = null;
    protected Map<String, Object> attrib = null;

    // Currently no systemAttrib exist today (only be set by container). See SSA.
    protected Map<String, Object> systemAttrib = null;
    protected HashMap<String, Header> headerMap = new HashMap<String, Header>();
    protected List<Dispatcher> _transactionStack = new ArrayList<Dispatcher>();
    protected List<Dispatcher> _applicationStack = new ArrayList<Dispatcher>();
    protected List<String> _roles = new ArrayList<String>(2);
    protected int _content_byte_offset = 0;
    protected TargetTuple _remote;
    protected volatile OutboundInterface oi;
    protected TargetTuple _initialRemote; //Initial Remote Address.
    protected TargetTuple _remoteHop; //Remote Address of the current hop.
    protected InetSocketAddress _local;
    protected Type _Type = Type.Undefined;
    protected boolean _IsContactIndicated = false;
    protected boolean _headersComplete = false;
    protected boolean _messageComplete = false;
    protected DialogFragment _dialog = null;
    protected String _fragmentId = DialogFragment.DEFAULT_FRAGMENT_ID;
    private Principal _principal = null;
    private String _user = null;
    private X509Certificate[] _clientCert = null;   
    protected SipMessageType _messageType;

    // Cached values for ByteBuffer concatenated write
    private byte[] toBufferContent = null;
    private int toBufferContentOffset = -1;
    private int toBufferHeaderOffset = 0;
    protected String _beKey;
    protected HeaderForm headerForm = HeaderForm.DEFAULT;
   
    /** Flag indicating that the remote has already been resolved. */
    private boolean remoteResolved = false;

    protected final Object byteBufferLock = new Object();
   
    /** Used in conjuction with setRemoteResovled() */
    public enum RetryPolicy {
        /** Dont retry. Could be used by the OutBound connections where no retry is needed. */
        DONT_RETRY,
        /** Retry without resolving message */
        RETRY,
        /** Resolve and retry */
        RESOLVE_AND_RETRY;
    }
   
    /** Used in conjuction with setRemoteResovled() */
    private RetryPolicy retryPolicy = RetryPolicy.RETRY;

    public SipServletMessageImpl() {
    }

    public SipServletMessageImpl(String protocol) {
        _protocol = protocol;
    }

    public SipServletMessageImpl(String method, String protocol) {
        _protocol = protocol;
        _method = method;
    }

    public SipMessageType getMessageType() {
        return _messageType;
    }

    /**
     * TODO: This implementation is out of date.
     * This should be updated if/when message are serialized/deserialized.
     */
    public void writeExternal(ObjectOutput output) throws IOException {
        try {
            // _method, _protocol, _content_byte, headerMap,
            // _remote, _initialRemote, _remoteHop, _local
            output.writeUTF(_method); // TODO change to writeInt + writeBytes and
                                      // check performance

            output.writeUTF(_protocol);

            if (_content_byte != null) {
                output.writeInt(_content_byte.length);
                output.write(_content_byte);
            } else {
                output.writeInt(0);
            }

            output.writeInt(headerMap.size());

            Iterator<String> key = headerMap.keySet().iterator();
            Iterator<Header> value = headerMap.values().iterator(); // TODO check
                                                                    // the Header
                                                                    // serialization

            while (key.hasNext()) {
                output.writeUTF(key.next());
                output.writeObject(value.next());
            }

            output.writeObject(_remote); // TODO check the TargetTuple
                                         // serialization

            output.writeObject(_initialRemote); // TODO check the TargetTuple
                                                // serialization

            output.writeObject(_remoteHop); // TODO check the TargetTuple
                                            // serialization

            output.writeObject(_local); // Is the InetSocketAddress serialization
                                        // fast!?
                                        // TODO _IsContactIndicated, _headersComplete, _messageComplete
                                        // output.writeBoolean(IsContactIndicated)
            output.writeObject(oi);
        } catch (Exception ignore) {
        }
    }

    /**
     * TODO: This implementation is out of date.
     * This should be updated if/when message are serialized/deserialized.
     */
    public void readExternal(ObjectInput input) throws IOException {
        try {
            // method, _protocol, _content_byte, headerMap, _remote, _local
            _method = input.readUTF();
            _protocol = input.readUTF();

            int cbl = input.readInt();
            _content_byte = new byte[cbl];
            input.read(_content_byte);

            int hml = input.readInt();

            if (headerMap == null) {
                headerMap = new HashMap<String, Header>(hml);
            }

            for (int i = 0; i < hml; i++) {
                String key = input.readUTF();
                Header value = (Header) input.readObject();
                headerMap.put(key, value);
            }

            _remote = (TargetTuple) input.readObject();
            _initialRemote = (TargetTuple) input.readObject();
            _remoteHop = (TargetTuple) input.readObject();
            _local = (InetSocketAddress) input.readObject();
            oi = (OutboundInterface) input.readObject();
            _transactionStack = new ArrayList<Dispatcher>();
            _applicationStack = new ArrayList<Dispatcher>();
            _sf = SipFactoryImpl.getInstance();
        } catch (Exception ignore) {
        }
    }

    /**
     * The message coming from caller (Type.Caller) or callee (Type.Callee). If
     * its not determined it could also be Type.Undefined
     *
     * @return the direction of the message. Coming from caller or callee.
     */
    public Type isDirection() {
        if ((_Type != Type.Caller) && (_Type != Type.Callee)) {
            try {
                if (getDialog() != null) {
                    if (getDialog().isMessageFromCaller(this)) {
                        _Type = Type.Caller;
                    } else {
                        _Type = Type.Callee;
                    }
                } else {
                    return Type.Undefined;
                }
            } catch (NotEqualDialogException e) {
                return Type.Undefined;
            }
        }

        return _Type;
    }

    public void setDirection(Type direction) {
        _Type = direction;
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#getFrom()
     */
    public Address getFromImpl() {
        try {
            return headerMap.get(Header.FROM).getAddressValue();
        } catch (ServletParseException e) {
            throw new LazyParsingException(400, e.getMessage(), e);
        }
    }

    public Address getFrom() {
        try {
            return getAddressHeader(Header.FROM);

            //return headerMap.get(Header.FROM).getAddressValue();
        } catch (ServletParseException e) {
            throw new LazyParsingException(400, e.getMessage(), e);
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#getTo()
     */
    public Address getToImpl() {
        try {
            return headerMap.get(Header.TO).getAddressValue();
        } catch (ServletParseException e) {
            throw new LazyParsingException(400, e.getMessage(), e);
        }
    }

    public Address getTo() {
        try {
            //return headerMap.get(Header.TO).getAddressValue();
            return getAddressHeader(Header.TO);
        } catch (ServletParseException e) {
            throw new LazyParsingException(400, e.getMessage(), e);
        }
    }

    public String createTag(String name) {
        String tag = _sf.createTag();

        try {
            Address adr = headerMap.get(name).getAddressValue();
            ((AddressImpl) adr).setReadOnly(false);
            adr.setParameter(AddressImpl.TAG_PARAM, tag);
            ((AddressImpl) adr).setReadOnly(true);
        } catch (ServletParseException e) {
            return null;
        }

        return tag;
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#getMethod()
     */
    public String getMethod() {
        return _method; // The req and response must set the method.
    }

    // Container internal when the message is parsed
    public void setMethod(String method) {
        _method = method;
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#getProtocol()
     */
    public String getProtocol() {
        return _protocol;
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#getHeader(java.lang.String)
     */
    public String getHeader(String name) {
        Header header = headerMap.get(Header.format(name));

        if (header == null) {
            return null;
        }

        return header.getValue();
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#getHeaders(java.lang.String)
     */
    public ListIterator<String> getHeaders(String name) {
        String pretty = Header.format(name);

        if (headerMap.containsKey(pretty)) {
            return headerMap.get(pretty).getValues();
        }

        return Collections.EMPTY_LIST.listIterator();
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#getHeaderNames()
     */
    public Iterator<String> getHeaderNames() {
        if ((headerForm == HeaderForm.DEFAULT) ||
                (headerForm == HeaderForm.LONG)) {
            return headerMap.keySet().iterator();
        } else { // COMPACT

            HashSet<String> headerNames = new HashSet<String>();
            headerNames.addAll(headerMap.keySet());

            for (String s : Header.LONG_TO_SHORT_MAP.keySet()) {
                if (headerNames.contains(s)) {
                    headerNames.remove(s);
                    headerNames.add(Header.LONG_TO_SHORT_MAP.get(s));
                }
            }

            return headerNames.iterator();
        }
    }

    /*
     * (non-Javadoc) Overwrites
     *
     * @see javax.servlet.sip.SipServletMessage#setHeader(java.lang.String,
     *      java.lang.String)
     */
    public void setHeader(String name, String value) {
        String pretty = Header.format(name);
        boolean system = Header.isSystemHeader(pretty, this);
        String crap = "\r\n\t";

        if ((value.indexOf(crap)) > 0) {
            value = value.replaceAll(crap, "");
        }

        if (system) {
            throw new IllegalArgumentException(Header.ILLEGAL_ACCESS);
        }

        validateMessageCommitted();

        setPrettyHeader(pretty, value);
    }

    public void setPrettyHeader(String pretty, String value) {
        Header header = null;

        if (headerMap.containsKey(pretty)) {
            header = headerMap.get(pretty);
            header.removeValues();
            header.setValue(value, true);
        } else {
            header = Header.createFormatted(pretty, this);
            header.setValue(value, true);
            headerMap.put(pretty, header);
        }
    }

    /*
     * (non-Javadoc) adds
     *
     * @see javax.servlet.sip.SipServletMessage#addHeader(java.lang.String,
     *      java.lang.String)
     */
    public void addHeader(String name, String value) {
        Header header = null;
        String pretty = Header.format(name);
        boolean system = Header.isSystemHeader(pretty, this);

        if (system) {
            throw new IllegalArgumentException(Header.ILLEGAL_ACCESS);
        }

        validateMessageCommitted();

        if (headerMap.containsKey(pretty)) {
            header = headerMap.get(pretty);
            header.setValue(value, false);
        } else {
            header = Header.createFormatted(pretty, this);
            header.setValue(value, false);
            headerMap.put(pretty, header);
        }
    }

    public void addHeader(Header header) {
        Header in = headerMap.get(header.getName());

        if (in != null) {
            in.merge(header);
        } else {
            headerMap.put(header.getName(), header);
        }
    }

    public void setHeader(Header header) {
        headerMap.put(header.getName(), header);
    }

    /*
     * public String getHeader(Header header) { Header in =
     * headerMap.get(header.getName()); if (in != null) return in.toString();
     * return null; }
     */
    public Header getRawHeader(String header) {
        return headerMap.get(header);
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#removeHeader(java.lang.String)
     */
    public void removeHeader(String name) {
        String pretty = Header.format(name);

        if (Header.isSystemHeader(pretty, this)) {
            throw new IllegalArgumentException(Header.ILLEGAL_ACCESS);
        }

        validateMessageCommitted();

        headerMap.remove(pretty);
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#getAddressHeader(java.lang.String)
     */
    public Address getAddressHeader(String name) throws ServletParseException {
      if (allowFromToChange(name)) {
            AddressImpl impl = getAddressHeaderImpl(name);
            return new AddressPolicyProxy(name, impl, getSessionImpl(), getDialog());
      } else {
          return getAddressHeaderImpl(name);
      }
    }
   
    private boolean allowFromToChange(String name) {
      return ConvergedContextConfig.ALLOW_FROM_CHANGE &&
             (Header.FROM.equals(name) || Header.TO.equals(name));
    }

    /*
     *
     */
    public AddressImpl getAddressHeaderImpl(String name)
        throws ServletParseException {
        Header address = headerMap.get(Header.format(name));

        if (address == null) {
            return null;
        }

        AddressImpl addressHeaderImpl = (AddressImpl) address.getAddressValue();

        if (addressHeaderImpl != null) {
            addressHeaderImpl.setModifiable(_isCommitted());
        }

        if (name.equals(Header.CONTACT) ) {      
            if (addressHeaderImpl != null) {
                addressHeaderImpl.setReadOnly(false);
            }
        }               
       
        return addressHeaderImpl;
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#getAddressHeaders(java.lang.String)
     */
    public ListIterator<Address> getAddressHeaders(String name)
        throws ServletParseException {
        String pretty = Header.format(name);
        boolean systemHeader = Header.isSystemHeader(pretty, this);

        if (headerMap.containsKey(pretty)) {
            return new SipListIterator<Address>(headerMap.get(pretty)
                                                         .getAddressValues(),
                systemHeader);
        } else {
            return Collections.EMPTY_LIST.listIterator();

            /*
            Header header = Header.createFormated(pretty, this);
            headerMap.put(pretty, header);

            return new SipListIterator<Address>(headerMap.get(pretty)
                                                         .getAddressValues(),
                systemHeader);
            */
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#setAddressHeader(java.lang.String,
     *      javax.servlet.sip.Address)
     */
    public void setAddressHeader(String name, Address value) {
        Header header = null;
        String pretty = Header.format(name);
        boolean system = Header.isSystemHeader(pretty, this);

        if (system) {
            throw new IllegalArgumentException(Header.ILLEGAL_ACCESS);
        }

        if (!(value instanceof AddressImpl)) {
            value = (AddressImpl) ((AddressImplInstance) value).getAddressImpl();
        }

        validateMessageCommitted();

        if (headerMap.containsKey(pretty)) {
            header = headerMap.get(pretty);
            header.removeValues();
            header.setAddressValue(value, false);
        } else {
            header = Header.createFormatted(pretty, this);
            header.setAddressValue(value, false);
            headerMap.put(pretty, header);
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#addAddressHeader(java.lang.String,
     *      javax.servlet.sip.Address, boolean)
     */
    public void addAddressHeader(String name, Address value, boolean first) {
        Header header = null;
        String pretty = Header.format(name);
        boolean system = Header.isSystemHeader(pretty, this);

        if (system) {
            throw new IllegalArgumentException(Header.ILLEGAL_ACCESS);
        }

        if (headerMap.containsKey(pretty)) {
            header = headerMap.get(pretty);
            header.setAddressValue(value, first);
        } else {
            // already formated using createFormated
            header = Header.createFormatted(pretty, this);
            header.setAddressValue(value, first);
            headerMap.put(pretty, header);
        }
    }

    public void addParameterableHeader(String name, Parameterable param,
        boolean first) {
        Header header = null;
        String pretty = Header.format(name);
        boolean system = Header.isSystemHeader(pretty, this);

        if (system) {
            throw new IllegalArgumentException(Header.ILLEGAL_ACCESS);
        }

        if (!ParameterableImpl.isParameterableHeader(pretty, param))
            throw new IllegalArgumentException("Specified header isn't defined to hold Parameterable value");

        if (headerMap.containsKey(pretty)) {
            header = headerMap.get(pretty);
            header.setParameterableValue(param, first);
        } else {
            // already formated using createFormated
            header = Header.createFormatted(pretty, this);
            header.setParameterableValue(param, first);
            headerMap.put(pretty, header);
        }
    }

    public Parameterable getParameterableHeader(String name)
        throws ServletParseException {
        if (name == null)
            throw new NullPointerException("Argument name is null");
        String pretty = Header.format(name);
        if (!ParameterableImpl.isParameterableHeader(pretty,null))
            throw new ServletParseException(" Header can not be parsed as Parameterable");
        Header parameterable = headerMap.get(pretty);

        if (parameterable == null) {
            return null;
        }

        ParameterableImpl pi = (ParameterableImpl) parameterable.getParameterableValue();
        if (pi != null) {
            pi.setModifiable(_isCommitted());
        }
        return pi;
    }

    public ListIterator<?extends Parameterable> getParameterableHeaders(
        String name) throws ServletParseException {
        if (name == null)
            throw new NullPointerException("Argument name is null");
        String pretty = Header.format(name);
        if (!ParameterableImpl.isParameterableHeader(pretty,null))
            throw new ServletParseException(" Header can not be parsed as Parameterable");
        boolean systemHeader = Header.isSystemHeader(pretty, this);

        if (headerMap.containsKey(pretty)) {
            return new SipListIterator<Parameterable>(headerMap.get(pretty)
                                                               .getParameterableValues(),
                systemHeader);
        } else {
            return Collections.EMPTY_LIST.listIterator();
        }
    }

    public void setParameterableHeader(String name, Parameterable param) {
        Header header = null;
        String pretty = Header.format(name);
        boolean system = Header.isSystemHeader(pretty, this);

        if (system) {
            throw new IllegalArgumentException(Header.ILLEGAL_ACCESS);
        }
        if (!ParameterableImpl.isParameterableHeader(pretty, param))
            throw new IllegalArgumentException("Specified header isn't defined to hold Parameterable value");

        validateMessageCommitted();

        if (headerMap.containsKey(pretty)) {
            header = headerMap.get(pretty);
            header.removeValues();
            header.setParameterableValue(param, false);
        } else {
            header = Header.createFormatted(pretty, this);
            header.setParameterableValue(param, false);
            headerMap.put(pretty, header);
        }
    }

    /* To be implemented */
    public HeaderForm getHeaderForm() {
        return headerForm;
    }

    public void setHeaderForm(HeaderForm form) {
        validateMessageCommitted();
        headerForm = form;
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#getCallId()
     */
    public String getCallId() {
        return headerMap.get(Header.CALL_ID).getValue();
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#getExpires()
     */
    public int getExpires() {
        Header header = headerMap.get(Header.EXPIRES);

        if (header == null) {
            return -1;
        }

        String exp = header.getValue();

        if (exp == null) {
            return -1;
        }

        return Integer.parseInt(exp);
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#setExpires(int)
     */
    public void setExpires(int seconds) {
        // TODO Could be optimized since no pretty print is required in this
        // case
        setPrettyHeader(Header.EXPIRES, String.valueOf(seconds));
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#getCharacterEncoding()
     */
    public String getCharacterEncoding() {
        Header h = headerMap.get(Header.CONTENT_ENCODING);

        if (h != null) {
            return h.getValue();
        }

        // This method should return null if the message does not specify
        // a character encoding. The service will use UTF-8 as default, SSA1.0.
        Header sh = headerMap.get(Header.CONTENT_TYPE);

        if (sh != null) {
            String value = sh.getValue();

            if (value != null) {
                _content_enc = ParserHelper.getValue(value, "charset", ";");
            }

            if (_content_enc != null) {
                return _content_enc;
            }
        }

        return null;
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#setCharacterEncoding(java.lang.String)
     */
    public void setCharacterEncoding(String enc)
        throws UnsupportedEncodingException {
        if (!Charset.isSupported(enc)) {
            throw new UnsupportedEncodingException("CharSet: " + enc);
        }
        _setCharacterEncoding(enc);
    }

    protected void _setCharacterEncoding(String enc) {
        validateMessageCommitted();

        Header ceh = headerMap.get(Header.CONTENT_ENCODING);
        if (ceh == null) {
            ceh = new MultiLineHeader(Header.CONTENT_ENCODING, false);
            headerMap.put(Header.CONTENT_ENCODING,ceh);
        } else {
            ceh.removeValues();
        }
        ceh.setValue(enc,false);

        // TODO see what encodings we should support
        // Perfect thing to write in a SAD
        Header h = headerMap.get(Header.CONTENT_TYPE);

        if (h != null) {
            String newEnc = ParserHelper.replaceParam(h.getValue(), "charset",
                    enc, ";", true);
            setPrettyHeader(Header.CONTENT_TYPE, newEnc);
            _content_enc = enc;
        }
    }

    public int getContentLengthHeader() {
        if (headerMap.get(Header.CONTENT_LENGTH) == null) {
            // fix so that SipParser notices when no contentLength is set
            return -1;
        }

        return getContentLength();
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#getContentLength()
     */
    public int getContentLength() {
        Header header = headerMap.get(Header.CONTENT_LENGTH);

        if (header == null) {
            return 0;
        }

        String len = header.getValue();

        if (len == null) {
            return 0;
        }

        return Integer.parseInt(len);
    }

    public int getMessageSize() {
        /*
        if ((_content_byte == null) && (_content_obj == null)) {
            return 0;
        } else if (_content_byte != null) {
            return _content_byte.length;
        } else if (_content_obj != null) {
            return _content_obj.toString().length();
        }

        return 0;
        */

        return getContentLength();

        // TODO - Add the header size + requri
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#getContentType()
     */
    public String getContentType() {
        if ((_content_obj == null) && (_content_byte == null)) {
            return null;
        }
        if ((_content_byte != null) && (_content_byte.length == 0)) {
            return null;
        }

        Header h = headerMap.get(Header.CONTENT_TYPE);

        if (h != null) {
            return h.getValue();
        }

        return null;
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#getRawContent()
     */
    public byte[] getRawContent() throws IOException {
        byte[] bytes = null;

        if ((_content_byte != null) || (_content_obj != null)) {
            Header sh = headerMap.get(Header.CONTENT_TYPE);

            if (sh != null) {
                String newEnc = ParserHelper.getValue(sh.getValue(), "charset",
                        ";");

                if (newEnc != null) {
                    _content_enc = newEnc;
                }

                if (_content_obj != null) {
                    if (_content_obj instanceof Multipart) {
                        try {
                            ByteArrayOutputStream stream = new ByteArrayOutputStream();
                            ((Multipart) _content_obj).writeTo(stream);
                            bytes = stream.toByteArray();
                            stream.close();
                        } catch (MessagingException e) {
                            throw new IOException(e.getMessage());
                        }
                    } else {
                        String msg = _content_obj.toString();
                        bytes = msg.getBytes(_content_enc);
                    }
                } else if (_content_byte != null) {
                    return _content_byte;
                }
            }
        }

        return bytes;
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#getContent()
     */
    public Object getContent() throws IOException, UnsupportedEncodingException {
        Object body = null;
        Header h = headerMap.get(Header.CONTENT_TYPE);

        if (h != null) {
            String mediaType = ParserHelper.getFirstToken(h.getValue(), "/");
            String charset = ParserHelper.getValue(h.getValue(), "charset", ";");

            if (_content_byte != null) {
                if (charset == null) {
                    charset = _content_enc;

                    if (charset == null) {
                        charset = _default_enc;
                    }
                }

                if (!Charset.isSupported(charset)) {
                    throw new UnsupportedEncodingException("CharSet: " + charset);
                }
                // Return a string if it is text
                if ("text".equalsIgnoreCase(mediaType)) {
                    body = getContentAsString(charset);
                }
                // Return a MimeMultipart if we have one
                else if ((_content_obj != null) &&
                        "multipart".equalsIgnoreCase(mediaType) &&
                        (_content_obj instanceof Multipart)) {
                    body = _content_obj;
                }
                // Unknown content, return a byte array
                else {
                    body = getRawContent();
                }
            } else if (_content_obj != null) {
                if ("multipart".equalsIgnoreCase(mediaType) &&
                        (_content_obj instanceof Multipart)) {
                    body = _content_obj;
                }
            }
        }

        return body;
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#setContent(java.lang.Object,
     *      java.lang.String)
     */
    public void setContent(Object content, String contentType)
        throws UnsupportedEncodingException {
        validateMessageCommitted();

        if ((content == null) || (contentType == null)) {
            throw new NullPointerException("null content");
        }

       
       
        int contentLength = -1;
        _content_byte = null;
        _content_obj = null;

        setContentType(contentType);

        String mediatype = ParserHelper.getFirstToken(contentType, "/");

        if (mediatype == null) {
            throw new UnsupportedEncodingException(
                "media type not known or null");
        }

        if (mediatype.equalsIgnoreCase("text")) {
            if (content instanceof byte[]) {
                _content_byte = (byte[]) content;
                contentLength = _content_byte.length;
            } else if (content instanceof String) {
                String encoding = getCharacterEncoding();

                if (encoding == null) {
                    encoding = _default_enc;
                }
                if (!Charset.isSupported(encoding)) {
                    throw new UnsupportedEncodingException("CharSet: " + encoding);
                }

                _content_byte = ((String) content).getBytes(encoding);
                contentLength = _content_byte.length;
            } else {
                _content_obj = content.toString();
                contentLength = _content_obj.toString().length();
            }
        } else if (mediatype.equalsIgnoreCase("multipart")) {
            if (content instanceof Multipart) {
                _content_obj = content;

                ByteArrayOutputStream stream = new ByteArrayOutputStream();

                try {
                    ((Multipart) _content_obj).writeTo(stream);
                    contentLength = stream.toByteArray().length;
                } catch (IOException e) {
                    throw new IllegalStateException(e.toString());
                } catch (MessagingException e) {
                    throw new IllegalStateException(e.toString());
                } finally {
                    try {
                        stream.close();
                    } catch (IOException e) {
                        // ignore
                    }
                }
            } else {
                try {
                    // getCharacterEncoding can return null.
                    String encoding = getCharacterEncoding();

                    if (encoding == null) {
                        encoding = _default_enc;
                    }

                    if (!Charset.isSupported(encoding)) {
                        throw new UnsupportedEncodingException("CharSet: " + encoding);
                    }
                    contentLength = parseMultipart(content, contentType,
                            encoding);
                } catch (MessagingException e) {
                    throw new IllegalStateException(e.getMessage());
                }
            }
        } else if (content instanceof byte[]) {
            _content_byte = (byte[]) content;
            contentLength = _content_byte.length;
        } else if (content instanceof String) {
            String encoding = getCharacterEncoding();

            if (encoding == null) {
                encoding = _default_enc;
            }

            _content_byte = ((String) content).getBytes(encoding);
            contentLength = _content_byte.length;
        } else {
            throw new IllegalArgumentException(
                "Content object is not supported for this MIME type");
        }       

        if (contentLength > -1) {
            setPrettyHeader(Header.CONTENT_LENGTH, String.valueOf(contentLength));
        }
    }

    /* Internal container function called by the Parser */
    public void setContent(byte[] content) {
        _content_byte = content;

        try {
            String ctype = getContentType();

            if (ctype != null) {
                setContent(content, ctype);
            }
        } catch (UnsupportedEncodingException e) {
        }
    }

    public int getContentMaxLength() {
        // TODO read value from jmx config
        // TODO define constant if no jmx config
        return 500000;
    }

    public void addContent(byte[] srcBytes, int pos, int length) {
        if (_content_byte == null) {
            // allocate array to hold the hole content (possibly several deltas)
            int content_len = getContentLengthHeader();

            if (content_len > getContentMaxLength()) {
                throw new RuntimeException(
                    "Content-length exceeds defined max value:" +
                    getContentLengthHeader() + "(" + getContentMaxLength() +
                    ")");
            }

            _content_byte = new byte[getContentLengthHeader()];
        }

        System.arraycopy(srcBytes, pos, _content_byte, _content_byte_offset,
            length);
        _content_byte_offset += length;
    }

    /**
     * Internal container function which returns the currrent parsing offset of
     * content data (used for internal debugging purpose)
     */
    public int getContentOffset() {
        return _content_byte_offset;
    }

    /*
     * Internal container function to set "Content-Length" header after
     */
    public void internalSetContentLength(int len) {
        setPrettyHeader(Header.CONTENT_LENGTH, new Integer(len).toString());
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#setContentLength(int)
     */
    public void setContentLength(int len) {
        if (isDirection() == Type.Callee || hasSentOnThread())
            throw new IllegalStateException("Incoming Message or Message has been sent!");
        setPrettyHeader(Header.CONTENT_LENGTH, new Integer(len).toString());
        // new IllegalStateException("Instead use the setContent method please!");
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#setContentType(java.lang.String)
     */
    public void setContentType(String type) {
        setPrettyHeader(Header.CONTENT_TYPE, type); // TODO enc
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#getAttribute(java.lang.String)
     */
    public Object getAttribute(String name) {
        if (attrib != null) {
            Object returnValue = attrib.get(name);
            if (returnValue != null) return returnValue;
        }

        if (systemAttrib != null) {
            return systemAttrib.get(name);
        }

        return null;
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#getAttributeNames()
     */
    public Enumeration<String> getAttributeNames() {
        Collection<String> names = null;
        if (attrib != null) {
            names = new ArrayList<String>(attrib.keySet());
        }

        if (systemAttrib != null) {
            if (names == null) {
                names = new ArrayList<String>(systemAttrib.keySet());
            } else {
                names.addAll(systemAttrib.keySet());
            }
        }

        if (names == null) {
            return empty;
        } else {
            return new EnumerationConverter<String>(names);
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#setAttribute(java.lang.String,
     *      java.lang.Object)
     */
    public void setAttribute(String name, Object o) {
        if (name == null) {
            throw new NullPointerException( " Argument name is null ");
        }
        if (o == null) {
            throw new NullPointerException( " Argument object is null ");
        }
        validateMessageCommitted();

        if (attrib == null) {
            attrib = new HashMap<String, Object>(1, 1);
        }

        attrib.put(name, o);
    }

    public void removeAttribute(String name) {
        if (name == null) {
            throw new NullPointerException(" name is null ");
        }

        attrib.remove(name);
    }

    public void setSystemAttribute(String name, Object o) {
        if (name == null) {
            throw new NullPointerException( " Argument name is null ");
        }
        if (o == null) {
            throw new NullPointerException( " Argument object is null ");
        }
        if (systemAttrib == null) {
            systemAttrib = new HashMap<String, Object>(1,1);
        }

        systemAttrib.put(name, o);
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#getSession()
     */
    public SipSession getSession() {
        return getSession(true);
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#getSession(boolean)
     */
    public SipSession getSession(boolean create) {
        return getSessionImpl();
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#getApplicationSession()
     */
    public SipApplicationSession getApplicationSession() {
        return getApplicationSession(true);
    }

    public SipSessionBase getSessionImpl() {
        return _session;
    }

    public void setSession(SipSessionBase sessionImpl) {
        _session = sessionImpl;
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#getApplicationSession(boolean)
     */
    public SipApplicationSession getApplicationSession(boolean create) {
        return getApplicationSessionImpl();
    }

    public SipApplicationSessionImpl getApplicationSessionImpl() {
        return getSessionImpl().getApplicationSessionImpl();
    }

    private Locale _getAcceptLanguage(String language) {
            StringTokenizer st = new StringTokenizer(language, "-");
            String localeLang = null;
            String localeCountry = null;
            String localeVariant = null;
            if (st.hasMoreTokens())
                localeLang = st.nextToken();
            if (st.hasMoreTokens())
                localeCountry = st.nextToken();
            if (st.hasMoreTokens())
                localeVariant = st.nextToken();
            if (localeLang != null) {
                if (localeCountry != null) {
                    if (localeVariant != null) {
                        return new Locale(localeLang, localeCountry, localeVariant);
                    } else {
                        return new Locale(localeLang, localeCountry);
                    }
                } else {
                    return new Locale(localeLang);
                }
            }
            return null;
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#getAcceptLanguage()
     */
    public Locale getAcceptLanguage() {
        Header al = headerMap.get(Header.ACCEPT_LANGUAGE);
        if (al != null) {
            Iterator<String> i = al.getValues();
            String language = null;
            float q = 0.0f;
            while (i.hasNext()) {
                String acceptLanguage = i.next();
                ParameterableImpl p = ParameterableImpl.parse(acceptLanguage);
                String qParam = p.getParameter("q");
                if ( qParam == null) { // default is 1.0 and reurn the Locale
                    language = p.getValue();
                    break;
                }
                float q1 = 0.0f;
                try {
                    q1 = Float.parseFloat(qParam);
                } catch (NumberFormatException e) {
                    continue;
                }
                if (q1 > q) {
                    language = p.getValue();
                    q = q1;
                } else if (language == null) {
                    language = p.getValue();
                }
            }
            return _getAcceptLanguage(language);
        }

        return null;
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#getAcceptLanguages()
     */
    public Iterator<Locale> getAcceptLanguages() {
        ArrayList<Locale> locales = new ArrayList<Locale>(3);
        Header al = headerMap.get(Header.ACCEPT_LANGUAGE);

        if (al != null) {
            Iterator<String> i = al.getValues();

            while (i.hasNext()) {
                StringTokenizer st = new StringTokenizer(i.next(), ";");
                if (st.hasMoreTokens())
                    locales.add(_getAcceptLanguage(st.nextToken()));
            }
        }

        return locales.iterator();
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#setAcceptLanguage(java.util.Locale)
     */
    public void setAcceptLanguage(Locale locale) {
        validateMessageCommitted();

        if (locale == null) {
            headerMap.remove(Header.ACCEPT_LANGUAGE);
        } else {
            Header al = headerMap.get(Header.ACCEPT_LANGUAGE);

            if (al == null) {
                al = new MultiLineHeader(Header.ACCEPT_LANGUAGE, false);
                headerMap.put(Header.ACCEPT_LANGUAGE, al);
            } else {
                al.removeValues();
            }

            al.setValue(locale.getLanguage(), false);
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#addAcceptLanguage(java.util.Locale)
     */
    public void addAcceptLanguage(Locale locale) {
        validateMessageCommitted();

        Header al = headerMap.get(Header.ACCEPT_LANGUAGE);

        if (al == null) {
            al = new MultiLineHeader(Header.ACCEPT_LANGUAGE, false);
            headerMap.put(Header.ACCEPT_LANGUAGE, al);
        }

        al.setValue(locale.getLanguage(), false);

        // TODO - should be sorted with the other based on q value
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#setContentLanguage(java.util.Locale)
     */
    public void setContentLanguage(Locale locale) {
        setPrettyHeader(Header.CONTENT_LANGUAGE, locale.getLanguage());
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#getContentLanguage()
     */
    public Locale getContentLanguage() {
        String cl = getHeader(Header.CONTENT_LANGUAGE);

        if (cl != null) {
            return new Locale(cl);
        } else {
        } // TODO locale identified by the charset parameter of the
          // Content-Type
          // header

        return Locale.getDefault();
    }

    public abstract void send() throws IOException;

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#isSecure()
     */
    public boolean isSecure() {
        return "TLS".equals(getTransport());
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#isCommitted()
     */
    public boolean isCommitted() {
        return false;
    }

    private boolean _isCommitted() {
        return !_NoJSR289isCommitted && isCommitted();
    }

    protected void validateMessageCommitted() {
        if (_isCommitted()) {
            throw new IllegalStateException("Message is already committed");
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#getRemoteUser()
     */
    public String getRemoteUser() {
        return _user;
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#isUserInRole(java.lang.String)
     */
    public boolean isUserInRole(String role) {
        AuthModule authModule =  getApplicationSessionImpl().getServletDispatcher().getAuthModule();
        if (authModule == null) {
            return false;
        }

        String servletName = getApplicationSessionImpl().getCurrentServlet();

        return authModule.isUserInRole(servletName, role,getUserPrincipal());
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#isUserInRole(java.lang.String)
     */
    public void setRoles(List<String> roles) {
        _roles = roles;
    }

    public List<String> getRoles() {
        return _roles;
    }

    public void setRole(String role) {
        _roles = new ArrayList<String>();
        _roles.add(role);
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#isUserInRole(java.lang.String)
     */
    public String popRole() {
        if (_roles != null) {
            int size = _roles.size();

            if (size > 0) {
                return _roles.remove(size - 1);
            }
        }

        return null;
    }

    public String peekRole() {
        int size = _roles.size();

        if (size > 0) {
            return _roles.get(size - 1);
        }

        return null;
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#getUserPrincipal()
     */
    public Principal getUserPrincipal() {
        return _principal;
    }

    public void setCertificate(X509Certificate[] cert) {
        _clientCert = cert;
    }

    public X509Certificate[] getCertificate() {
        return _clientCert;
    }

    /**
     * _initialRemote is the original remote address from where
     * container received the request (or a response). This will
     * be set in the Network Manager. CLB would set this too, if
     * it decodes the address from the special header it received
     * from the frontend.
     */
    public void setInitialRemote(TargetTuple initialRemote) {
        _initialRemote = initialRemote;
        _remote = initialRemote;
        _remoteHop = initialRemote;
    }

    public TargetTuple getInitialRemote() {
        return _initialRemote;
    }

    /**
     * _remote is the target-tuple that is used by the container to
     * the other party it is communicating with. This will be set
     * by different places in the container.
     */
    public void setRemote(TargetTuple remote) {
        _remote = remote;
    }

    public TargetTuple getRemote() {
        return _remote;
    }
    /**
     * oi is the target-tuple that is used by the container to
     * determine the Network interface that will be used to send the message
     * out.
     */
    public void setOutboundInterface(OutboundInterface outboundinterface) {
        oi = outboundinterface;
    }

    public OutboundInterface getOutboundInterface() {
        return oi;
    }
   
   
    /**
     * Address of the last hop from where the container received
     * this incoming message. This could be localhost, if App
     * router has internally routed the request.
     */
    public void setRemoteHop(TargetTuple remoteHop) {
        _remoteHop = remoteHop;
    }

    public TargetTuple getRemoteHop() {
        return _remoteHop;
    }

    public void setLocal(InetSocketAddress local) {
        _local = local;
    }

    public InetSocketAddress getLocal() {
        return _local;
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#getLocalAddr()
     */
    public String getLocalAddr() {
        if (_local != null) {
            return _local.getAddress().getHostAddress();
        }

        return null;
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#getLocalPort()
     */
    public int getLocalPort() {
        if (_local != null) {
            return _local.getPort();
        }

        return -1;
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#getRemoteAddr()
     */
    public String getRemoteAddr() {
        if (_remoteHop != null) {
            return _remoteHop.getIP();
        }

        return null;
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#getInitialRemoteAddr()
     */
    public String getInitialRemoteAddr() {
        if (_initialRemote != null) {
            return _initialRemote.getIP();
        }

        return null;
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#getRemotePort()
     */
    public int getRemotePort() {
        if (_remoteHop != null) {
            return _remoteHop.getPort();
        }

        return -1;
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#getInitialRemotePort()
     */
    public int getInitialRemotePort() {
        if (_initialRemote != null) {
            return _initialRemote.getPort();
        }

        return -1;
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#getTransport()
     */
    public String getTransport() {
        if (_remote != null) {
            return _remote.getProtocol().name().toUpperCase();
        }

        return null;
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.sip.SipServletMessage#getInitialTransport()
     */
    public String getInitialTransport() {
        if (_initialRemote != null) {
            return _initialRemote.getProtocol().name().toUpperCase();
        }

        return null;
    }

    /****************************************************************************
     * Checks the To,From,Cseq,Call-Id
     *
     * @return
     */

    /**
     * Return true if no to tag has been set in the message otherwise false.
     *
     * @return true if no to tag has been set in the message otherwise false.
     */
    public boolean hasToTag() {
        return getToImpl().getParameter(AddressImpl.TAG_PARAM) != null;
    }

    public boolean isValidMessage() {
        // TODO implement
        return false;
    }

    public void pushTransactionDispatcher(Dispatcher disp) {
        _transactionStack.add(disp);
    }

    public void pushApplicationDispatcher(Dispatcher disp) {
        _applicationStack.add(disp);
    }

    public void clearTransactionDispatchers() {
        _transactionStack.clear();
    }

    public void clearApplicationDispatchers() {
        _applicationStack.clear();
    }

    public abstract Dispatcher popDispatcher();

    public abstract Dispatcher peekDispatcher();

    String toDebugString() {
        StringBuilder sb = new StringBuilder("Call-ID: ");
        sb.append(headerMap.get(Header.CALL_ID).getValue());
        sb.append(" To-tag: ");

        try {
            sb.append(headerMap.get(Header.TO).getAddressValue()
                               .getParameter(AddressImpl.TAG_PARAM));
        } catch (Exception e) {
            sb.append("undefined");
        }

        sb.append(" From-tag: ");

        try {
            sb.append(headerMap.get(Header.FROM).getAddressValue()
                               .getParameter(AddressImpl.TAG_PARAM));
        } catch (Exception e) {
            sb.append("undefined");
        }

        sb.append(" CSeq: ");
        sb.append(headerMap.get(Header.CSEQ).getValue());

        return sb.toString();
    }

    /**
     * The method called when a service called send method. This is checked in
     * the ApplicationDispatcher whether to continue executing other services or
     * stopping. Has to be set to false first to activate the function for the
     * current thread, this is in order not to loose memory in non Sip container
     * threads like from EJB,Timers and Http container.
     *
     * @param hasSent
     *        A boolean.
     */
    public void setSentOnThread(boolean hasSent) {
        long tid = Thread.currentThread().getId();

        if (hasSent) {
            if (sentMap.containsKey(tid)) {
                sentMap.put(tid, Boolean.TRUE);
            }
        } else { //Init function for thread
            sentMap.put(tid, Boolean.FALSE);
        }
    }

    /**
     * The method return a boolean indicating if a service has sent a response or
     * generate a request to break the execution of other services.
     * Return null if not initialized in thread by : setSentOnThread(false)
     *
     * @return
     */
    public boolean hasSentOnThread() {
        Boolean b = sentMap.get(Thread.currentThread().getId());

        if (b == null) {
            return false;
        } else {
            return b.booleanValue();
        }
    }

    /**
     * Helper function, if not activated setSentOnThread(true) will not be saved.
     * This is in order that threads sending SIP messages that does not need
     * to se this status will not consume memory.
     *
     */
    public void activateSentOnThread() {
        setSentOnThread(false);
    }

    /**
     * Clear should be called in a finally statemnt to always clean up.
     * If not done each thread in the system may consume memory in the map.
     * This should be avoided by proper clean up!
     */
    public void clearSentOnThread() {
        sentMap.remove(Thread.currentThread().getId());
    }

    /**
     * States that a Contact header should be added to this message
     */
    public void indicateContact() {
        _IsContactIndicated = true;
    }

    /**
     * Returns that a Contact header should be added to this message
     *
     * @return whether a Contact header should be added to this message or not
     */
    public boolean isContactIndicated() {
        return _IsContactIndicated;
    }

    /**
     * This method returns the content-length for a multipart message.
     *
     * @param content -
     *        the content
     * @param contentType -
     *        the content type
     * @param charSet -
     *        encoding
     * @return content-length.
     * @throws UnsupportedEncodingException
     * @throws MessagingException
     */
    private int parseMultipart(Object content, String contentType,
        String charSet) throws UnsupportedEncodingException, MessagingException {
        if (content instanceof byte[]) {
            ByteArrayDataSource bds = new ByteArrayDataSource((byte[])content,
                                                            contentType);
            _content_obj = new MimeMultipart(bds);
            return ((byte[])content).length;
        }
        if (content instanceof String) {
            String multipart = (String) content;
            _content_obj = new MimeMultipart(new StringDataSource(multipart,
                                             contentType, charSet));
            return multipart.length();
        }
        throw new IllegalArgumentException(
            "Content object needs to be a Multipart object, byte[], " +
            "or a String for MIME multipart type");

    }

    /**
     * Used with getContent().
     *
     * @param charSet -
     *        the encoding to be used
     * @return - the content as a String encoded with charSet or null if the
     *         content is null.
     */
    private String getContentAsString(String charSet) {
        // If an object, return its toString()
        if (_content_obj != null) {
            return _content_obj.toString();
        } else if (_content_byte != null) {
            try {
                // Encode it
                return new String(_content_byte, charSet);
            } catch (UnsupportedEncodingException e) {
                throw new IllegalArgumentException("Unsupported encoding " + e);
            }
        }

        return null;
    }

    public boolean isHeadersComplete() {
        return _headersComplete;
    }

    public void setHeadersComplete(boolean complete) {
        _headersComplete = complete;
    }

    public boolean isMessageComplete() {
        return _messageComplete;
    }

    public void setMessageComplete(boolean complete) {
        _messageComplete = complete;

        String ctype = getContentType();

        if (ctype != null) {
            try {
                setContent(_content_byte, ctype);
            } catch (UnsupportedEncodingException e) {
            }
        }
    }

    public DialogFragment getDialog() {
        return _dialog;
    }

    public void setDialog(DialogFragment dialog) {
        _dialog = dialog;
    }

    public String getFragmentId() {
        return _fragmentId;
    }

    public void addInternalHeader(String name, String value) {
        Header header = null;
        String pretty = Header.format(name);

        if (headerMap.containsKey(pretty)) {
            header = headerMap.get(pretty);
            header.removeValues();
            header.setValue(value, true);
        } else {
            header = Header.createFormatted(pretty, this);
            header.setValue(value, true);
            headerMap.put(pretty, header);
        }
    }

    /**
     * Returns true if the message has a body. This method was added for the
     * solution of HF73291, which says that a message with a body but missing a
     * Content-Type header should be rejected with a 400 response.
     *
     * @return
     */
    public boolean hasBody() {
        return (_content_byte != null) || (_content_obj != null);
    }

    /**
     * Extracts the Cseq number from the message.
     *
     * @param m
     *        the messge holding the Cseq value
     * @return the CSeq number
     */
    public int getCSeqNumber() throws NumberFormatException {
        String CSeq = getHeader("CSeq");

        if (CSeq == null) {
            throw new NumberFormatException(
                "Could not extract CSeq number from message.");
        }

        int posStart = 0;

        // trim
        while ((CSeq.charAt(posStart) == ' ') && (posStart < CSeq.length())) {
            ++posStart;
        }

        int posStop = posStart;

        // find end of integer, CSeq number
        while (Character.isDigit(CSeq.charAt(posStop)) &&
                (posStop < CSeq.length())) {
            ++posStop;
        }

        if (posStop >= CSeq.length()) {
            // the CSeq is corrupt
            throw new NumberFormatException(
                "Could not extract CSeq number from message.");
        }

        return Integer.parseInt(CSeq.substring(posStart, posStop));
    }

    public void setUser(String user) {
        _user = user;
    }

    public void setUserPrincipal(Principal value) {
        _principal = value;
    }

    /**
     * Translate the <code>SipServletMessageImpl</code> bytes into
     * a <code>ByteBuffer</code>.
     *
     * @param byteBuffer The byteBuffer to store the bytes.
     * @return byteBuffer The byteBuffer to store the bytes.
     */

    /*abstract public ByteBuffer toBuffer(ByteBuffer byteBuffer)
        throws UnsupportedEncodingException;*/
    abstract ByteBuffer toBufferFirstline(ByteBuffer byteBuffer)
        throws UnsupportedEncodingException;

    /**
     * returns true if there is data in this request which should be copied to
     * the writebuffer and sent via the link (e.g. tcp)
     *
     * @return
     */
    public boolean toBufferHasRemaining() {
        synchronized (byteBufferLock){
        return toBufferContentOffset != ((toBufferContent == null) ? 0
                                                                   : toBufferContent.length);
        }
    }

    /**
     * resets some variables used to hold state during transmission for the case
     * when msgsize > buffersize
     */
    public void toBufferInit() {
        synchronized (byteBufferLock) {
        toBufferContent = null;
        toBufferContentOffset = -1;
        toBufferHeaderOffset = 0;
        }
    }

    /**
     * puts the data in this message to the supplied ByteBuffer. The invocation
     * of this method has to be repeated until toBufferHasRemaining() return
     * false.
     *
     * @param bb
     * @return
     * @throws UnsupportedEncodingException
     *         Thrown when SipFactoryImpl.SIP_CHARSET encoding is not supported.
     */
    public ByteBuffer toBuffer(ByteBuffer bb)
        throws UnsupportedEncodingException {
        /*
         * This "bigmessage fix" is based on the assumption that the headers will
         * fit into the provided writeBuffer (typical size 8K)
         */
        synchronized (byteBufferLock) {
        if (toBufferContentOffset == -1) {
            toBufferContentOffset = 0;
            toBufferFirstline(bb);

            int len = 0;

            // byte[] content = null;
            try {
                toBufferContent = getRawContent();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            if (toBufferContent != null) {
                len = toBufferContent.length;
            }

            Header cl = headerMap.get(Header.CONTENT_LENGTH);

            if (cl == null) {
                cl = new SingleLineHeader(Header.CONTENT_LENGTH, false);
                cl.setValue(new StringBuilder().append(len).toString(), false);
                headerMap.put(Header.CONTENT_LENGTH, cl);
            } else {
                cl.setValue(new StringBuilder().append(len).toString(), false);
            }

            HashMap<String, Header> clonedHeaders = headerMap;

            if (headerForm == HeaderForm.COMPACT) {
                clonedHeaders = new HashMap<String, Header>(headerMap);

                for (String s : Header.LONG_TO_SHORT_MAP.keySet()) {
                    if (clonedHeaders.containsKey(s)) {
                        Header h = clonedHeaders.remove(s);
                        h.setName(Header.LONG_TO_SHORT_MAP.get(s));
                        clonedHeaders.put(Header.LONG_TO_SHORT_MAP.get(s), h);
                    }
                }
            }

            Object[] headers = clonedHeaders.values().toArray();

            while (toBufferHeaderOffset < headers.length) {
                byte[] h = headers[toBufferHeaderOffset].toString()
                                                        .getBytes(SipFactoryImpl.SIP_CHARSET);

                if (bb.remaining() < h.length) {
                    return bb; // There is no more space in this buffer
                }

                bb.put(h);
                toBufferHeaderOffset++;
            }

            bb.put(SipFactoryImpl.NEW_LINE.getBytes());
        }

        if ((toBufferContent != null) && (toBufferContent.length > 0)) {
            int remainingContent = (toBufferContent.length -
                toBufferContentOffset);
            int remainingBufferLength = bb.remaining();
            int bytesToCopy = (remainingContent < remainingBufferLength)
                ? remainingContent : remainingBufferLength;
            bb.put(toBufferContent, toBufferContentOffset, bytesToCopy);
            toBufferContentOffset += bytesToCopy;
        }
        }
        return bb;
    }
   
    /**
     * Sets the hash key used by the Converged Load Balance to route on.
     * @param beKey the back-end hash key
     */
    public void setBeKey(String beKey) {
        this._beKey = beKey;
    }

    /**
     * Gets the hash key used by the Converged Load Balance to route on.
     * @return the hash key used by the Converged Load Balance to route on
     */
    public String getBeKey() {
        return _beKey;
    }

    //Optimazation in order not have to do instanceof
    public enum SipMessageType {SipRequest,
        SipResponse;
    }
   
    /**
     * Sets the remote that is already resolved.
     */
    public void setResolvedRemote(TargetTuple remote) {
        _remote = remote;
        remoteResolved = true;
    }
   
    /**
     * Checks if the remote is already resolved
     * @return true if the remote has been already been resolved
     */
    public boolean isRemoteResolved() {
        return remoteResolved;
    }
    /**
     * Gets the retry policy that shall be considered when message has failed on the IO level
     * @return the retry policy for this message.
     */
    public RetryPolicy getRetryPolicy() {
        return retryPolicy;
    }

    /**
     * Sets the message sending error policy.
     * @param retryPolicy the policy to apply when the message has failed on the IO level
     */
    public void setRetryPolicy(RetryPolicy retryPolicy) {
        this.retryPolicy = retryPolicy;
    }
   
    /**
     * Returns the bekey for request. If bekey is already set on
     * request/response, then that value is returned. Else it is retrieved from
     * SipApplicationSession associated with request/response.
     *
     * @return bekey associated with request/response
     */
    public String getBeKeyFromSession() {
        if(_beKey != null){
            return _beKey;
        }
        if (_session != null) {
            SipApplicationSessionImpl sas = _session.getApplicationSessionImpl();
            if (sas != null) {
                String key = sas.getBeKey();
                if(key == null){
                    _logger.log(Level.WARNING,
                            "sip.stack.message.cannot_retrieve_bekey_from_sas_id",
                            new Object[]{sas.getId()});                   
                }
                return key;
            } else {
                _logger.log(Level.WARNING,
                        "sip.stack.message.cannot_retrieve_bekey_sas_is_null");
            }
        } else {
            _logger.log(Level.WARNING,
                    "sip.stack.message.cannot_retrieve_bekey_session_is_null");
           
        }
        return null;
    }
}
TOP

Related Classes of com.ericsson.ssa.sip.SipServletMessageImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.