Java tutorial
/** * (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(); } } }