com.taobao.tair.comm.TairClient.java Source code

Java tutorial

Introduction

Here is the source code for com.taobao.tair.comm.TairClient.java

Source

/**
 * (C) 2007-2010 Taobao Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 */
package com.taobao.tair.comm;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.mina.common.IoFuture;
import org.apache.mina.common.IoFutureListener;
import org.apache.mina.common.IoSession;
import org.apache.mina.common.WriteFuture;

import com.taobao.tair.etc.TairClientException;
import com.taobao.tair.packet.BasePacket;

public class TairClient {

    private static final Log LOGGER = LogFactory.getLog(TairClient.class);

    private static final boolean isDebugEnabled = LOGGER.isDebugEnabled();

    private static ConcurrentHashMap<Integer, ResponseCallbackTask> callbackTasks = new ConcurrentHashMap<Integer, ResponseCallbackTask>();

    private static long minTimeout = 100L;

    private static ConcurrentHashMap<Integer, ArrayBlockingQueue<Object>> responses = new ConcurrentHashMap<Integer, ArrayBlockingQueue<Object>>();

    private final IoSession session;

    private String key;

    private TairClientFactory clientFactory;
    private static Thread callBackTaskScan = new Thread(new CallbackTasksScan());
    static {
        callBackTaskScan.setName("Thread-" + CallbackTasksScan.class.getName());
        callBackTaskScan.setDaemon(true);
        callBackTaskScan.start();
    }

    protected TairClient(TairClientFactory factory, IoSession session, String key) {
        this.session = session;
        this.key = key;
        this.clientFactory = factory;
    }

    public Object invoke(final BasePacket packet, final long timeout) throws TairClientException {
        if (isDebugEnabled) {
            LOGGER.debug("send request [" + packet.getChid() + "],time is:" + System.currentTimeMillis());
        }
        ArrayBlockingQueue<Object> queue = new ArrayBlockingQueue<Object>(1);
        responses.put(packet.getChid(), queue);
        ByteBuffer bb = packet.getByteBuffer();
        bb.flip();
        byte[] data = new byte[bb.remaining()];
        bb.get(data);
        WriteFuture writeFuture = session.write(data);
        writeFuture.addListener(new IoFutureListener() {

            public void operationComplete(IoFuture future) {
                WriteFuture wfuture = (WriteFuture) future;
                if (wfuture.isWritten()) {
                    return;
                }
                String error = "send message to tair server error [" + packet.getChid() + "], tair server: "
                        + session.getRemoteAddress() + ", maybe because this connection closed :"
                        + !session.isConnected();
                LOGGER.warn(error);
                TairResponse response = new TairResponse();
                response.setRequestId(packet.getChid());
                response.setResponse(new TairClientException(error));
                try {
                    putResponse(packet.getChid(), response.getResponse());
                } catch (TairClientException e) {
                    // IGNORE,should not happen
                }
                // close this session
                if (session.isConnected())
                    session.close();
                else
                    clientFactory.removeClient(key);
            }

        });
        Object response = null;
        try {
            response = queue.poll(timeout, TimeUnit.MILLISECONDS);
            if (response == null) {
                throw new TairClientException("tair client invoke timeout,timeout is: " + timeout
                        + ",requestId is: " + packet.getChid() + "request type:" + packet.getClass().getName());
            } else if (response instanceof TairClientException) {
                throw (TairClientException) response;
            }
        } catch (InterruptedException e) {
            throw new TairClientException("tair client invoke error", e);
        } finally {
            responses.remove(packet.getChid());
            // For GC
            queue = null;
        }
        if (isDebugEnabled) {
            LOGGER.debug("return response [" + packet.getChid() + "],time is:" + System.currentTimeMillis());
            LOGGER.debug("current responses size: " + responses.size());
        }

        // do decode here
        if (response instanceof BasePacket) {
            ((BasePacket) response).decode();
        }
        return response;
    }

    public void invokeAsync(final BasePacket packet, final long timeout, ResponseListener listener) {
        if (isDebugEnabled) {
            LOGGER.debug("send request [" + packet.getChid() + "] async,time is:" + System.currentTimeMillis());
        }
        if (minTimeout > timeout) {
            minTimeout = timeout;
        }
        final ResponseCallbackTask callbackTask = new ResponseCallbackTask(packet.getChid(), listener, this.session,
                timeout);
        callbackTasks.put(packet.getChid(), callbackTask);

        ByteBuffer bb = packet.getByteBuffer();
        bb.flip();
        byte[] data = new byte[bb.remaining()];
        bb.get(data);
        WriteFuture writeFuture = session.write(data);
        writeFuture.addListener(new IoFutureListener() {

            public void operationComplete(IoFuture future) {
                WriteFuture wfuture = (WriteFuture) future;
                if (wfuture.isWritten()) {
                    return;
                }
                String error = "send message to tair server error [" + packet.getChid() + "], tair server: "
                        + session.getRemoteAddress() + ", maybe because this connection closed :"
                        + !session.isConnected();
                LOGGER.warn(error);
                callbackTask.setResponse(new TairClientException(error));

                // close this session
                if (session.isConnected())
                    session.close();
                else
                    clientFactory.removeClient(key);
            }

        });
    }

    protected void putResponse(Integer requestId, Object response) throws TairClientException {
        if (responses.containsKey(requestId)) {
            try {
                ArrayBlockingQueue<Object> queue = responses.get(requestId);
                if (queue != null) {
                    queue.put(response);
                    if (isDebugEnabled) {
                        LOGGER.debug("put response [" + requestId + "],time is:" + System.currentTimeMillis());
                    }
                } else if (isDebugEnabled) {
                    LOGGER.debug("give up the response,maybe because timeout,requestId is:" + requestId);
                }

            } catch (InterruptedException e) {
                throw new TairClientException("put response error", e);
            }
        } else {
            if (isDebugEnabled)
                LOGGER.debug("give up the response,maybe because timeout,requestId is:" + requestId);
        }
    }

    protected boolean isCallbackTask(Integer requestId) {
        return callbackTasks.containsKey(requestId);
    }

    protected void putCallbackResponse(Integer requestId, Object response) throws TairClientException {
        ResponseCallbackTask task = callbackTasks.get(requestId);
        if (task == null)
            return;
        task.setResponse(response);
    }

    static class CallbackTasksScan implements Runnable {

        static final long DEFAULT_SLEEPTIME = 10L;

        boolean isRunning = true;

        final TairClientException timeoutException = new TairClientException("receive response timeout");

        public void run() {
            while (isRunning) {
                List<Integer> removeIds = new ArrayList<Integer>();
                for (Entry<Integer, ResponseCallbackTask> entry : callbackTasks.entrySet()) {
                    long currentTime = System.currentTimeMillis();
                    ResponseCallbackTask task = entry.getValue();
                    if ((task.getIsDone().get())) {
                        removeIds.add(task.getRequestId());
                    } else if (task.getTimeout() < currentTime) {
                        removeIds.add(task.getRequestId());
                        task.setResponse(timeoutException);
                    }
                }
                for (Integer removeId : removeIds) {
                    callbackTasks.remove(removeId);
                }
                long sleepTime = DEFAULT_SLEEPTIME;
                if (callbackTasks.size() == 0) {
                    sleepTime = minTimeout;
                }
                try {
                    Thread.sleep(sleepTime);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    public String toString() {
        if (this.session != null)
            return this.session.toString();
        return "null session client";
    }

    public void close() {
        if (session != null) {
            session.close();
        }
        if (responses != null) {
            responses.clear();
        }
    }

}