com.meidusa.venus.client.RemotingInvocationHandler.java Source code

Java tutorial

Introduction

Here is the source code for com.meidusa.venus.client.RemotingInvocationHandler.java

Source

/*
 * Copyright 2008-2108 amoeba.meidusa.com 
 * 
 *    This program is free software; you can redistribute it and/or modify it under the terms of 
 * the GNU AFFERO GENERAL PUBLIC LICENSE as published by the Free Software Foundation; either version 3 of the License, 
 * or (at your option) any later version. 
 * 
 *    This program is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
 * See the GNU AFFERO GENERAL PUBLIC LICENSE for more details. 
 *    You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE along with this program; 
 * if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

package com.meidusa.venus.client;

import com.meidusa.fastjson.JSON;
import com.meidusa.fastmark.feature.SerializerFeature;
import com.meidusa.toolkit.common.bean.util.InitialisationException;
import com.meidusa.toolkit.net.BackendConnection;
import com.meidusa.toolkit.net.BackendConnectionPool;
import com.meidusa.toolkit.util.TimeUtil;
import com.meidusa.venus.annotations.*;
import com.meidusa.venus.annotations.util.AnnotationUtil;
import com.meidusa.venus.client.xml.bean.EndpointConfig;
import com.meidusa.venus.client.xml.bean.ServiceConfig;
import com.meidusa.venus.exception.*;
import com.meidusa.venus.extension.athena.AthenaTransactionId;
import com.meidusa.venus.extension.athena.delegate.AthenaTransactionDelegate;
import com.meidusa.venus.io.network.AbstractBIOConnection;
import com.meidusa.venus.io.packet.*;
import com.meidusa.venus.io.packet.serialize.SerializeServiceRequestPacket;
import com.meidusa.venus.io.packet.serialize.SerializeServiceResponsePacket;
import com.meidusa.venus.io.serializer.Serializer;
import com.meidusa.venus.io.serializer.SerializerFactory;
import com.meidusa.venus.metainfo.EndpointParameter;
import com.meidusa.venus.notify.InvocationListener;
import com.meidusa.venus.notify.ReferenceInvocationListener;
import com.meidusa.venus.poolable.RequestLoadbalanceObjectPool;
import com.meidusa.venus.util.UUID;
import com.meidusa.venus.util.Utils;
import com.meidusa.venus.util.VenusAnnotationUtils;
import com.meidusa.venus.util.VenusTracerUtil;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.pool.ObjectPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 
 * @author Struct
 * 
 */
public class RemotingInvocationHandler extends VenusInvocationHandler {
    private static SerializerFeature[] JSON_FEATURE = new SerializerFeature[] { SerializerFeature.ShortString };
    private static Logger logger = LoggerFactory.getLogger(RemotingInvocationHandler.class);
    private static Logger performanceLogger = LoggerFactory.getLogger("venus.client.performance");
    private InvocationListenerContainer container;
    private ObjectPool bioConnPool;
    private BackendConnectionPool nioConnPool;
    private static AtomicLong sequenceId = new AtomicLong(1);
    private VenusServiceFactory serviceFactory;
    private VenusExceptionFactory venusExceptionFactory;
    private boolean enableAsync = true;
    private byte serializeType = PacketConstant.CONTENT_TYPE_JSON;

    public boolean isEnableAsync() {
        return enableAsync;
    }

    public void setEnableAsync(boolean enableAsync) {
        this.enableAsync = enableAsync;
    }

    public VenusExceptionFactory getVenusExceptionFactory() {
        return venusExceptionFactory;
    }

    public void setVenusExceptionFactory(VenusExceptionFactory venusExceptionFactory) {
        this.venusExceptionFactory = venusExceptionFactory;
    }

    public void setServiceFactory(VenusServiceFactory serviceFactory) {
        this.serviceFactory = serviceFactory;
    }

    public short getSerializeType() {
        return serializeType;
    }

    public void setSerializeType(byte serializeType) {
        this.serializeType = serializeType;
    }

    public InvocationListenerContainer getContainer() {
        return container;
    }

    public void setContainer(InvocationListenerContainer container) {
        this.container = container;
    }

    public ObjectPool getBioConnPool() {
        return bioConnPool;
    }

    public void setBioConnPool(ObjectPool bioConnPool) {
        this.bioConnPool = bioConnPool;
    }

    public BackendConnectionPool getNioConnPool() {
        return nioConnPool;
    }

    public void setNioConnPool(BackendConnectionPool nioConnPool) {
        this.nioConnPool = nioConnPool;
    }

    protected Object invokeRemoteService(Service service, Endpoint endpoint, Method method,
            EndpointParameter[] params, Object[] args) throws Exception {
        String apiName = VenusAnnotationUtils.getApiname(method, service, endpoint);

        AthenaTransactionId athenaTransactionId = null;
        if (service.athenaFlag()) {
            athenaTransactionId = AthenaTransactionDelegate.getDelegate().startClientTransaction(apiName);
        }
        boolean async = false;

        if (endpoint.async()) {
            async = true;
        }

        byte[] traceID = VenusTracerUtil.getTracerID();

        if (traceID == null) {
            traceID = VenusTracerUtil.randomTracerID();
        }

        Serializer serializer = SerializerFactory.getSerializer(serializeType);

        SerializeServiceRequestPacket serviceRequestPacket = new SerializeServiceRequestPacket(serializer, null);

        serviceRequestPacket.clientId = PacketConstant.VENUS_CLIENT_ID;
        serviceRequestPacket.clientRequestId = sequenceId.getAndIncrement();
        serviceRequestPacket.traceId = traceID;
        serviceRequestPacket.apiName = apiName;
        serviceRequestPacket.serviceVersion = service.version();
        serviceRequestPacket.parameterMap = new HashMap<String, Object>();

        if (params != null) {
            for (int i = 0; i < params.length; i++) {
                if (args[i] instanceof InvocationListener) {
                    async = true;
                    ReferenceInvocationListener listener = new ReferenceInvocationListener();
                    ServicePacketBuffer buffer = new ServicePacketBuffer(16);
                    buffer.writeLengthCodedString(args[i].getClass().getName(), "utf-8");
                    buffer.writeInt(System.identityHashCode(args[i]));
                    listener.setIdentityData(buffer.toByteBuffer().array());
                    Type type = method.getGenericParameterTypes()[i];
                    if (type instanceof ParameterizedType) {
                        ParameterizedType genericType = ((ParameterizedType) type);
                        container.putInvocationListener((InvocationListener) args[i],
                                genericType.getActualTypeArguments()[0]);
                    } else {
                        throw new InvalidParameterException("invocationListener is not generic");
                    }

                    serviceRequestPacket.parameterMap.put(params[i].getParamName(), listener);
                } else {
                    serviceRequestPacket.parameterMap.put(params[i].getParamName(), args[i]);
                }

            }
        }
        setTransactionId(serviceRequestPacket, athenaTransactionId);

        PerformanceLevel pLevel = AnnotationUtil.getAnnotation(method.getAnnotations(), PerformanceLevel.class);
        long start = TimeUtil.currentTimeMillis();
        long borrowed = start;

        if (async) {
            if (!this.isEnableAsync()) {
                throw new VenusConfigException("service async call disabled");
            }

            BackendConnection conn = null;
            try {

                if (nioConnPool instanceof RequestLoadbalanceObjectPool) {
                    conn = (BackendConnection) ((RequestLoadbalanceObjectPool) nioConnPool)
                            .borrowObject(serviceRequestPacket.parameterMap, endpoint);
                } else {
                    conn = nioConnPool.borrowObject();
                }
                borrowed = TimeUtil.currentTimeMillis();

                conn.write(serviceRequestPacket.toByteBuffer());
                VenusTracerUtil.logRequest(traceID, serviceRequestPacket.apiName,
                        JSON.toJSONString(serviceRequestPacket.parameterMap, JSON_FEATURE));
                return null;
            } finally {
                if (service.athenaFlag()) {
                    AthenaTransactionDelegate.getDelegate().completeClientTransaction();
                }
                if (performanceLogger.isDebugEnabled()) {
                    long end = TimeUtil.currentTimeMillis();
                    long time = end - borrowed;
                    StringBuffer buffer = new StringBuffer();
                    buffer.append("[").append(borrowed - start).append(",").append(time)
                            .append("]ms (*client,async*) traceID=").append(UUID.toString(traceID)).append(", api=")
                            .append(serviceRequestPacket.apiName);

                    performanceLogger.debug(buffer.toString());
                }

                if (conn != null) {
                    nioConnPool.returnObject(conn);
                }
            }
        } else {
            AbstractBIOConnection conn = null;
            int soTimeout = 0;
            int oldTimeout = 0;
            boolean success = true;
            int errorCode = 0;
            AbstractServicePacket packet = null;
            String remoteAddress = null;
            boolean invalided = false;
            try {
                if (bioConnPool instanceof RequestLoadbalanceObjectPool) {
                    conn = (AbstractBIOConnection) ((RequestLoadbalanceObjectPool) bioConnPool)
                            .borrowObject(serviceRequestPacket.parameterMap, endpoint);
                } else {
                    conn = (AbstractBIOConnection) bioConnPool.borrowObject();
                }
                remoteAddress = conn.getRemoteAddress();
                borrowed = TimeUtil.currentTimeMillis();
                ServiceConfig config = this.serviceFactory.getServiceConfig(method.getDeclaringClass());

                oldTimeout = conn.getSoTimeout();
                if (config != null) {
                    EndpointConfig endpointConfig = config.getEndpointConfig(endpoint.name());
                    if (endpointConfig != null) {
                        int eTimeOut = endpointConfig.getTimeWait();
                        if (eTimeOut > 0) {
                            soTimeout = eTimeOut;
                        }
                    } else {
                        if (config.getTimeWait() > 0) {
                            soTimeout = config.getTimeWait();
                        } else {
                            if (endpoint.timeWait() > 0) {
                                soTimeout = endpoint.timeWait();
                            }
                        }
                    }

                } else {

                    if (endpoint.timeWait() > 0) {
                        soTimeout = endpoint.timeWait();
                    }
                }

                byte[] bts;

                try {

                    if (soTimeout > 0) {
                        conn.setSoTimeout(soTimeout);
                    }
                    conn.write(serviceRequestPacket.toByteArray());
                    VenusTracerUtil.logRequest(traceID, serviceRequestPacket.apiName,
                            JSON.toJSONString(serviceRequestPacket.parameterMap, JSON_FEATURE));
                    bts = conn.read();
                } catch (IOException e) {
                    try {
                        conn.close();
                    } catch (Exception e1) {
                        // ignore
                    }

                    bioConnPool.invalidateObject(conn);
                    invalided = true;
                    Class<?>[] eClass = method.getExceptionTypes();

                    if (eClass != null && eClass.length > 0) {
                        for (Class<?> clazz : eClass) {
                            if (e.getClass().isAssignableFrom(clazz)) {
                                throw e;
                            }
                        }
                    }

                    throw new RemoteSocketIOException("api=" + serviceRequestPacket.apiName + ", remoteIp="
                            + conn.getRemoteAddress() + ",(" + e.getMessage() + ")", e);
                }

                int type = AbstractServicePacket.getType(bts);
                switch (type) {
                case PacketConstant.PACKET_TYPE_ERROR:
                    ErrorPacket error = new ErrorPacket();
                    error.init(bts);
                    packet = error;
                    Exception e = venusExceptionFactory.getException(error.errorCode, error.message);
                    if (e == null) {
                        throw new DefaultVenusException(error.errorCode, error.message);
                    } else {
                        if (error.additionalData != null) {
                            Map<String, Type> tmap = Utils.getBeanFieldType(e.getClass(), Exception.class);
                            if (tmap != null && tmap.size() > 0) {
                                Object obj = serializer.decode(error.additionalData, tmap);
                                BeanUtils.copyProperties(e, obj);
                            }
                        }
                        throw e;
                    }
                case PacketConstant.PACKET_TYPE_OK:
                    OKPacket ok = new OKPacket();
                    ok.init(bts);
                    packet = ok;
                    return null;
                case PacketConstant.PACKET_TYPE_SERVICE_RESPONSE:
                    ServiceResponsePacket response = new SerializeServiceResponsePacket(serializer,
                            method.getGenericReturnType());
                    response.init(bts);
                    packet = response;
                    return response.result;
                default: {
                    logger.warn("unknow response type=" + type);
                    success = false;
                    return null;
                }
                }
            } catch (Exception e) {
                success = false;

                if (e instanceof CodedException
                        || (errorCode = venusExceptionFactory.getErrorCode(e.getClass())) != 0
                        || e instanceof RuntimeException) {
                    if (e instanceof CodedException) {
                        errorCode = ((CodedException) e).getErrorCode();
                    }
                    throw e;
                } else {
                    RemoteException code = e.getClass().getAnnotation(RemoteException.class);
                    if (code != null) {
                        errorCode = code.errorCode();
                        throw e;
                    } else {
                        ExceptionCode eCode = e.getClass().getAnnotation(ExceptionCode.class);
                        if (eCode != null) {
                            errorCode = eCode.errorCode();
                            throw e;
                        } else {
                            errorCode = -1;
                            if (conn == null) {
                                throw new DefaultVenusException(e.getMessage(), e);
                            } else {
                                throw new DefaultVenusException(
                                        e.getMessage() + ". remoteAddress=" + conn.getRemoteAddress(), e);
                            }
                        }
                    }
                }
            } finally {
                if (service.athenaFlag()) {
                    AthenaTransactionDelegate.getDelegate().completeClientTransaction();
                }
                long end = TimeUtil.currentTimeMillis();
                long time = end - borrowed;
                StringBuffer buffer = new StringBuffer();
                buffer.append("[").append(borrowed - start).append(",").append(time)
                        .append("]ms (*client,sync*) traceID=").append(UUID.toString(traceID)).append(", api=")
                        .append(serviceRequestPacket.apiName);
                if (remoteAddress != null) {
                    buffer.append(", remote=").append(remoteAddress);
                } else {
                    buffer.append(", pool=").append(bioConnPool.toString());
                }
                buffer.append(", clientID=").append(PacketConstant.VENUS_CLIENT_ID).append(", requestID=")
                        .append(serviceRequestPacket.clientRequestId);

                if (packet != null) {
                    if (packet instanceof ErrorPacket) {
                        buffer.append(", errorCode=").append(((ErrorPacket) packet).errorCode);
                        buffer.append(", message=").append(((ErrorPacket) packet).message);
                    } else {
                        buffer.append(", errorCode=0");
                    }
                }

                if (pLevel != null) {

                    if (pLevel.printParams()) {
                        buffer.append(", params=");
                        buffer.append(JSON.toJSONString(serviceRequestPacket.parameterMap, JSON_FEATURE));
                    }

                    if (time > pLevel.error() && pLevel.error() > 0) {
                        if (performanceLogger.isErrorEnabled()) {
                            performanceLogger.error(buffer.toString());
                        }
                    } else if (time > pLevel.warn() && pLevel.warn() > 0) {
                        if (performanceLogger.isWarnEnabled()) {
                            performanceLogger.warn(buffer.toString());
                        }
                    } else if (time > pLevel.info() && pLevel.info() > 0) {
                        if (performanceLogger.isInfoEnabled()) {
                            performanceLogger.info(buffer.toString());
                        }
                    } else {
                        if (performanceLogger.isDebugEnabled()) {
                            performanceLogger.debug(buffer.toString());
                        }
                    }
                } else {
                    buffer.append(", params=");
                    buffer.append(JSON.toJSONString(serviceRequestPacket.parameterMap, JSON_FEATURE));

                    if (time >= 30 * 1000) {
                        if (performanceLogger.isErrorEnabled()) {
                            performanceLogger.error(buffer.toString());
                        }
                    } else if (time >= 10 * 1000) {
                        if (performanceLogger.isWarnEnabled()) {
                            performanceLogger.warn(buffer.toString());
                        }
                    } else if (time >= 5 * 1000) {
                        if (performanceLogger.isInfoEnabled()) {
                            performanceLogger.info(buffer.toString());
                        }
                    } else {
                        if (performanceLogger.isDebugEnabled()) {
                            performanceLogger.debug(buffer.toString());
                        }
                    }
                }

                if (conn != null && !invalided) {
                    if (!conn.isClosed() && soTimeout > 0) {
                        conn.setSoTimeout(oldTimeout);
                    }
                    bioConnPool.returnObject(conn);
                }

            }
        }
    }

    private void setTransactionId(SerializeServiceRequestPacket serviceRequestPacket,
            AthenaTransactionId athenaTransactionId) {
        if (athenaTransactionId != null) {
            if (athenaTransactionId.getRootId() != null) {
                serviceRequestPacket.rootId = athenaTransactionId.getRootId().getBytes();
            }

            if (athenaTransactionId.getParentId() != null) {
                serviceRequestPacket.parentId = athenaTransactionId.getParentId().getBytes();
            }

            if (athenaTransactionId.getMessageId() != null) {
                serviceRequestPacket.messageId = athenaTransactionId.getMessageId().getBytes();
            }
        }
    }

    public void init() throws InitialisationException {

    }
}