Package com.flipkart.phantom.thrift.impl

Source Code of com.flipkart.phantom.thrift.impl.ThriftProxy

/*
* Copyright 2012-2015, the original author or authors.
*
* 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 com.flipkart.phantom.thrift.impl;

import com.flipkart.phantom.task.spi.AbstractHandler;
import com.flipkart.phantom.task.spi.TaskContext;
import com.flipkart.phantom.thrift.impl.proxy.SocketObjectFactory;
import org.apache.commons.pool.impl.GenericObjectPool;
import org.apache.thrift.ProcessFunction;
import org.apache.thrift.TBase;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TMessage;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.protocol.TProtocolFactory;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
import org.apache.thrift.transport.TTransportException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;

import java.lang.reflect.Method;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

/**
* <code>ThriftProxy</code> holds the details of a ThriftProxy and loads the necessary Thrift Classes.
* Note that this class works only with Thrift classes generated using the IDL compiler version 0.9. This is because
* it uses reflection to determine declared methods on the interface. The target service may be of any version.
* This implementation has been tested  with Thrift versions 0.6 and 0.2.
*
* @author Regunath B
* @version 1.0, 28 March, 2013
*/
public abstract class ThriftProxy extends AbstractHandler implements InitializingBean {

    /** Thrift transport errors */
    private static final Map<Integer, String> THRIFT_ERRORS = new HashMap<Integer, String>();
    static {
        THRIFT_ERRORS.put(TTransportException.UNKNOWN,"Unknown exception");
        THRIFT_ERRORS.put(TTransportException.NOT_OPEN,"Transport not open");
        THRIFT_ERRORS.put(TTransportException.ALREADY_OPEN,"Transport already open");
        THRIFT_ERRORS.put(TTransportException.TIMED_OUT,"Thrift timed out");
        THRIFT_ERRORS.put(TTransportException.END_OF_FILE,"Reached end of file");
    }

    /** The default Thrift interface class name for Thrift services */
    private static final String DEFAULT_SERVICE_INTERFACE_NAME="Iface";

    /** The default Thrift TProcessor class name */
    private static final String DEFAULT_PROCESSOR_CLASS_NAME="Processor";

    /** The default Thrift call result class name */
    private static final String DEFAULT_RESULT_CLASS_NAME="_result";

    /** Logger for this class*/
    private static final Logger LOGGER = LoggerFactory.getLogger(ThriftProxy.class);

    /** The Thrift binary protocol factory*/
    private TProtocolFactory protocolFactory =  new TBinaryProtocol.Factory();

    /** The target Thrift server connect details*/
    private String thriftServer;
    private int thriftPort;
    private int thriftTimeoutMillis = -1;

    /** The fully qualified class name of the Thrift service generated by the Thrift compiler from the IDL file*/
    private String thriftServiceClass;

    /** Map of the method names and the respective Thrift ProcessFunction instances*/
    @SuppressWarnings("rawtypes")
    protected Map<String, ProcessFunction> processMap = new HashMap<String, ProcessFunction>();

    /** Properties for initializing Generic Object Pool */
    private int poolSize =10;
    private long maxWait = 100;
    private int maxIdle = poolSize;
    private int minIdle = poolSize/2;
    private long timeBetweenEvictionRunsMillis = 20000;

    /** The GenericObjectPool object */
    private GenericObjectPool<Socket> socketPool;

    /**
     * Interface method implementation. Checks if all mandatory properties have been set
     * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
     */
    public void afterPropertiesSet() throws Exception {
        Assert.notNull(this.thriftServer, "The 'thriftServer' may not be null");
        Assert.notNull(this.thriftServiceClass, "The 'thriftServiceClass' may not be null");
    }

    /**
     * Initialize this ThriftProxy
     */
    public void init(TaskContext context) throws Exception {
        if(this.thriftServiceClass == null) {
            throw new AssertionError("The 'thriftServiceClass' may not be null");
        }
        if(this.processMap==null || this.processMap.isEmpty()) {
            throw new AssertionError("ProcessFunctions not populated. Maybe The 'thriftServiceClass' is not a valid class?");
        }
        if (this.thriftTimeoutMillis == -1) { // implying none set
            throw new Exception("'thriftTimeoutMillis' must be set to a non-negative value!");
        }

        //Create pool
        this.socketPool = new GenericObjectPool<Socket>(
                new SocketObjectFactory(this),
                this.poolSize,
                GenericObjectPool.WHEN_EXHAUSTED_GROW,
                this.maxWait ,
                this.maxIdle ,
                this.minIdle , false, false,
                this.timeBetweenEvictionRunsMillis,
                GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN,
                GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS,
                true);
    }

    /**
     *
     * Is called by the {@link com.flipkart.phantom.thrift.impl.ThriftProxyExecutor#run()} for processing the request.
     * Currently not utilizing the generic pooled objects.
     *
     * @param clientTransport
     * @return transport {@link TTransport} containing clientOutput
     * @throws Exception
     */
    @SuppressWarnings("rawtypes")
    public TTransport doRequest(TTransport clientTransport)
    {
        TSocket serviceSocket = null;
        try
        {
            //Get Protocol from transport
            TProtocol clientProtocol = this.protocolFactory.getProtocol(clientTransport);

            TMessage message = clientProtocol.readMessageBegin();
            //Arguments
            ProcessFunction invokedProcessFunction = this.getProcessMap().get(message.name);
            if (invokedProcessFunction == null) {
                throw new RuntimeException("Unable to find a matching ProcessFunction for invoked method : " + message.name);
            }
            TBase args = invokedProcessFunction.getEmptyArgsInstance(); // get the empty args. The values will then be read from the client's TProtocol
            //Read the argument values from the client's TProtocol
            args.read(clientProtocol);
            clientProtocol.readMessageEnd();

            // Instantiate the call result object using the Thrift naming convention used for classes
            TBase result = (TBase) Class.forName( this.getThriftServiceClass() + "$" + message.name + DEFAULT_RESULT_CLASS_NAME).newInstance();

            serviceSocket = new TSocket(this.getThriftServer(), this.getThriftPort(), this.getThriftTimeoutMillis());
            TProtocol serviceProtocol = new TBinaryProtocol(serviceSocket);
            serviceSocket.open();

            //Send the arguments to the server and relay the response back
            //Create the custom TServiceClient client which sends request to actual Thrift servers and relays the response back to the client
            ProxyServiceClient proxyClient = new ProxyServiceClient(clientProtocol,serviceProtocol,serviceProtocol);

            //Send the request
            proxyClient.sendBase(message.name, args, message.seqid);
            //Get the response back (it is written to client's TProtocol)
            proxyClient.receiveBase(result, message.name);

            LOGGER.debug("Processed message : " + this.getThriftServiceClass() + "." + message.name);
        } catch (Exception e) {
            if (e.getClass().isAssignableFrom(TTransportException.class)) {
                //isConnectionValid = false;
                throw new RuntimeException("Thrift transport exception executing the proxy service call : " +
                        THRIFT_ERRORS.get(((TTransportException)e).getType()), e);
            } else {
                throw new RuntimeException("Exception executing the proxy service call : " + e.getMessage(), e);
            }
        } finally {
            if (serviceSocket != null) {
                serviceSocket.close();
            }
        }
        return clientTransport;
    }

    /**
     * Gets a pooled TSocket instance
     * @return a TSocket instance
     */
    public TSocket getPooledSocket() {
        try {
            return new TSocket(this.socketPool.borrowObject());
        } catch (Exception e) {
            LOGGER.error("Error while borrowing TSocket : " + e.getMessage(),e);
            throw new RuntimeException("Error while borrowing TSocket : " + e.getMessage(),e);
        }
    }

    /**
     * Returns the specified TSocket back to the pool
     * @param socket the pooled TSocket instance
     * @param isConnectionValid flag to indicate if the socket was found to be invalid during use
     */
    public void returnPooledSocket(TSocket socket, boolean isConnectionValid) {
        try {
            if (isConnectionValid) {
                this.socketPool.returnObject(socket.getSocket());
            } else {
                this.socketPool.invalidateObject(socket.getSocket());
            }
        } catch (Exception e) {
            LOGGER.error("Error while returning TSocket : " + e.getMessage(),e);
            throw new RuntimeException("Error while borrowing TSocket : " + e.getMessage(),e);
        }
    }

    /**
     * Get the name of this ThriftProxy.
     * @return the name of this ThriftProxy
     */
    public String getName() {
        return this.thriftServiceClass;
    }

    /**
     * Abstract method implementation
     * @see com.flipkart.phantom.task.spi.AbstractHandler#getType()
     */
    public String getType() {
        return "ThriftProxy";
    }

    /**
     * Shutdown hooks provided by the ThriftProxy
     */
    public void shutdown(TaskContext context) throws Exception {
        super.deactivate();
    }

    /** Getter/Setter methods */
    public String getThriftServer() {
        return thriftServer;
    }
    public void setThriftServer(String thriftServer) {
        this.thriftServer = thriftServer;
    }
    public int getThriftPort() {
        return thriftPort;
    }
    public void setThriftPort(int thriftPort) {
        this.thriftPort = thriftPort;
    }
    public int getThriftTimeoutMillis() {
        return thriftTimeoutMillis;
    }
    public void setThriftTimeoutMillis(int thriftTimeoutMillis) {
        this.thriftTimeoutMillis = thriftTimeoutMillis;
    }
    @SuppressWarnings("rawtypes")
    public void setThriftServiceClass(String thriftServiceClass) {
        this.thriftServiceClass = thriftServiceClass;
        // Inspect and add ProcessFunction instances for all public methods on the declared service interface
        String serviceInterfaceClass = this.thriftServiceClass + "$" + DEFAULT_SERVICE_INTERFACE_NAME;
        try {
            Class serviceClass = Class.forName(serviceInterfaceClass);
            Method[] methods = serviceClass.getDeclaredMethods();
            for (Method method : methods) {
                String processFunctionClass = this.thriftServiceClass + "$" + DEFAULT_PROCESSOR_CLASS_NAME + "$" + method.getName();
                this.processMap.put(method.getName(), (ProcessFunction)Class.forName(processFunctionClass).newInstance());
            }
        } catch (Exception e) {
            LOGGER.error("Unable to inspect specified Thrift service class. Error is : " + e.getMessage(), e);
            // empty the processMap. This will fail the init of this handler in #afterPropertiesSet()
            this.processMap.clear();
        }
    }
    public String getThriftServiceClass() {
        return thriftServiceClass;
    }
    @SuppressWarnings("rawtypes")
  public Map<String, ProcessFunction> getProcessMap() {
        return processMap;
    }
    public int getPoolSize() {
        return poolSize;
    }
    public void setPoolSize(int poolSize) {
        this.poolSize = poolSize;
    }
    public long getMaxWait() {
        return maxWait;
    }
    public void setMaxWait(long maxWait) {
        this.maxWait = maxWait;
    }
    public int getMaxIdle() {
        return maxIdle;
    }
    public void setMaxIdle(int maxIdle) {
        this.maxIdle = maxIdle;
    }
    public int getMinIdle() {
        return minIdle;
    }
    public void setMinIdle(int minIdle) {
        this.minIdle = minIdle;
    }
    public long getTimeBetweenEvictionRunsMillis() {
        return timeBetweenEvictionRunsMillis;
    }
    public void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) {
        this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
    }

    /** End Getter/Setter methods */

}
TOP

Related Classes of com.flipkart.phantom.thrift.impl.ThriftProxy

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.