Package com.ericsson.ssa.container

Source Code of com.ericsson.ssa.container.LinkBase$WriteRequestTask

/*
* 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.container.processor.QueuedProcessor;
import com.ericsson.ssa.container.processor.QueuedProcessor.ProcessorException;
import com.ericsson.ssa.container.processor.QueuedTask;
import com.ericsson.ssa.container.reporter.Reporter;
import com.ericsson.ssa.container.startup.SipMonitoring;
import com.ericsson.ssa.sip.Dispatcher;
import com.ericsson.ssa.sip.Header;
import com.ericsson.ssa.sip.Layer;
import com.ericsson.ssa.sip.LayerHelper;
import com.ericsson.ssa.sip.SipParser;
import com.ericsson.ssa.sip.SipServletMessageImpl;
import com.ericsson.ssa.sip.SipServletRequestImpl;
import com.ericsson.ssa.sip.SipServletResponseImpl;
import com.ericsson.ssa.sip.dns.SipTransports;
import com.ericsson.ssa.sip.dns.TargetResolver;
import com.ericsson.ssa.sip.dns.TargetTuple;
import com.ericsson.ssa.sip.timer.GeneralTimer;
import com.ericsson.ssa.sip.timer.TimerServiceImpl;

import java.io.IOException;

import java.net.InetSocketAddress;

import java.nio.ByteBuffer;

import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jvnet.glassfish.comms.util.LogUtil;


/**
* @author ekrigro
* @reveiwed ehsroha 2006-nov-14
*/
public abstract class LinkBase implements Link {
    protected static final int BUFFER_SIZE = 4096;
    private Logger _log = LogUtil.SIP_LOGGER.getLogger();
    protected OLDNetworkManager _networkManager;
    protected SipContainerThreadPool _threadPool = SipContainerThreadPool.getInstance();
    protected Layer _nextLayer;
    protected SipParser _parser = SipParser.getInstance();
    protected ByteBuffer _readBuffer = null;
    protected ByteBuffer _writeBuffer = null;
    protected ReentrantLock _readMutex = new ReentrantLock();
    protected SipServletMessageImpl _message = null;
    private QueuedProcessor _writeProcessor;
    private GeneralTimer _linkAliveTimer;
    private boolean _enableLinkAliveTimer;
    private AtomicBoolean _linkActive = new AtomicBoolean(false);

    public LinkBase(OLDNetworkManager networkManager, Layer next,
        boolean enableLinkAliveTimer) {
        _networkManager = networkManager;
        _nextLayer = next;
        // TODO: We have found that there might be problems if read buffers are
        // placed in the thread pool queue.
        // As a consequence the linkAliveTimer is disabled.
        _enableLinkAliveTimer = enableLinkAliveTimer;
        // The second argument (0) will cause the processor thread to die
        // immediately when the queue is empty.
        _writeProcessor = new QueuedProcessor(_networkManager.getSipLinkMaxQueueLength(),
                0);

        if (_enableLinkAliveTimer) {
            _linkAliveTimer = TimerServiceImpl.getInstance()
                                              .createTimer(this,
                    _networkManager.getSipLinkAliveTimeout() * 1000,
                    _networkManager.getSipLinkAliveTimeout() * 1000,
                    false, "Link alive timer");
        }
    }

    public LinkBase(OLDNetworkManager networkManager, Layer next) {
        this(networkManager, next, false);
    }

    public abstract void write(InetSocketAddress remote, ByteBuffer buffer)
        throws IOException;

    public void dispatch(SipServletRequestImpl req) {
        Dispatcher nextDispatcher = req.popDispatcher();

        if (nextDispatcher != null) {
            nextDispatcher.dispatch(req);

            return;
        }

        putTask(new WriteRequestTask(req));
    }

    /**
     * Sends the error. Asynchronously, i.e. in a separate thread so that the
     * caller thread is not blocked, if required.
     *
     * @param req
     * @param async
     *           if true the error is sent asynchronously, i.e. in a separate
     *           thread so that the caller thread is not blocked.
     */
    private void sendError(final SipServletRequestImpl req, boolean async) {
        // RFC 3261, 8.1.3.1 If a fatal transport error is reported by the
        // transport layer (generally, due to fatal ICMP errors in UDP or
        // connection failures in TCP), the condition MUST be treated as a
        // 503 (Service Unavailable) status code.
        if (_log.isLoggable(Level.FINE)) {
            _log.log(Level.FINE, "Send 503 due to failure when writing: " +
                req);
        }

        if (req.getMethod().equals("ACK")) {
            return;
        }

        if (async) {
            _threadPool.execute(new Callable() {
                    public Object call() throws Exception {
                        SipServletResponseImpl resp = req.createTerminatingResponse(503);
                        //_nextLayer.next(resp);
                        LayerHelper.next(resp, _nextLayer, _nextLayer);
                        return null;
                    }
                });
        } else {
            SipServletResponseImpl resp = req.createTerminatingResponse(503);
            //            _nextLayer.next(resp);
            LayerHelper.next(resp, _nextLayer, _nextLayer);
        }
    }

    public void dispatch(SipServletResponseImpl resp) {
        Dispatcher nextDispatcher = resp.popDispatcher();

        if (nextDispatcher != null) {
            nextDispatcher.dispatch(resp);

            return;
        }

        putTask(new WriteResponseTask(resp));
    }

    public final void close() throws IOException {
        shutdownWriteProcessor();
        closeImpl();
    }

    /**
     * Shuts down the {@link #_writeProcessor} and cancels the link alive timer.
     */
    protected void shutdownWriteProcessor() {
        cancelLinkAliveTimer();
        _writeProcessor.shutdown();
    }

    /**
     * Implementation of close() to be provided by subclasses.
     *
     * @throws IOException
     */
    protected abstract void closeImpl() throws IOException;

    /**
     * Called when the {@link #_linkAliveTimer} fires.
     */
    public void timeout(GeneralTimer timer) {
        if (!_linkActive.getAndSet(false)) {
            _writeProcessor.shutdown();

            try {
                if (_log.isLoggable(Level.FINE)) {
                    _log.log(Level.FINE,
                        LinkBase.this + " Closed link " + getInfo() +
                        " due to no activity for " +
                        _networkManager.getSipLinkAliveTimeout() + " seconds");
                }

                close();
            } catch (IOException e) {
                if (_log.isLoggable(Level.FINE)) {
                    _log.log(Level.FINE, "Failure when closing link: " + e);
                }
            }
        }
    }

    /**
     * Cancels the (possible) current {@link #_linkAliveTimer} and starts a new.
     */
    public void markAsActive() {
        _linkActive.set(true);
    }

    /**
     * Cancel the {@link #_linkAliveTimer}, if existing.
     */
    private void cancelLinkAliveTimer() {
        if (_linkAliveTimer != null) {
            _linkAliveTimer.cancel();
        }
    }

    /**
     * Puts a task for processing in the write processor.
     *
     * @param task
     * @throws ProcessorException
     */
    private void putTaskImpl(QueuedTask task) throws ProcessorException {
        markAsActive();
        _writeProcessor.put(task);

        if (_log.isLoggable(Level.FINE)) {
            _log.log(Level.FINE, "writeProcessor: " + _writeProcessor);
        }
    }

    /**
     * Puts a task for processing in the write processor and handles exceptions.
     *
     * @param task
     */
    protected void putTask(QueuedTask task) {
        try {
            putTaskImpl(task);
        } catch (ProcessorException e) {
            _log.log(Level.FINE,
                "Failed to put task for writing due to exceeded queue size.");
        }
    }

    /**
     * Puts a task for processing in the write processor.
     *
     * @param task
     */
    private void putTask(WriteRequestTask task) {
        try {
            putTaskImpl(task);
        } catch (ProcessorException e) {
            SipServletRequestImpl request = ((WriteRequestTask) task).getRequest();
            _log.log(Level.FINE,
                "Network OUT request " + request.getMethod() +
                " is dropped due to exceeded queue size --> \r\n" +
                request.toString());
            sendError(request, false);
        }
    }

    protected void handleMessage(ByteBuffer buffer,
        final InetSocketAddress local, final SipTransports transportProtocol,
        final InetSocketAddress socketAddress) {
        SipServletMessageImpl parsedMessage = null;

        try {
            final TargetTuple remote = new TargetTuple(transportProtocol,
                    socketAddress);

            // TODO - change so that the parser can do next(), to avoid
            // instanceof
            int initialSize = 0;
            int remaining = buffer.remaining();

            while ((remaining > 0) && (initialSize != remaining)) {
                initialSize = remaining;

                SipParserErrorHandlerImpl sipParserErrorHandler = new SipParserErrorHandlerImpl(this);
                sipParserErrorHandler.setErrorResponseEnabled(_networkManager.isErrorResponseEnabled()); // From
                                                                                                         // preferences

                parsedMessage = _parser.parseMessage(_message, buffer, local,
                        remote, sipParserErrorHandler);
                remaining = buffer.remaining();

                if ((parsedMessage != null) &&
                        parsedMessage.isMessageComplete() && (remaining > 0)) {
                    final SipServletMessageImpl msg = parsedMessage;
                    parsedMessage = null;
                    _threadPool.execute(new Callable() {
                            public Object call() throws Exception {
                                if (_log.isLoggable(Level.FINE)) {
                                    _log.log(Level.FINE,
                                        "Executing Inner Task -> lock = " +
                                        _readMutex.isHeldByCurrentThread());
                                }

                                processMessage(msg);

                                if (_log.isLoggable(Level.FINE)) {
                                    _log.log(Level.FINE,
                                        "Executing Inner Task -> lock = " +
                                        _readMutex.isHeldByCurrentThread());
                                }
                                return null;
                            }
                        });
                }

                if ((remaining > 0) && (initialSize != remaining)) {
                    _message = parsedMessage;
                    buffer.mark(); // TODO - check if reset is called?
                    buffer.compact();
                    buffer.flip();

                    if (_log.isLoggable(Level.FINE)) {
                        _log.log(Level.FINE,
                            "Position = " + buffer.position() + " ,Remains = " +
                            buffer.remaining());
                    }
                }
            }

            if (parsedMessage == null) {
                if (_log.isLoggable(Level.FINE)) {
                    _log.log(Level.FINE,
                        "The parsed request is null - not continuing!(" +
                        buffer.position() + ')');
                }

                int p = buffer.limit();
                buffer.clear();
                buffer.position(p);
                _message = null;

                if (SipMonitoring.isEnabled(SipMonitoring.NETWORK_MANAGER)) {
                    _networkManager.incrEasInvalidSipMessages();
                }

                return;
            }

            if (!parsedMessage.isMessageComplete()) {
                if (_log.isLoggable(Level.FINE)) {
                    _log.log(Level.FINE,
                        "The parsed request is not complete - not continuing! (" +
                        buffer.position() + ')');
                }

                if (buffer.hasRemaining()) {
                    buffer.compact();
                } else {
                    buffer.clear();
                }

                _message = parsedMessage;

                return;
            }

            buffer.clear();
            _message = null;
        } catch (Throwable t) {
            if (t instanceof Exception) {
                if (_log.isLoggable(Level.INFO)) {
                    _log.log(Level.INFO,
                        "Exception processing request " + getInfo() + ": " +
                        t.getMessage(), t);
                }
            } else {
                // FIXME alarm for e.g. OutOfMemoryError
                _log.log(Level.SEVERE, "Caught Throwable: ", t);
            }

            // TODO remove isLocked
            if (_readMutex.isLocked() && _readMutex.isHeldByCurrentThread()) {
                _message = null;
                buffer.clear();
            }

            if (SipMonitoring.isEnabled(SipMonitoring.NETWORK_MANAGER)) {
                _networkManager.incrEasInvalidSipMessages();
            }

            return;
        } finally {
            _readMutex.unlock();
        }

        processMessage(parsedMessage);
    }

    protected void processMessage(final SipServletMessageImpl message) {
        if (message instanceof SipServletRequestImpl) {
            SipServletRequestImpl req = (SipServletRequestImpl) message;

            if (_log.isLoggable(Level.FINE)) {
                _log.log(Level.FINE,
                    "Network IN request " + req.getMethod() + " --> \r\n" +
                    req.toString());
            }

            if (hasBodyWithoutContentType(message)) {
                return;
            }

            // Respones should use the same link
            req.pushTransactionDispatcher(this);
            req.pushApplicationDispatcher(_networkManager);

            // trigger reporter for network manager
            Reporter reporter = _networkManager.getReporter();
            if (reporter != null) {
                reporter.logIncomingRequest(Reporter.InterceptionType.LAYER, req, NetworkManager.class.getSimpleName());
            }

            _nextLayer.next(req);

            if (reporter != null) {
                reporter.logPostIncomingRequest(Reporter.InterceptionType.LAYER, req, NetworkManager.class.getSimpleName(), null);
            }

            if (SipMonitoring.isEnabled(SipMonitoring.NETWORK_MANAGER)) {
                _networkManager.incrEasReceivedSipRequests();
            }
        } else {
            SipServletResponseImpl resp = (SipServletResponseImpl) message;
            Header cseq = resp.getRawHeader(Header.CSEQ);

            if (cseq == null) {
                if (SipMonitoring.isEnabled(SipMonitoring.NETWORK_MANAGER)) {
                    _networkManager.incrEasInvalidSipMessages();
                }

                return; // Drop since missing required header
            }

            String c = cseq.getValue();
            int index = c.indexOf(' ');
            resp.setMethod(c.substring(index + 1));

            if (_log.isLoggable(Level.FINE)) {
                _log.log(Level.FINE,
                    "Network IN response " + resp.getStatus() + " " +
                    resp.getMethod() + " --> \r\n" + resp.toString());
            }

            // trigger reporter for network manager
            Reporter reporter = _networkManager.getReporter();
            if (reporter != null) {
                reporter.logIncomingResponse(Reporter.InterceptionType.LAYER, resp, NetworkManager.class.getSimpleName());
            }
            _nextLayer.next(resp);

            if (reporter != null) {
                reporter.logPostIncomingResponse(Reporter.InterceptionType.LAYER, resp, NetworkManager.class.getSimpleName(), null);
            }

            if (SipMonitoring.isEnabled(SipMonitoring.NETWORK_MANAGER)) {
                _networkManager.incrEasReceivedSipResponses();
            }
        }
    }

    public boolean hasBodyWithoutContentType(SipServletMessageImpl message) {
        // The SIP-stack should refuse all request with body and without
        // Content-Type header field.
        if (message.hasBody() && (message.getContentType() == null)) {
            try {
                if (_log.isLoggable(Level.FINE)) {
                    _log.log(Level.FINE,
                        "Missing Content-Type header field, the request has a body.");
                }

                SipServletRequestImpl req = (SipServletRequestImpl) message;
                String phraze = "Missing Content-Type header field";

                SipServletResponseImpl resp = req.createTerminatingResponse(400,
                        phraze);

                // TR HH52078
                if (resp != null) {
                    while (resp.popDispatcher() != null) {
                    } // After this layer it shouldn't go anywhere

                    TargetResolver tr = TargetResolver.getInstance();
                    TargetTuple tt = tr.resolveResponse(resp);

                    if (tt != null) {
                        resp.setRemote(tt);
                        dispatch(resp);
                    }
                } else if (_log.isLoggable(Level.FINE)) {
                    _log.log(Level.FINE,
                        "failed to find out where to send error response " +
                        getInfo() + ".");
                }
            } catch (Exception ignore) {
                if (_log.isLoggable(Level.FINE)) {
                    _log.log(Level.FINE,
                        "Unexpected exception " + getInfo() + ": " + ignore);
                }
            }

            return true;
        }

        return false;
    }

    /**
     * Common base class for write tasks sent to {@link #_writeProcessor}.
     * Supervises the stale timeout, which is calculated timeout as
     * min(SipLinkTimeout*SipLinkTimeoutRetries, 32 s)
     */
    private abstract class WriteTask implements QueuedTask {
        long timeout;

        /**
         * Constructor.
         */
        public WriteTask() {
            this.timeout = System.currentTimeMillis() +
                Math.min(_networkManager.getSipLinkTimeout() * _networkManager.getSipLinkTimeoutRetries() * _networkManager.getSipLinkMaxQueueLength(),
                    32000);
        }

        /**
         * Returns "write task is stale error" error if timeout is exceeded.
         */
        public String getError() {
            if (System.currentTimeMillis() > timeout) {
                return "write task is stale";
            } else {
                return null;
            }
        }
    }

    /**
     * Task for writing a response to the link.
     */
    private class WriteResponseTask extends WriteTask {
        SipServletResponseImpl response;

        /**
         * Constructor.
         *
         * @param response
         *           the response to write
         */
        public WriteResponseTask(SipServletResponseImpl response) {
            this.response = response;
        }

        /**
         * Do the writing ({@link #writeResponse(SipServletResponseImpl)}).
         */
        public void run() {
            if (_log.isLoggable(Level.FINE)) {
                _log.log(Level.FINE,
                    "Network OUT response " + response.getMethod() +
                    " is going to be written to link: " + getInfo() +
                    " --> \r\n" + response.toString());
            }

            writeResponse(response);
        }

        /**
         * Do error action (actaually nothing, just silent drop of response).
         */
        public void doErrorAction(String cause) {
            // Silently drop for responses
            if (_log.isLoggable(Level.FINE)) {
                _log.log(Level.FINE,
                    "Network OUT response " + response.getMethod() +
                    " is dropped due to " + cause + " --> \r\n" +
                    response.toString());
            }
        }

        /**
         * Implements the actual writing of a response.
         *
         * @param req
         *           the request to write
         */
        private void writeResponse(SipServletResponseImpl resp) {
            try {
                resp.toBufferInit();

                while (resp.toBufferHasRemaining()) {
                    _writeBuffer.clear();
                    resp.toBuffer(_writeBuffer);

                    if (_log.isLoggable(Level.FINE)) {
                        _log.log(Level.FINE,
                            "Sending:" + _writeBuffer.position() + " bytes");
                    }

                    _writeBuffer.flip();
                    write(resp.getRemote().getSocketAddress(), _writeBuffer);
                }
            } catch (IOException ioe) {
                if (_log.isLoggable(Level.INFO)) {
                    _log.log(Level.INFO,
                        "Got IOException in write " + getInfo() + ": ", ioe);
                }
            } catch (Throwable t) {
                if (_log.isLoggable(Level.INFO)) {
                    _log.log(Level.INFO,
                        "Got unexpected exception in dispatch " + getInfo() +
                        ": ", t);
                }
            } finally {
                if (!isOpen()) {
                    // Link has been closed, shutdown writeProcessor
                    shutdownWriteProcessor();
                }
            }

            if (SipMonitoring.isEnabled(SipMonitoring.NETWORK_MANAGER)) {
                _networkManager.incrEasSentSipResponses();
            }
        }
    }

    /**
     * Task for writing a request to the link.
     */
    private class WriteRequestTask extends WriteTask {
        SipServletRequestImpl request;

        /**
         * Constructor.
         *
         * @param request
         *           the request to write
         */
        public WriteRequestTask(SipServletRequestImpl request) {
            this.request = request;
        }

        /**
         * Gets the request to write.
         *
         * @return the request
         */
        public SipServletRequestImpl getRequest() {
            return request;
        }

        /**
         * Do error action: send and error ({@link LinkBase#sendError(SipServletRequestImpl, boolean)}).
         */
        public void doErrorAction(String cause) {
            if (_log.isLoggable(Level.FINE)) {
                _log.log(Level.FINE,
                    "Network OUT request " + request.getMethod() +
                    " is dropped due to " + cause + " --> \r\n" +
                    request.toString());
            }

            sendError(request, true);
        }

        /**
         * Do the writing.
         */
        public void run() {
            if (_log.isLoggable(Level.FINE)) {
                _log.log(Level.FINE,
                    "Network OUT request " + request.getMethod() +
                    " is going to be written to link: " + getInfo() +
                    "--> \r\n" + request.toString());
            }

            writeRequest(request);
        }

        /**
         * Implements the actual writing of a request.
         *
         * @param req
         *           the request to write
         */
        private void writeRequest(SipServletRequestImpl req) {
            try {
                req.toBufferInit();

                while (req.toBufferHasRemaining()) {
                    _writeBuffer.clear();
                    req.toBuffer(_writeBuffer);

                    if (_log.isLoggable(Level.FINE)) {
                        _log.log(Level.FINE,
                            "Sending:" + _writeBuffer.position() + " bytes");
                    }

                    _writeBuffer.flip();
                    write(req.getRemote().getSocketAddress(), _writeBuffer);
                }
            } catch (IOException ioe) {
                if (_log.isLoggable(Level.INFO)) {
                    _log.log(Level.INFO,
                        "Got IOException in write " + getInfo() + ": ", ioe);
                }

                sendError(req, true);

                return;
            } catch (Throwable t) {
                if (_log.isLoggable(Level.INFO)) {
                    _log.log(Level.INFO,
                        "Got unexpected exception in dispatch " + getInfo() +
                        ": ", t);
                }

                sendError(req, true);

                return;
            } finally {
                if (!isOpen()) {
                    // Link has been closed, shutdown writeProcessor
                    shutdownWriteProcessor();
                }
            }

            if (SipMonitoring.isEnabled(SipMonitoring.NETWORK_MANAGER)) {
                _networkManager.incrEasSentSipRequests();
            }
        }
    }
}
TOP

Related Classes of com.ericsson.ssa.container.LinkBase$WriteRequestTask

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.