org.wso2.carbon.cloud.gateway.transport.server.CGThriftServerHandler.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.carbon.cloud.gateway.transport.server.CGThriftServerHandler.java

Source

/*
 * Copyright WSO2, Inc. (http://wso2.com)
 *
 * 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.wso2.carbon.cloud.gateway.transport.server;

import org.apache.axis2.AxisFault;
import org.apache.axis2.transport.base.threads.WorkerPool;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.thrift.TException;
import org.wso2.carbon.cloud.gateway.common.CGUtils;
import org.wso2.carbon.cloud.gateway.common.thrift.gen.CloudGatewayService;
import org.wso2.carbon.cloud.gateway.common.thrift.gen.Message;
import org.wso2.carbon.cloud.gateway.common.thrift.gen.NotAuthorizedException;
import org.wso2.carbon.core.common.AuthenticationException;
import org.wso2.carbon.core.services.authentication.AuthenticationAdmin;

import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.Semaphore;

/**
 * This implements the handler for Thrift server. This uses static objects(for buffers) in order to make
 * sure that standalone deployment and Stratos based deployment will work.
 * Since static objects can be accessed from anywhere make sure to review (in order to avoid stealing
 * from this buffers) any custom code deployed into the same JVM.
 */
public class CGThriftServerHandler implements CloudGatewayService.Iface {

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

    private WorkerPool workerPool;

    public CGThriftServerHandler(WorkerPool workerPool) {
        this.workerPool = workerPool;
    }

    /**
     * A list of semaphores for two way messages
     */
    private static Map<String, Semaphore> semaphoreMap = new ConcurrentHashMap<String, Semaphore>();

    /**
     * When the back end worker task received a response that will be placed in this buffer to pick
     * up by the blocked thread. This buffer is used to communicate between two threads
     */
    private static Map<String, Message> middleBuffer = new ConcurrentHashMap<String, Message>();

    /**
     * Keep track of authorized list of queues in the form of queueName->SecureUUID
     */
    private static Map<String, String> authorizedQueues = new ConcurrentHashMap<String, String>();

    /**
     * List of request buffers which holds the request messages in the form of
     * SecureUUID->BlockingQueue buffer
     */
    private static Map<String, BlockingQueue<Message>> requestBuffers = new ConcurrentHashMap<String, BlockingQueue<Message>>();

    /**
     * The response message buffer
     */
    private static BlockingQueue<Message> responseBuffer = new LinkedBlockingQueue<Message>();

    /**
     * Allow access to user userName for the buffer queueName and return the token or throw
     * exception if user can't allow access to the buffer
     *
     * @param userName  user name of the client
     * @param password  password of the client
     * @param queueName the name of the buffer to use should allow access to
     * @return a token to use for server buffer access
     * @throws NotAuthorizedException throws in case of illegal access
     * @throws TException             throws in case of an connection error
     */
    public String login(String userName, String password, String queueName)
            throws NotAuthorizedException, TException {
        // check if this user is configured
        AuthenticationAdmin authAdmin = new AuthenticationAdmin();
        try {
            if (!authAdmin.login(userName, password, "localhost")) {
                throw new NotAuthorizedException("User '" + userName + "' not authorized to access" + " buffers");
            }
        } catch (AuthenticationException e) {
            throw new NotAuthorizedException(e.getMessage());
        }
        SecureRandom rand = new SecureRandom();
        String token = Integer.toString(rand.nextInt());
        if (authorizedQueues.containsKey(queueName)) {
            // an already authorized user try to login again let him/her login again
            String oldToken = authorizedQueues.get(queueName);
            authorizedQueues.remove(queueName);
            if (requestBuffers.containsKey(oldToken)) {
                BlockingQueue<Message> oldBuffer = requestBuffers.remove(oldToken);
                // initialize the new buffer with the existing messages
                requestBuffers.put(token, new LinkedBlockingQueue<Message>(oldBuffer));
            }
        } else {
            // initialize the buffer for this request
            requestBuffers.put(token, new LinkedBlockingQueue<Message>());
        }
        authorizedQueues.put(queueName, token);

        return token;
    }

    /**
     * This will perform the exchange of data buffers between the client and the server.
     * This must do it's all operations without blocking as much as possible.
     *
     * @param responseMessageList The response buffer from thrift client
     * @param blockSize           size of the message bulk that need to return client
     * @param token               the token to authorize the exchange operation
     * @return a message bulk of size, 'size' if possible
     * @throws NotAuthorizedException in case the provided token is invalid
     * @throws TException             in case of an error
     */
    public List<Message> exchange(List<Message> responseMessageList, int blockSize, String token)
            throws NotAuthorizedException, TException {
        if (!authorizedQueues.containsValue(token)) {
            throw new NotAuthorizedException("You don't have required permission to access the buffers");
        }

        // if there is any response messages copy the response from the client to server's response
        // buffer and then hand over the copy/move operations into separate threads because
        // the processing of buffers are independent of the exchange operation
        if (responseMessageList.size() > 0) {
            workerPool.execute(new MessageCopyTask(responseMessageList, responseBuffer));
        }
        List<Message> requestMsgList = new ArrayList<Message>();

        // if there is any request messages send them to the client
        if (requestBuffers.size() > 0) {
            BlockingQueue<Message> requestBuffer = requestBuffers.get(token);
            if (requestBuffer != null) {
                try {
                    CGUtils.moveElements(requestBuffer, requestMsgList, blockSize);
                } catch (AxisFault axisFault) {
                    log.error("Error while moving elements :", axisFault);
                }
            }
        }
        return requestMsgList;
    }

    /**
     * Add a message into the server's request buffer, wait if the buffer is full.
     *
     * @param msg   the new Thrift message
     * @param token the token to look up the real buffer
     * @throws AxisFault in case of an error - for e.g. out of space in the queue
     */
    public static void addRequestMessage(Message msg, String token) throws AxisFault {
        try {
            BlockingQueue<Message> buffer = requestBuffers.get(token);
            if (buffer == null) {
                throw new AxisFault("The requested buffer is not found");
            }
            buffer.put(msg);
        } catch (Exception e) {
            throw new AxisFault(e.getMessage(), e);
        }
    }

    /**
     * Return the head of the response buffer, not that if there are no message it'll
     * block if no messages available.
     * <p/>
     * Note that since the buffers are static objects anybody has the access to buffers (and
     * the data inside them) but before deploy any custom be warned to review them!
     *
     * @return the message if there is any or null in case of this thread is interrupted
     */
    public static Message getResponseMessage() {
        try {
            // block if there is no messages
            return responseBuffer.take();
        } catch (InterruptedException e) {
            // ignore
        }
        return null;
    }

    /**
     * Return the request message buffer
     * <p/>
     * Note that since the buffers are static objects anybody has the access to buffers (and
     * the data inside them) but before deploy any custom be warned to review them!
     *
     * @param token the token to the buffer is bound to
     * @return request message buffer
     */
    public BlockingQueue<Message> getRequestBuffer(String token) {
        return requestBuffers.get(token);
    }

    /**
     * Return the token give the queue name
     *
     * @param queueName queue name
     * @return the secure token that this queue is bound
     */
    public static String getSecureUUID(String queueName) {
        return authorizedQueues.get(queueName);
    }

    /**
     * Return the list of request buffers
     * <p/>
     * Note that since the buffers are static objects anybody has the access to buffers (and
     * the data inside them) but before deploy any custom be warned to review them!
     *
     * @return request buffer list
     */
    public static Map<String, BlockingQueue<Message>> getRequestBuffers() {
        return requestBuffers;
    }

    /**
     * Add a new request buffer to the list of request buffers
     *
     * @param token the token for the new buffer
     */
    public static void addNewRequestBuffer(final String token) {
        requestBuffers.put(token, new LinkedBlockingQueue<Message>());
    }

    public static Map<String, Semaphore> getSemaphoreMap() {
        return semaphoreMap;
    }

    public static Map<String, Message> getMiddleBuffer() {
        return middleBuffer;
    }

    /**
     * An asynchronous message copy task among buffers
     */
    private class MessageCopyTask implements Runnable {
        private List<Message> src;
        private BlockingQueue<Message> dest;

        private MessageCopyTask(List<Message> src, BlockingQueue<Message> dest) {
            this.src = src;
            this.dest = dest;
        }

        public void run() {
            copyElements(this.src, this.dest);
        }

        private void copyElements(final List<Message> src, BlockingQueue<Message> dest) {
            dest.addAll(src);
        }
    }
}