# TargetContext.java

    /**
     *  Copyright (c) 2009, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
     *
     *  Licensed under the Apache License, Version 2.0 (the "License");
     *  you may not use this file except in compliance with the License.
     *  You may obtain a copy of the License at
     *
     *        http://www.apache.org/licenses/LICENSE-2.0
     *
     *  Unless required by applicable law or agreed to in writing, software
     *  distributed under the License is distributed on an "AS IS" BASIS,
     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     *  See the License for the specific language governing permissions and
     *  limitations under the License.
     */

    package org.apache.synapse.transport.passthru;

    import org.apache.axis2.context.MessageContext;
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.apache.http.HttpRequest;
    import org.apache.http.nio.NHttpConnection;
    import org.apache.synapse.commons.CorrelationConstants;
    import org.apache.synapse.commons.logger.ContextAwareLogger;
    import org.apache.synapse.transport.passthru.config.TargetConfiguration;
    import org.apache.synapse.transport.passthru.util.ControlledByteBuffer;

    /**
     * When a connection is created, an object of this class is stored in the Connection Context.
     * It is used as a holder for information required during the life-cycle of this connection.
     */
    public class TargetContext {
        private TargetConfiguration targetConfiguration = null;

        public static final String CONNECTION_INFORMATION = "CONNECTION_INFORMATION";
        /** The request for this connection */
        private TargetRequest request;
        /** The response for this connection */
        private TargetResponse response;
        /** State of the connection */
        private ProtocolState state;
        /** The request message context */
        private MessageContext requestMsgCtx;
        /** The current reader */
        private Pipe reader;
        /** The current writer */
        private Pipe writer;
        /** logger for correlation.log */
        private static final Log correlationLog = LogFactory.getLog(PassThroughConstants.CORRELATION_LOGGER);
        /** Time that state got updated*/
        private long lastStateUpdatedTime;

        public TargetContext(TargetConfiguration targetConfiguration) {
            this.targetConfiguration = targetConfiguration;
        }

        public ProtocolState getState() {
            return state;
        }

        public void setState(ProtocolState state) {
            this.state = state;
        }

        public TargetConfiguration getTargetConfiguration() {
            return targetConfiguration;
        }

        public TargetRequest getRequest() {
            return request;
        }

        public void setRequest(TargetRequest request) {
            this.request = request;
        }

        public TargetResponse getResponse() {
            return response;
        }

        public void setResponse(TargetResponse response) {
            this.response = response;
        }

        public MessageContext getRequestMsgCtx() {
            return requestMsgCtx;
        }

        public void setRequestMsgCtx(MessageContext requestMsgCtx) {
            this.requestMsgCtx = requestMsgCtx;
        }

        public Pipe getReader() {
            return reader;
        }

        public Pipe getWriter() {
            return writer;
        }

        public void setReader(Pipe reader) {
            this.reader = reader;
        }

        public void setWriter(Pipe writer) {
            this.writer = writer;
        }

        /**
         * Reset the resources associated with this context
         */
        public void reset() {
           reset(false);
        }

        /**
         * Reset the resources associated with this context
         *
         * @param isError whether an error is causing this shutdown of the connection.
         *                It is very important to set this flag correctly.
         *                When an error causing the shutdown of the connections we should not
         *                release associated writer buffer to the pool as it might lead into
         *                situations like same buffer is getting released to both source and target
         *                buffer factories
         */
        public void reset(boolean isError) {
            request = null;
            response = null;

            if (writer != null) {
                if (!isError) {      // If there is an error we do not release the buffer to the factory
                    ControlledByteBuffer buffer = writer.getBuffer();
                    targetConfiguration.getBufferFactory().release(buffer);
                }
            }

            reader = null;
            writer = null;
        }

        public static void create(NHttpConnection conn, ProtocolState state,
                                  TargetConfiguration configuration) {
            TargetContext targetContext = new TargetContext(configuration);
            conn.getContext().setAttribute(CONNECTION_INFORMATION, targetContext);
            targetContext.setState(state);
            if (targetContext.getTargetConfiguration().isCorrelationLoggingEnabled()) {
                targetContext.updateLastStateUpdatedTime();
            }
        }

        public static void updateState(NHttpConnection conn, ProtocolState state) {

            TargetContext targetContext = (TargetContext)
                    conn.getContext().getAttribute(CONNECTION_INFORMATION);
            if (targetContext != null) {
                if(targetContext.getState() == state) {
                    return;
                }
                targetContext.setState(state);
                if (targetContext.getTargetConfiguration().isCorrelationLoggingEnabled() && isCorrelationIdAvailable(conn)) {
                    long lastStateUpdateTime = targetContext.getLastStateUpdatedTime();
                    String url = "", method = "";
                    if (targetContext.getRequest() != null) {
                        url = targetContext.getRequest().getUrl().toString();
                        method = targetContext.getRequest().getMethod();
                    } else {
                        HttpRequest httpRequest = conn.getHttpRequest();
                        if (httpRequest != null) {
                            url = httpRequest.getRequestLine().getUri();
                            method = httpRequest.getRequestLine().getMethod();
                        }
                    }
                    if ((method.length() != 0) && (url.length() != 0)) {
                        ContextAwareLogger.getLogger(conn.getContext(), correlationLog, false)
                                .info((targetContext.updateLastStateUpdatedTime() - lastStateUpdateTime)
                                        + "|HTTP State Transition|"
                                        + conn.getContext().getAttribute("http.connection") + "|" + method + "|" + url
                                        + "|" + state.name());
                    }
                }
            } else {
                throw new IllegalStateException("Connection information should be present");
            }
        }

        public static boolean assertState(NHttpConnection conn, ProtocolState state) {
            TargetContext info = (TargetContext)
                    conn.getContext().getAttribute(CONNECTION_INFORMATION);

            return info != null && info.getState() == state;

        }

        public static ProtocolState getState(NHttpConnection conn) {
            TargetContext info = (TargetContext)
                    conn.getContext().getAttribute(CONNECTION_INFORMATION);

            return info != null ? info.getState() : null;
        }

        public static void setRequest(NHttpConnection conn, TargetRequest request) {
            TargetContext info = (TargetContext)
                    conn.getContext().getAttribute(CONNECTION_INFORMATION);

            if (info != null) {
                info.setRequest(request);
            } else {
                throw new IllegalStateException("Connection information should be present");
            }
        }

        public static void setResponse(NHttpConnection conn, TargetResponse response) {
            TargetContext info = (TargetContext)
                    conn.getContext().getAttribute(CONNECTION_INFORMATION);

            if (info != null) {
                info.setResponse(response);
            } else {
                throw new IllegalStateException("Connection information should be present");
            }
        }

        public static TargetRequest getRequest(NHttpConnection conn) {
            TargetContext info = (TargetContext)
                    conn.getContext().getAttribute(CONNECTION_INFORMATION);

            return info != null ? info.getRequest() : null;
        }

        public static TargetResponse getResponse(NHttpConnection conn) {
            TargetContext info = (TargetContext)
                    conn.getContext().getAttribute(CONNECTION_INFORMATION);

            return info != null ? info.getResponse() : null;
        }

        public static TargetContext get(NHttpConnection conn) {
            return (TargetContext) conn.getContext().getAttribute(CONNECTION_INFORMATION);
        }

        public long getLastStateUpdatedTime() {
            return lastStateUpdatedTime;
        }

        public long updateLastStateUpdatedTime() {
            this.lastStateUpdatedTime = System.currentTimeMillis();
            return this.lastStateUpdatedTime;
        }

        public static Boolean isCorrelationIdAvailable(NHttpConnection connection) {
            if (connection.getContext().getAttribute(CorrelationConstants.CORRELATION_ID) != null) {
                return true;
            }
            return false;
        }
    }