org.siddhiesb.transport.passthru.DeliveryAgent.java Source code

Java tutorial

Introduction

Here is the source code for org.siddhiesb.transport.passthru.DeliveryAgent.java

Source

/*
 *  Copyright (c) 2005-2008, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 *  WSO2 Inc. licenses this file to you 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.siddhiesb.transport.passthru;

/*import org.apache.axis2.AxisFault;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.addressing.EndpointReference;*/
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpHost;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.nio.NHttpClientConnection;
import org.siddhiesb.common.api.CommonAPIConstants;
import org.siddhiesb.common.api.CommonContext;
import org.siddhiesb.transport.http.conn.ProxyConfig;
import org.siddhiesb.transport.passthru.config.TargetConfiguration;
import org.siddhiesb.transport.passthru.connections.TargetConnections;
import org.siddhiesb.transport.passthru.util.TargetRequestFactory;

import java.util.Queue;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.net.URL;
import java.net.MalformedURLException;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * This class acts as a gateway for differed delivery of the messages. When a message is to be
 * delivered it is submitted to this class. If a connection is available to the target this
 * class will try to deliver the message immediately over that connection. If a connection is
 * not available it will queue the message and request a connection from the pool. When a new
 * connection is available a queued message will be sent through it. 
 */
public class DeliveryAgent {

    private static final Log log = LogFactory.getLog(DeliveryAgent.class);

    /**
     * This Map holds the messages that need to be delivered. But at the moment maximum
     * number of connections to the host:pair is being used. So these messages has to wait
     * until a new connection is available.
     */
    private Map<HttpRoute, Queue<CommonContext>> waitingMessages = new ConcurrentHashMap<HttpRoute, Queue<CommonContext>>();

    /** The connection management */
    private TargetConnections targetConnections;

    /** Configuration of the sender */
    private TargetConfiguration targetConfiguration;

    /** Proxy config */
    private ProxyConfig proxyConfig;

    /** The maximum number of messages that can wait for a connection */
    private int maxWaitingMessages = Integer.MAX_VALUE;

    /** Lock for synchronizing access */
    private Lock lock = new ReentrantLock();

    /**
     * Create a delivery agent with the target configuration and connection management.
     *
     * @param targetConfiguration configuration of the sender
     * @param targetConnections connection management
     */
    public DeliveryAgent(TargetConfiguration targetConfiguration, TargetConnections targetConnections,
            ProxyConfig proxyConfig) {
        this.targetConfiguration = targetConfiguration;
        this.targetConnections = targetConnections;
        this.proxyConfig = proxyConfig;
    }

    /**
     * This method queues the message for delivery. If a connection is already existing for
     * the destination epr, the message will be delivered immediately. Otherwise message has
     * to wait until a connection is established. In this case this method will inform the
     * system about the need for a connection.
     *
     * @param commonContext the message context to be sent
     */
    public void submit(CommonContext commonContext) {
        try {

            String toAddress = (String) commonContext.getProperty(CommonAPIConstants.ENDPOINT);
            URL url = new URL(toAddress);
            String scheme = url.getProtocol() != null ? url.getProtocol() : "http";
            String hostname = url.getHost();
            int port = url.getPort();
            if (port == -1) {
                // use default
                if ("http".equals(scheme)) {
                    port = 80;
                } else if ("https".equals(scheme)) {
                    port = 443;
                }
            }
            HttpHost target = new HttpHost(hostname, port, scheme);
            boolean secure = "https".equalsIgnoreCase(target.getSchemeName());

            HttpHost proxy = null; //proxyConfig.selectProxy(target);

            HttpRoute route;
            if (proxy != null) {
                route = new HttpRoute(target, null, proxy, secure);
            } else {
                route = new HttpRoute(target, null, secure);
            }

            // first we queue the message
            Queue<CommonContext> queue = null;
            lock.lock();
            try {
                queue = waitingMessages.get(route);
                if (queue == null) {
                    queue = new ConcurrentLinkedQueue<CommonContext>();
                    waitingMessages.put(route, queue);
                }
                if (queue.size() == maxWaitingMessages) {
                    CommonContext msgCtx = queue.poll();
                }

                queue.add(commonContext);
            } finally {
                lock.unlock();
            }

            NHttpClientConnection conn = targetConnections.getConnection(route);
            if (conn != null) {
                conn.resetInput();
                conn.resetOutput();
                CommonContext commonContext1 = queue.poll();

                if (commonContext1 != null) {
                    tryNextMessage(commonContext1, route, conn);
                }
            }

        } catch (MalformedURLException e) {
            handleException("Malformed URL in the target EPR", e);
        }
    }

    public void errorConnecting(HttpRoute route, int errorCode, String message) {
        Queue<CommonContext> queue = waitingMessages.get(route);
        if (queue != null) {
            CommonContext msgCtx = queue.poll();
        } else {
            throw new IllegalStateException("Queue cannot be null for: " + route);
        }
    }

    /**
     * Notification for a connection availability. When this occurs a message in the
     * queue for delivery will be tried.
     *
     */
    public void connected(HttpRoute route) {
        Queue<CommonContext> queue = null;
        lock.lock();
        try {
            queue = waitingMessages.get(route);
        } finally {
            lock.unlock();
        }

        while (queue.size() > 0) {
            NHttpClientConnection conn = targetConnections.getConnection(route);
            if (conn != null) {
                CommonContext messageContext = queue.poll();
                if (messageContext != null) {
                    tryNextMessage(messageContext, route, conn);
                }
            } else {
                break;
            }
        }
    }

    private void tryNextMessage(CommonContext commonContext, HttpRoute route, NHttpClientConnection conn) {
        if (conn != null) {
            TargetContext.get(conn).setCommonContext(commonContext);
            submitRequest(conn, route, commonContext);
        }
    }

    private void submitRequest(NHttpClientConnection conn, HttpRoute route, CommonContext commonContext) {
        if (log.isDebugEnabled()) {
            log.debug("Submitting new request to the connection: " + conn);
        }

        TargetRequest request = TargetRequestFactory.create(commonContext, route, targetConfiguration);
        TargetContext.setRequest(conn, request);

        org.siddhiesb.transport.passthru.Pipe pipe = (org.siddhiesb.transport.passthru.Pipe) commonContext
                .getProperty(PassThroughConstants.PASS_THROUGH_PIPE);
        if (pipe != null) {
            pipe.attachConsumer(conn);
            request.connect(pipe);

            /*ToDo: Handle this for builder invoked scenarios*/
            /*if (Boolean.TRUE.equals(msgContext.getProperty(PassThroughConstants.MESSAGE_BUILDER_INVOKED))) {
            synchronized (msgContext) {
                OutputStream out = pipe.getOutputStream();
                msgContext.setProperty(PassThroughConstants.BUILDER_OUTPUT_STREAM, out);
                msgContext.setProperty(PassThroughConstants.WAIT_BUILDER_IN_STREAM_COMPLETE, Boolean.TRUE);
                msgContext.notifyAll();
            }
            return;
            }*/
        }

        conn.requestOutput();
    }

    private void handleException(String s, Exception e) {
        log.error(s, e);
    }
}