Package net.rubyeye.xmemcached

Source Code of net.rubyeye.xmemcached.XMemcachedClient

/**
*Copyright [2009-2010] [dennis zhuang(killme2008@gmail.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 net.rubyeye.xmemcached;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;

import net.rubyeye.xmemcached.auth.AuthInfo;
import net.rubyeye.xmemcached.buffer.BufferAllocator;
import net.rubyeye.xmemcached.buffer.SimpleBufferAllocator;
import net.rubyeye.xmemcached.codec.MemcachedCodecFactory;
import net.rubyeye.xmemcached.command.Command;
import net.rubyeye.xmemcached.command.CommandType;
import net.rubyeye.xmemcached.command.ServerAddressAware;
import net.rubyeye.xmemcached.command.TextCommandFactory;
import net.rubyeye.xmemcached.command.binary.BinaryGetMultiCommand;
import net.rubyeye.xmemcached.exception.MemcachedException;
import net.rubyeye.xmemcached.impl.ArrayMemcachedSessionLocator;
import net.rubyeye.xmemcached.impl.ClosedMemcachedTCPSession;
import net.rubyeye.xmemcached.impl.KeyIteratorImpl;
import net.rubyeye.xmemcached.impl.MemcachedClientStateListenerAdapter;
import net.rubyeye.xmemcached.impl.MemcachedConnector;
import net.rubyeye.xmemcached.impl.MemcachedHandler;
import net.rubyeye.xmemcached.impl.MemcachedTCPSession;
import net.rubyeye.xmemcached.impl.ReconnectRequest;
import net.rubyeye.xmemcached.monitor.Constants;
import net.rubyeye.xmemcached.monitor.MemcachedClientNameHolder;
import net.rubyeye.xmemcached.monitor.XMemcachedMbeanServer;
import net.rubyeye.xmemcached.networking.Connector;
import net.rubyeye.xmemcached.networking.MemcachedSession;
import net.rubyeye.xmemcached.transcoders.CachedData;
import net.rubyeye.xmemcached.transcoders.SerializingTranscoder;
import net.rubyeye.xmemcached.transcoders.Transcoder;
import net.rubyeye.xmemcached.utils.AddrUtil;
import net.rubyeye.xmemcached.utils.ByteUtils;
import net.rubyeye.xmemcached.utils.InetSocketAddressWrapper;
import net.rubyeye.xmemcached.utils.Protocol;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.code.yanf4j.buffer.IoBuffer;
import com.google.code.yanf4j.config.Configuration;
import com.google.code.yanf4j.core.Session;
import com.google.code.yanf4j.core.SocketOption;
import com.google.code.yanf4j.util.SystemUtils;

/**
* Memcached Client for connecting to memcached server and do operations.
*
* @author dennis(killme2008@gmail.com)
*
*/
public class XMemcachedClient implements XMemcachedClientMBean, MemcachedClient {

  private static final Logger log = LoggerFactory
      .getLogger(XMemcachedClient.class);
  protected MemcachedSessionLocator sessionLocator;
  private volatile boolean shutdown;
  protected MemcachedConnector connector;
  @SuppressWarnings("unchecked")
  private Transcoder transcoder;
  private boolean sanitizeKeys;
  private MemcachedHandler memcachedHandler;
  protected CommandFactory commandFactory;
  private long opTimeout = DEFAULT_OP_TIMEOUT;
  private long connectTimeout = DEFAULT_CONNECT_TIMEOUT; // 连接超时
  protected int connectionPoolSize = DEFAULT_CONNECTION_POOL_SIZE;

  protected final AtomicInteger serverOrderCount = new AtomicInteger();

  private Map<InetSocketAddress, AuthInfo> authInfoMap = new HashMap<InetSocketAddress, AuthInfo>();

  private String name; // cache name

  private volatile boolean failureMode;

  private final CopyOnWriteArrayList<MemcachedClientStateListenerAdapter> stateListenerAdapters = new CopyOnWriteArrayList<MemcachedClientStateListenerAdapter>();
  private Thread shutdownHookThread;
  private volatile boolean isHutdownHookCalled = false;

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#setMergeFactor(int)
   */
  public final void setMergeFactor(final int mergeFactor) {
    if (mergeFactor < 0) {
      throw new IllegalArgumentException("mergeFactor<0");
    }
    this.connector.setMergeFactor(mergeFactor);
  }

  public final MemcachedSessionLocator getSessionLocator() {
    return this.sessionLocator;
  }

  public final CommandFactory getCommandFactory() {
    return this.commandFactory;
  }

  public String getName() {
    return this.name;
  }

  public void setName(String name) {
    this.name = name;
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#getConnectTimeout()
   */
  public long getConnectTimeout() {
    return this.connectTimeout;
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#setConnectTimeout(long)
   */
  public void setConnectTimeout(long connectTimeout) {
    if (connectTimeout < 0) {
      throw new IllegalArgumentException("connectTimeout<0");
    }
    this.connectTimeout = connectTimeout;
  }

  public void setEnableHeartBeat(boolean enableHeartBeat) {
    this.memcachedHandler.setEnableHeartBeat(enableHeartBeat);
  }

  /**
   * get operation timeout setting
   *
   * @return
   */
  public final long getOpTimeout() {
    return this.opTimeout;
  }

  /**
   * set operation timeout,default is one second.
   *
   * @param opTimeout
   */
  public final void setOpTimeout(long opTimeout) {
    if (opTimeout < 0) {
      throw new IllegalArgumentException("opTimeout<0");
    }
    this.opTimeout = opTimeout;
  }

  public void setHealSessionInterval(long healConnectionInterval) {
    if (null != this.connector) {
      this.connector.setHealSessionInterval(healConnectionInterval);
    }

  }

  public long getHealSessionInterval() {
    if (null != this.connector) {
      return this.connector.getHealSessionInterval();
    }
    return -1L;
  }

  public Map<InetSocketAddress, AuthInfo> getAuthInfoMap() {
    return this.authInfoMap;
  }

  public void setAuthInfoMap(Map<InetSocketAddress, AuthInfo> map) {
    this.authInfoMap = map;
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#getConnector()
   */
  public final Connector getConnector() {
    return this.connector;
  }

  /*
   * (non-Javadoc)
   *
   * @see
   * net.rubyeye.xmemcached.MemcachedClient#setOptimizeMergeBuffer(boolean)
   */
  public final void setOptimizeMergeBuffer(final boolean optimizeMergeBuffer) {
    this.connector.setOptimizeMergeBuffer(optimizeMergeBuffer);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#isShutdown()
   */
  public final boolean isShutdown() {
    return this.shutdown;
  }

  @SuppressWarnings("unchecked")
  private final <T> GetsResponse<T> gets0(final String key,
      final byte[] keyBytes, final Transcoder<T> transcoder)
      throws MemcachedException, TimeoutException, InterruptedException {
    GetsResponse<T> result = (GetsResponse<T>) this.fetch0(key, keyBytes,
        CommandType.GETS_ONE, this.opTimeout, transcoder);
    return result;
  }

  private final void sendCommand(final Command cmd) throws MemcachedException {
    if (this.shutdown) {
      throw new MemcachedException("Xmemcached is stopped");
    }
    this.connector.send(cmd);
  }

  /**
   * XMemcached constructor,default weight is 1
   *
   * @param server
   *            服务器IP
   * @param port
   *            服务器端口
   * @throws IOException
   */
  public XMemcachedClient(final String server, final int port)
      throws IOException {
    this(server, port, 1);
  }

  /**
   * XMemcached constructor
   *
   * @param host
   *            server host
   * @param port
   *            server port
   * @param weight
   *            server weight
   * @throws IOException
   */
  public XMemcachedClient(final String host, final int port, int weight)
      throws IOException {
    super();
    if (weight <= 0) {
      throw new IllegalArgumentException("weight<=0");
    }
    this.checkServerPort(host, port);
    this.buildConnector(new ArrayMemcachedSessionLocator(),
        new SimpleBufferAllocator(), XMemcachedClientBuilder
            .getDefaultConfiguration(), XMemcachedClientBuilder
            .getDefaultSocketOptions(), new TextCommandFactory(),
        new SerializingTranscoder());
    this.start0();
    this.connect(new InetSocketAddressWrapper(this.newSocketAddress(host,
        port), this.serverOrderCount.incrementAndGet(), weight, null));
  }

  protected InetSocketAddress newSocketAddress(final String server,
      final int port) {
    return new InetSocketAddress(server, port);
  }

  private void checkServerPort(String server, int port) {
    if (server == null || server.length() == 0) {
      throw new IllegalArgumentException();
    }
    if (port <= 0) {
      throw new IllegalArgumentException();
    }
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#addServer(java.lang.String,
   * int)
   */
  public final void addServer(final String server, final int port)
      throws IOException {
    this.addServer(server, port, 1);
  }

  /**
   * add a memcached server to MemcachedClient
   *
   * @param server
   * @param port
   * @param weight
   * @throws IOException
   */
  public final void addServer(final String server, final int port, int weight)
      throws IOException {
    if (weight <= 0) {
      throw new IllegalArgumentException("weight<=0");
    }
    this.checkServerPort(server, port);
    this.connect(new InetSocketAddressWrapper(this.newSocketAddress(server,
        port), this.serverOrderCount.incrementAndGet(), weight, null));
  }

  /*
   * (non-Javadoc)
   *
   * @see
   * net.rubyeye.xmemcached.MemcachedClient#addServer(java.net.InetSocketAddress
   * )
   */
  public final void addServer(final InetSocketAddress inetSocketAddress)
      throws IOException {
    this.addServer(inetSocketAddress, 1);
  }

  public final void addServer(final InetSocketAddress inetSocketAddress,
      int weight) throws IOException {
    if (inetSocketAddress == null) {
      throw new IllegalArgumentException("Null InetSocketAddress");
    }
    if (weight <= 0) {
      throw new IllegalArgumentException("weight<=0");
    }
    this.connect(new InetSocketAddressWrapper(inetSocketAddress,
        this.serverOrderCount.incrementAndGet(), weight, null));
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#addServer(java.lang.String)
   */
  public final void addServer(String hostList) throws IOException {
    Map<InetSocketAddress, InetSocketAddress> addresses = AddrUtil
        .getAddressMap(hostList);
    if (addresses != null && addresses.size() > 0) {
      for (Map.Entry<InetSocketAddress, InetSocketAddress> entry : addresses
          .entrySet()) {
        final InetSocketAddress mainNodeAddr = entry.getKey();
        final InetSocketAddress standbyNodeAddr = entry.getValue();
        this.connect(new InetSocketAddressWrapper(mainNodeAddr,
            this.serverOrderCount.incrementAndGet(), 1, null));
        if (standbyNodeAddr != null) {
          this.connect(new InetSocketAddressWrapper(standbyNodeAddr,
              this.serverOrderCount.incrementAndGet(), 1,
              mainNodeAddr));
        }
      }
    }
  }

  public void addOneServerWithWeight(String server, int weight)
      throws IOException {
    Map<InetSocketAddress, InetSocketAddress> addresses = AddrUtil
        .getAddressMap(server);
    if (addresses == null) {
      throw new IllegalArgumentException("Null Server");
    }
    if (addresses.size() != 1) {
      throw new IllegalArgumentException(
          "Please add one server at one time");
    }
    if (weight <= 0) {
      throw new IllegalArgumentException("weight<=0");
    }
    if (addresses != null && addresses.size() > 0) {
      for (Map.Entry<InetSocketAddress, InetSocketAddress> entry : addresses
          .entrySet()) {
        final InetSocketAddress mainNodeAddr = entry.getKey();
        final InetSocketAddress standbyNodeAddr = entry.getValue();
        this.connect(new InetSocketAddressWrapper(mainNodeAddr,
            this.serverOrderCount.incrementAndGet(), 1, null));
        if (standbyNodeAddr != null) {
          this.connect(new InetSocketAddressWrapper(standbyNodeAddr,
              this.serverOrderCount.incrementAndGet(), 1,
              mainNodeAddr));
        }
      }
    }

  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#getServersDescription()
   */
  public final List<String> getServersDescription() {
    final List<String> result = new ArrayList<String>();
    for (Session session : this.connector.getSessionSet()) {
      InetSocketAddress socketAddress = session.getRemoteSocketAddress();
      int weight = ((MemcachedSession) session)
          .getInetSocketAddressWrapper().getWeight();
      result.add(SystemUtils.getRawAddress(socketAddress) + ":"
          + socketAddress.getPort() + "(weight=" + weight + ")");
    }
    return result;
  }

  public final void setServerWeight(String server, int weight) {
    InetSocketAddress socketAddress = AddrUtil.getOneAddress(server);
    Queue<Session> sessionQueue = this.connector
        .getSessionByAddress(socketAddress);
    if (sessionQueue == null) {
      throw new IllegalArgumentException("There is no server " + server);
    }
    for (Session session : sessionQueue) {
      if (session != null) {
        ((MemcachedTCPSession) session).getInetSocketAddressWrapper()
            .setWeight(weight);
      }
    }
    this.connector.updateSessions();
  }

  /*
   * (non-Javadoc)
   *
   * @see
   * net.rubyeye.xmemcached.MemcachedClient#removeServer(java.lang.String)
   */
  public final void removeServer(String hostList) {
    List<InetSocketAddress> addresses = AddrUtil.getAddresses(hostList);
    if (addresses != null && addresses.size() > 0) {
      for (InetSocketAddress address : addresses) {
        // Close main sessions
        Queue<Session> sessionQueue = this.connector
            .getSessionByAddress(address);
        if (sessionQueue != null) {
          for (Session session : sessionQueue) {
            if (session != null) {
              // Disable auto reconnection
              ((MemcachedSession) session)
                  .setAllowReconnect(false);
              // Close connection
              ((MemcachedSession) session).quit();
            }
          }
        }
        // Close standby sessions
        List<Session> standBySession = this.connector
            .getStandbySessionListByMainNodeAddr(address);
        if (standBySession != null) {
          for (Session session : standBySession) {
            this.connector.removeReconnectRequest(session
                .getRemoteSocketAddress());
            if (session != null) {
              // Disable auto reconnection
              ((MemcachedSession) session)
                  .setAllowReconnect(false);
              // Close connection
              ((MemcachedSession) session).quit();
            }
          }
        }
        this.connector.removeReconnectRequest(address);
      }

    }

  }

  protected void checkSocketAddress(InetSocketAddress address) {

  }

  private void connect(final InetSocketAddressWrapper inetSocketAddressWrapper)
      throws IOException {
    // creat connection pool
    InetSocketAddress inetSocketAddress = inetSocketAddressWrapper
        .getInetSocketAddress();
    this.checkSocketAddress(inetSocketAddress);
    for (int i = 0; i < this.connectionPoolSize; i++) {
      Future<Boolean> future = null;
      boolean connected = false;
      Throwable throwable = null;
      try {
        future = this.connector.connect(inetSocketAddressWrapper);

        if (!future.get(this.connectTimeout, TimeUnit.MILLISECONDS)) {
          log.error("connect to "
              + SystemUtils.getRawAddress(inetSocketAddress)
              + ":" + inetSocketAddress.getPort() + " fail");
        } else {
          connected = true;
        }
      } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
      } catch (ExecutionException e) {
        if (future != null) {
          future.cancel(true);
        }
        throwable = e;
        log.error("connect to "
            + SystemUtils.getRawAddress(inetSocketAddress) + ":"
            + inetSocketAddress.getPort() + " error", e);
      } catch (TimeoutException e) {
        if (future != null) {
          future.cancel(true);
        }
        throwable = e;
        log.error("connect to "
            + SystemUtils.getRawAddress(inetSocketAddress) + ":"
            + inetSocketAddress.getPort() + " timeout", e);
      } catch (Exception e) {
        if (future != null) {
          future.cancel(true);
        }
        throwable = e;
        log.error("connect to "
            + SystemUtils.getRawAddress(inetSocketAddress) + ":"
            + inetSocketAddress.getPort() + " error", e);
      }
      // If it is not connected,it will be added to waiting queue for
      // reconnecting.
      if (!connected) {
        // If we use failure mode, add a mock session at first
        if (this.failureMode) {
          this.connector.addSession(new ClosedMemcachedTCPSession(
              inetSocketAddressWrapper));
        }
        this.connector.addToWatingQueue(new ReconnectRequest(
            inetSocketAddressWrapper, 0, this
                .getHealSessionInterval()));
        log.error("Connect to "
            + SystemUtils.getRawAddress(inetSocketAddress) + ":"
            + inetSocketAddress.getPort() + " fail", throwable);
        // throw new IOException(throwable);
      }
    }
  }

  @SuppressWarnings("unchecked")
  private final <T> Object fetch0(final String key, final byte[] keyBytes,
      final CommandType cmdType, final long timeout,
      Transcoder<T> transcoder) throws InterruptedException,
      TimeoutException, MemcachedException, MemcachedException {
    final Command command = this.commandFactory.createGetCommand(key,
        keyBytes, cmdType, this.transcoder);
    this.sendCommand(command);
    this.latchWait(command, timeout);
    command.getIoBuffer().free(); // free buffer
    this.checkException(command);
    CachedData data = (CachedData) command.getResult();
    if (data == null) {
      return null;
    }
    if (transcoder == null) {
      transcoder = this.transcoder;
    }
    if (cmdType == CommandType.GETS_ONE) {
      return new GetsResponse<T>(data.getCas(), transcoder.decode(data));
    } else {
      return transcoder.decode(data);
    }
  }

  private final void start0() throws IOException {
    this.registerMBean();
    this.startConnector();
    MemcachedClientNameHolder.clear();
  }

  private final void startConnector() throws IOException {
    if (this.shutdown) {
      this.shutdown = false;
      this.connector.start();
      this.memcachedHandler.start();
      shutdownHookThread = new Thread() {
        @Override
        public void run() {
          try {
            isHutdownHookCalled = true;
            XMemcachedClient.this.shutdown();
          } catch (IOException e) {
            log.error("Shutdown XMemcachedClient error", e);
          }
        }
      };
      Runtime.getRuntime().addShutdownHook(shutdownHookThread);
    }
  }

  @SuppressWarnings("unchecked")
  private void buildConnector(MemcachedSessionLocator locator,
      BufferAllocator bufferAllocator, Configuration configuration,
      Map<SocketOption, Object> socketOptions,
      CommandFactory commandFactory, Transcoder transcoder) {
    if (locator == null) {
      locator = new ArrayMemcachedSessionLocator();

    }
    if (bufferAllocator == null) {
      bufferAllocator = new SimpleBufferAllocator();
    }
    if (configuration == null) {
      configuration = XMemcachedClientBuilder.getDefaultConfiguration();
    }
    if (transcoder == null) {
      transcoder = new SerializingTranscoder();
    }
    if (commandFactory == null) {
      commandFactory = new TextCommandFactory();
    }
    if (this.name == null) {
      this.name = "MemcachedClient-"
          + Constants.MEMCACHED_CLIENT_COUNTER.getAndIncrement();
      MemcachedClientNameHolder.setName(this.name);
    }
    this.commandFactory = commandFactory;
    ByteUtils.setProtocol(this.commandFactory.getProtocol());
    log.warn("XMemcachedClient use "
        + this.commandFactory.getProtocol().name() + " protocol");
    this.commandFactory.setBufferAllocator(bufferAllocator);
    this.shutdown = true;
    this.transcoder = transcoder;
    this.sessionLocator = locator;
    this.connector = this.newConnector(bufferAllocator, configuration,
        this.sessionLocator, this.commandFactory,
        this.connectionPoolSize);
    this.memcachedHandler = new MemcachedHandler(this);
    this.connector.setHandler(this.memcachedHandler);
    this.connector.setCodecFactory(new MemcachedCodecFactory());
    this.connector.setSessionTimeout(-1);
    this.connector.setSocketOptions(socketOptions);
    if (this.isFailureMode()) {
      log.warn("XMemcachedClient in failure mode.");
    }
    this.connector.setFailureMode(this.failureMode);
    this.sessionLocator.setFailureMode(this.failureMode);
  }

  protected MemcachedConnector newConnector(BufferAllocator bufferAllocator,
      Configuration configuration,
      MemcachedSessionLocator memcachedSessionLocator,
      CommandFactory commandFactory, int i) {
    // make sure dispatch message thread count is zero
    configuration.setDispatchMessageThreadCount(0);
    return new MemcachedConnector(configuration, memcachedSessionLocator,
        bufferAllocator, commandFactory, i);
  }

  private final void registerMBean() {
    if (this.shutdown) {
      XMemcachedMbeanServer.getInstance().registMBean(
          this,
          this.getClass().getPackage().getName() + ":type="
              + this.getClass().getSimpleName() + "-"
              + MemcachedClientNameHolder.getName());
    }
  }

  public void setOptimizeGet(boolean optimizeGet) {
    this.connector.setOptimizeGet(optimizeGet);
  }

  /*
   * (non-Javadoc)
   *
   * @see
   * net.rubyeye.xmemcached.MemcachedClient#setBufferAllocator(net.rubyeye
   * .xmemcached.buffer.BufferAllocator)
   */
  public final void setBufferAllocator(final BufferAllocator bufferAllocator) {
    this.connector.setBufferAllocator(bufferAllocator);
  }

  /**
   * XMemcached Constructor.
   *
   * @param inetSocketAddress
   * @param weight
   * @throws IOException
   */
  public XMemcachedClient(final InetSocketAddress inetSocketAddress,
      int weight) throws IOException {
    super();
    if (inetSocketAddress == null) {
      throw new IllegalArgumentException("Null InetSocketAddress");

    }
    if (weight <= 0) {
      throw new IllegalArgumentException("weight<=0");
    }
    this.buildConnector(new ArrayMemcachedSessionLocator(),
        new SimpleBufferAllocator(), XMemcachedClientBuilder
            .getDefaultConfiguration(), XMemcachedClientBuilder
            .getDefaultSocketOptions(), new TextCommandFactory(),
        new SerializingTranscoder());
    this.start0();
    this.connect(new InetSocketAddressWrapper(inetSocketAddress,
        this.serverOrderCount.incrementAndGet(), weight, null));
  }

  public XMemcachedClient(final InetSocketAddress inetSocketAddress)
      throws IOException {
    this(inetSocketAddress, 1);
  }

  public XMemcachedClient() throws IOException {
    super();
    this.buildConnector(new ArrayMemcachedSessionLocator(),
        new SimpleBufferAllocator(), XMemcachedClientBuilder
            .getDefaultConfiguration(), XMemcachedClientBuilder
            .getDefaultSocketOptions(), new TextCommandFactory(),
        new SerializingTranscoder());
    this.start0();
  }

  /**
   * XMemcachedClient constructor.Every server's weight is one by default.
   *
   * @param locator
   * @param allocator
   * @param conf
   * @param commandFactory
   * @param transcoder
   * @param addressList
   * @param stateListeners
   * @throws IOException
   */
  @SuppressWarnings("unchecked")
  XMemcachedClient(MemcachedSessionLocator locator,
      BufferAllocator allocator, Configuration conf,
      Map<SocketOption, Object> socketOptions,
      CommandFactory commandFactory, Transcoder transcoder,
      Map<InetSocketAddress, InetSocketAddress> addressMap,
      List<MemcachedClientStateListener> stateListeners,
      Map<InetSocketAddress, AuthInfo> map, int poolSize, String name,
      boolean failureMode) throws IOException {
    super();
    this.setFailureMode(failureMode);
    this.setName(name);
    this.optimiezeSetReadThreadCount(conf, addressMap == null ? 0
        : addressMap.size());
    this.buildConnector(locator, allocator, conf, socketOptions,
        commandFactory, transcoder);
    if (stateListeners != null) {
      for (MemcachedClientStateListener stateListener : stateListeners) {
        this.addStateListener(stateListener);
      }
    }
    this.setAuthInfoMap(map);
    this.setConnectionPoolSize(poolSize);
    this.start0();
    if (addressMap != null) {
      for (Map.Entry<InetSocketAddress, InetSocketAddress> entry : addressMap
          .entrySet()) {
        final InetSocketAddress mainNodeAddr = entry.getKey();
        final InetSocketAddress standbyNodeAddr = entry.getValue();
        this.connect(new InetSocketAddressWrapper(mainNodeAddr,
            this.serverOrderCount.incrementAndGet(), 1, null));
        if (standbyNodeAddr != null) {
          this.connect(new InetSocketAddressWrapper(standbyNodeAddr,
              this.serverOrderCount.incrementAndGet(), 1,
              mainNodeAddr));
        }
      }
    }
  }

  /**
   * XMemcachedClient constructor.
   *
   * @param locator
   * @param allocator
   * @param conf
   * @param commandFactory
   * @param transcoder
   * @param addressList
   * @param weights
   * @param stateListeners
   *            weight array for address list
   * @throws IOException
   */
  @SuppressWarnings("unchecked")
  XMemcachedClient(MemcachedSessionLocator locator,
      BufferAllocator allocator, Configuration conf,
      Map<SocketOption, Object> socketOptions,
      CommandFactory commandFactory, Transcoder transcoder,
      Map<InetSocketAddress, InetSocketAddress> addressMap,
      int[] weights, List<MemcachedClientStateListener> stateListeners,
      Map<InetSocketAddress, AuthInfo> infoMap, int poolSize,
      final String name, boolean failureMode) throws IOException {
    super();
    this.setFailureMode(failureMode);
    this.setName(name);
    if (weights == null && addressMap != null) {
      throw new IllegalArgumentException("Null weights");
    }
    if (weights != null && addressMap == null) {
      throw new IllegalArgumentException("Null addressList");
    }

    if (weights != null) {
      for (int weight : weights) {
        if (weight <= 0) {
          throw new IllegalArgumentException("Some weights<=0");
        }
      }
    }
    if (weights != null && addressMap != null
        && weights.length < addressMap.size()) {
      throw new IllegalArgumentException(
          "weights.length is less than addressList.size()");
    }
    this.optimiezeSetReadThreadCount(conf, addressMap == null ? 0
        : addressMap.size());
    this.buildConnector(locator, allocator, conf, socketOptions,
        commandFactory, transcoder);
    if (stateListeners != null) {
      for (MemcachedClientStateListener stateListener : stateListeners) {
        this.addStateListener(stateListener);
      }
    }
    this.setAuthInfoMap(infoMap);
    this.setConnectionPoolSize(poolSize);
    this.start0();
    if (addressMap != null && weights != null) {
      int i = 0;
      for (Map.Entry<InetSocketAddress, InetSocketAddress> entry : addressMap
          .entrySet()) {
        final InetSocketAddress mainNodeAddr = entry.getKey();
        final InetSocketAddress standbyNodeAddr = entry.getValue();
        this.connect(new InetSocketAddressWrapper(mainNodeAddr,
            this.serverOrderCount.incrementAndGet(), weights[i],
            null));
        if (standbyNodeAddr != null) {
          this.connect(new InetSocketAddressWrapper(standbyNodeAddr,
              this.serverOrderCount.incrementAndGet(),
              weights[i], mainNodeAddr));
        }
        i++;
      }
    }
  }

  private final void optimiezeSetReadThreadCount(Configuration conf,
      int addressCount) {
    if (conf != null && addressCount > 1) {
      if (this.isLinuxPlatform()
          && conf.getReadThreadCount() == DEFAULT_READ_THREAD_COUNT) {
        int threadCount = 2 * SystemUtils.getSystemThreadCount();
        conf
            .setReadThreadCount(addressCount > threadCount ? threadCount
                : addressCount);
      }
    }
  }

  private final boolean isLinuxPlatform() {
    String osName = System.getProperty("os.name");
    if (osName != null && osName.toLowerCase().indexOf("linux") >= 0) {
      return true;
    } else {
      return false;
    }
  }

  /**
   * XMemcached Constructor.Every server's weight is one by default.
   *
   * @param addressList
   * @throws IOException
   */
  public XMemcachedClient(List<InetSocketAddress> addressList)
      throws IOException {
    super();
    if (addressList == null || addressList.isEmpty()) {
      throw new IllegalArgumentException("Empty address list");
    }
    BufferAllocator simpleBufferAllocator = new SimpleBufferAllocator();
    this.buildConnector(new ArrayMemcachedSessionLocator(),
        simpleBufferAllocator, XMemcachedClientBuilder
            .getDefaultConfiguration(), XMemcachedClientBuilder
            .getDefaultSocketOptions(), new TextCommandFactory(),
        new SerializingTranscoder());
    this.start0();
    for (InetSocketAddress inetSocketAddress : addressList) {
      this.connect(new InetSocketAddressWrapper(inetSocketAddress,
          this.serverOrderCount.incrementAndGet(), 1, null));

    }
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#get(java.lang.String, long,
   * net.rubyeye.xmemcached.transcoders.Transcoder)
   */
  @SuppressWarnings("unchecked")
  public final <T> T get(final String key, final long timeout,
      final Transcoder<T> transcoder) throws TimeoutException,
      InterruptedException, MemcachedException {
    return (T) this.get0(key, timeout, CommandType.GET_ONE, transcoder);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#get(java.lang.String, long)
   */
  @SuppressWarnings("unchecked")
  public final <T> T get(final String key, final long timeout)
      throws TimeoutException, InterruptedException, MemcachedException {
    return (T) this.get(key, timeout, this.transcoder);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#get(java.lang.String,
   * net.rubyeye.xmemcached.transcoders.Transcoder)
   */
  public final <T> T get(final String key, final Transcoder<T> transcoder)
      throws TimeoutException, InterruptedException, MemcachedException {
    return this.get(key, this.opTimeout, transcoder);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#get(java.lang.String)
   */
  @SuppressWarnings("unchecked")
  public final <T> T get(final String key) throws TimeoutException,
      InterruptedException, MemcachedException {
    return (T) this.get(key, this.opTimeout);
  }

  private <T> Object get0(String key, final long timeout,
      final CommandType cmdType, final Transcoder<T> transcoder)
      throws TimeoutException, InterruptedException, MemcachedException {
    key = this.sanitizeKey(key);
    byte[] keyBytes = ByteUtils.getBytes(key);
    ByteUtils.checkKey(keyBytes);
    return this.fetch0(key, keyBytes, cmdType, timeout, transcoder);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#gets(java.lang.String, long,
   * net.rubyeye.xmemcached.transcoders.Transcoder)
   */
  @SuppressWarnings("unchecked")
  public final <T> GetsResponse<T> gets(final String key, final long timeout,
      final Transcoder<T> transcoder) throws TimeoutException,
      InterruptedException, MemcachedException {
    return (GetsResponse<T>) this.get0(key, timeout, CommandType.GETS_ONE,
        transcoder);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#gets(java.lang.String)
   */
  public final <T> GetsResponse<T> gets(final String key)
      throws TimeoutException, InterruptedException, MemcachedException {
    return this.gets(key, this.opTimeout);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#gets(java.lang.String, long)
   */
  @SuppressWarnings("unchecked")
  public final <T> GetsResponse<T> gets(final String key, final long timeout)
      throws TimeoutException, InterruptedException, MemcachedException {
    return this.gets(key, timeout, this.transcoder);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#gets(java.lang.String,
   * net.rubyeye.xmemcached.transcoders.Transcoder)
   */
  @SuppressWarnings("unchecked")
  public final <T> GetsResponse<T> gets(final String key,
      final Transcoder transcoder) throws TimeoutException,
      InterruptedException, MemcachedException {
    return this.gets(key, this.opTimeout, transcoder);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#get(java.util.Collection,
   * long, net.rubyeye.xmemcached.transcoders.Transcoder)
   */
  public final <T> Map<String, T> get(
      final Collection<String> keyCollections, final long timeout,
      final Transcoder<T> transcoder) throws TimeoutException,
      InterruptedException, MemcachedException {
    return this.getMulti0(keyCollections, timeout, CommandType.GET_MANY,
        transcoder);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#get(java.util.Collection,
   * net.rubyeye.xmemcached.transcoders.Transcoder)
   */
  public final <T> Map<String, T> get(
      final Collection<String> keyCollections,
      final Transcoder<T> transcoder) throws TimeoutException,
      InterruptedException, MemcachedException {
    return this.getMulti0(keyCollections, this.opTimeout,
        CommandType.GET_MANY, transcoder);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#get(java.util.Collection)
   */
  public final <T> Map<String, T> get(final Collection<String> keyCollections)
      throws TimeoutException, InterruptedException, MemcachedException {
    return this.get(keyCollections, this.opTimeout);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#get(java.util.Collection,
   * long)
   */
  @SuppressWarnings("unchecked")
  public final <T> Map<String, T> get(
      final Collection<String> keyCollections, final long timeout)
      throws TimeoutException, InterruptedException, MemcachedException {
    return this.get(keyCollections, timeout, this.transcoder);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#gets(java.util.Collection,
   * long, net.rubyeye.xmemcached.transcoders.Transcoder)
   */
  @SuppressWarnings("unchecked")
  public final <T> Map<String, GetsResponse<T>> gets(
      final Collection<String> keyCollections, final long timeout,
      final Transcoder<T> transcoder) throws TimeoutException,
      InterruptedException, MemcachedException {
    return (Map<String, GetsResponse<T>>) this.getMulti0(keyCollections,
        timeout, CommandType.GETS_MANY, transcoder);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#gets(java.util.Collection)
   */
  public final <T> Map<String, GetsResponse<T>> gets(
      final Collection<String> keyCollections) throws TimeoutException,
      InterruptedException, MemcachedException {
    return this.gets(keyCollections, this.opTimeout);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#gets(java.util.Collection,
   * long)
   */
  @SuppressWarnings("unchecked")
  public final <T> Map<String, GetsResponse<T>> gets(
      final Collection<String> keyCollections, final long timeout)
      throws TimeoutException, InterruptedException, MemcachedException {
    return this.gets(keyCollections, timeout, this.transcoder);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#gets(java.util.Collection,
   * net.rubyeye.xmemcached.transcoders.Transcoder)
   */
  public final <T> Map<String, GetsResponse<T>> gets(
      final Collection<String> keyCollections,
      final Transcoder<T> transcoder) throws TimeoutException,
      InterruptedException, MemcachedException {
    return this.gets(keyCollections, this.opTimeout, transcoder);
  }

  private final <T> Map<String, T> getMulti0(final Collection<String> keys,
      final long timeout, final CommandType cmdType,
      final Transcoder<T> transcoder) throws TimeoutException,
      InterruptedException, MemcachedException {
    if (keys == null || keys.size() == 0) {
      return null;
    }
    Collection<String> keyCollections = keys;
    if (this.sanitizeKeys) {
      keyCollections = new ArrayList<String>(keys.size());
      for (String key : keys) {
        keyCollections.add(this.sanitizeKey(key));
      }
    }
    final CountDownLatch latch;
    final List<Command> commands;
    if (this.connector.getSessionSet().size() <= 1) {
      commands = new ArrayList<Command>(1);
      latch = new CountDownLatch(1);
      commands.add(this.sendGetMultiCommand(keyCollections, latch,
          cmdType, transcoder));

    } else {
      Collection<List<String>> catalogKeys = this
          .catalogKeys(keyCollections);
      commands = new ArrayList<Command>(catalogKeys.size());
      latch = new CountDownLatch(catalogKeys.size());
      for (List<String> catalogKeyCollection : catalogKeys) {
        commands.add(this.sendGetMultiCommand(catalogKeyCollection,
            latch, cmdType, transcoder));
      }
    }
    if (!latch.await(timeout, TimeUnit.MILLISECONDS)) {
      for (Command getCmd : commands) {
        getCmd.cancel();
      }
      throw new TimeoutException("Timed out waiting for operation");
    }
    return this.reduceResult(cmdType, transcoder, commands);
  }

  @SuppressWarnings("unchecked")
  private <T> Map<String, T> reduceResult(final CommandType cmdType,
      final Transcoder<T> transcoder, final List<Command> commands)
      throws MemcachedException {
    final Map<String, T> result = new HashMap<String, T>(commands.size());
    for (Command getCmd : commands) {
      getCmd.getIoBuffer().free();
      this.checkException(getCmd);
      Map<String, CachedData> map = (Map<String, CachedData>) getCmd
          .getResult();
      if (cmdType == CommandType.GET_MANY) {
        Iterator<Map.Entry<String, CachedData>> it = map.entrySet()
            .iterator();
        while (it.hasNext()) {
          Map.Entry<String, CachedData> entry = it.next();
          if (this.sanitizeKeys) {
            result.put(this.decodeKey(entry.getKey()), transcoder
                .decode(entry.getValue()));
          } else {
            result.put(entry.getKey(), transcoder.decode(entry
                .getValue()));
          }
        }

      } else {
        Iterator<Map.Entry<String, CachedData>> it = map.entrySet()
            .iterator();
        while (it.hasNext()) {
          Map.Entry<String, CachedData> entry = it.next();
          GetsResponse getsResponse = new GetsResponse(entry
              .getValue().getCas(), transcoder.decode(entry
              .getValue()));
          result.put(entry.getKey(), (T) getsResponse);
        }

      }

    }
    return result;
  }

  /**
   * Hash key to servers
   *
   * @param keyCollections
   * @return
   */
  private final Collection<List<String>> catalogKeys(
      final Collection<String> keyCollections) {
    final Map<Session, List<String>> catalogMap = new HashMap<Session, List<String>>();

    for (String key : keyCollections) {
      Session index = this.sessionLocator.getSessionByKey(key);
      if (!catalogMap.containsKey(index)) {
        List<String> tmpKeys = new ArrayList<String>(100);
        tmpKeys.add(key);
        catalogMap.put(index, tmpKeys);
      } else {
        catalogMap.get(index).add(key);
      }
    }

    Collection<List<String>> catalogKeys = catalogMap.values();
    return catalogKeys;
  }

  /**
   * Hash key to servers
   *
   * @param keyExpMap
   * @return
   */
  private final Collection<Map<String, Integer>> catalogKeys(
      final Map<String, Integer> keyExpMap) {
    final Map<Session, Map<String, Integer>> catalogMap = new HashMap<Session, Map<String, Integer>>();
    for (Map.Entry<String, Integer> entry : keyExpMap.entrySet()) {
      final String key = entry.getKey();
      final Integer expTime = entry.getValue();
      Session index = this.sessionLocator.getSessionByKey(key);
      if (!catalogMap.containsKey(index)) {
        Map<String, Integer> tmpKeyExpMap = new HashMap<String, Integer>(
            100);
        tmpKeyExpMap.put(key, expTime);
        catalogMap.put(index, tmpKeyExpMap);
      } else {
        catalogMap.get(index).put(key, expTime);
      }
    }
    return catalogMap.values();
  }

  private final <T> Command sendGetMultiCommand(
      final Collection<String> keys, final CountDownLatch latch,
      final CommandType cmdType, final Transcoder<T> transcoder)
      throws InterruptedException, TimeoutException, MemcachedException {
    final Command command = this.commandFactory.createGetMultiCommand(keys,
        latch, cmdType, transcoder);
    this.sendCommand(command);
    return command;
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#set(java.lang.String, int, T,
   * net.rubyeye.xmemcached.transcoders.Transcoder, long)
   */
  public final <T> boolean set(String key, final int exp, final T value,
      final Transcoder<T> transcoder, final long timeout)
      throws TimeoutException, InterruptedException, MemcachedException {
    key = this.sanitizeKey(key);
    byte[] keyBytes = this.checkStoreArguments(key, exp, value);
    return this.sendStoreCommand(this.commandFactory.createSetCommand(key,
        keyBytes, exp, value, false, transcoder), timeout);
  }

  @SuppressWarnings("unchecked")
  public void setWithNoReply(String key, int exp, Object value)
      throws InterruptedException, MemcachedException {
    this.setWithNoReply(key, exp, value, this.transcoder);
  }

  public <T> void setWithNoReply(String key, int exp, T value,
      Transcoder<T> transcoder) throws InterruptedException,
      MemcachedException {
    key = this.sanitizeKey(key);
    byte[] keyBytes = this.checkStoreArguments(key, exp, value);
    try {
      this.sendStoreCommand(this.commandFactory.createSetCommand(key,
          keyBytes, exp, value, true, transcoder), this.opTimeout);
    } catch (TimeoutException e) {
      throw new MemcachedException(e);
    }
  }

  private final <T> byte[] checkStoreArguments(final String key,
      final int exp, final T value) {
    byte[] keyBytes = ByteUtils.getBytes(key);
    ByteUtils.checkKey(keyBytes);
    if (value == null) {
      throw new IllegalArgumentException("value could not be null");
    }
    if (exp < 0) {
      throw new IllegalArgumentException(
          "Expire time must be greater than 0");
    }
    return keyBytes;
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#set(java.lang.String, int,
   * java.lang.Object)
   */
  public final boolean set(final String key, final int exp, final Object value)
      throws TimeoutException, InterruptedException, MemcachedException {
    return this.set(key, exp, value, this.opTimeout);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#set(java.lang.String, int,
   * java.lang.Object, long)
   */
  @SuppressWarnings("unchecked")
  public final boolean set(final String key, final int exp,
      final Object value, final long timeout) throws TimeoutException,
      InterruptedException, MemcachedException {
    return this.set(key, exp, value, this.transcoder, timeout);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#set(java.lang.String, int, T,
   * net.rubyeye.xmemcached.transcoders.Transcoder)
   */
  public final <T> boolean set(final String key, final int exp,
      final T value, final Transcoder<T> transcoder)
      throws TimeoutException, InterruptedException, MemcachedException {
    return this.set(key, exp, value, transcoder, this.opTimeout);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#add(java.lang.String, int, T,
   * net.rubyeye.xmemcached.transcoders.Transcoder, long)
   */
  public final <T> boolean add(String key, final int exp, final T value,
      final Transcoder<T> transcoder, final long timeout)
      throws TimeoutException, InterruptedException, MemcachedException {
    key = this.sanitizeKey(key);
    byte[] keyBytes = this.checkStoreArguments(key, exp, value);
    return this.sendStoreCommand(this.commandFactory.createAddCommand(key,
        keyBytes, exp, value, false, transcoder), timeout);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#add(java.lang.String, int,
   * java.lang.Object)
   */
  public final boolean add(final String key, final int exp, final Object value)
      throws TimeoutException, InterruptedException, MemcachedException {
    return this.add(key, exp, value, this.opTimeout);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#add(java.lang.String, int,
   * java.lang.Object, long)
   */
  @SuppressWarnings("unchecked")
  public final boolean add(final String key, final int exp,
      final Object value, final long timeout) throws TimeoutException,
      InterruptedException, MemcachedException {
    return this.add(key, exp, value, this.transcoder, timeout);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#add(java.lang.String, int, T,
   * net.rubyeye.xmemcached.transcoders.Transcoder)
   */
  public final <T> boolean add(final String key, final int exp,
      final T value, final Transcoder<T> transcoder)
      throws TimeoutException, InterruptedException, MemcachedException {
    return this.add(key, exp, value, transcoder, this.opTimeout);
  }

  @SuppressWarnings("unchecked")
  public void addWithNoReply(String key, int exp, Object value)
      throws InterruptedException, MemcachedException {
    this.addWithNoReply(key, exp, value, this.transcoder);

  }

  public <T> void addWithNoReply(String key, int exp, T value,
      Transcoder<T> transcoder) throws InterruptedException,
      MemcachedException {
    key = this.sanitizeKey(key);
    byte[] keyBytes = this.checkStoreArguments(key, exp, value);
    try {
      this.sendStoreCommand(this.commandFactory.createAddCommand(key,
          keyBytes, exp, value, true, transcoder), this.opTimeout);
    } catch (TimeoutException e) {
      throw new MemcachedException(e);
    }

  }

  @SuppressWarnings("unchecked")
  public void replaceWithNoReply(String key, int exp, Object value)
      throws InterruptedException, MemcachedException {
    this.replaceWithNoReply(key, exp, value, this.transcoder);

  }

  public <T> void replaceWithNoReply(String key, int exp, T value,
      Transcoder<T> transcoder) throws InterruptedException,
      MemcachedException {
    key = this.sanitizeKey(key);
    byte[] keyBytes = this.checkStoreArguments(key, exp, value);
    try {
      this.sendStoreCommand(this.commandFactory.createReplaceCommand(key,
          keyBytes, exp, value, true, transcoder), this.opTimeout);
    } catch (TimeoutException e) {
      throw new MemcachedException(e);
    }

  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#replace(java.lang.String,
   * int, T, net.rubyeye.xmemcached.transcoders.Transcoder, long)
   */
  public final <T> boolean replace(String key, final int exp, final T value,
      final Transcoder<T> transcoder, final long timeout)
      throws TimeoutException, InterruptedException, MemcachedException {
    key = this.sanitizeKey(key);
    byte[] keyBytes = this.checkStoreArguments(key, exp, value);
    return this.sendStoreCommand(this.commandFactory.createReplaceCommand(
        key, keyBytes, exp, value, false, transcoder), timeout);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#replace(java.lang.String,
   * int, java.lang.Object)
   */
  public final boolean replace(final String key, final int exp,
      final Object value) throws TimeoutException, InterruptedException,
      MemcachedException {
    return this.replace(key, exp, value, this.opTimeout);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#replace(java.lang.String,
   * int, java.lang.Object, long)
   */
  @SuppressWarnings("unchecked")
  public final boolean replace(final String key, final int exp,
      final Object value, final long timeout) throws TimeoutException,
      InterruptedException, MemcachedException {
    return this.replace(key, exp, value, this.transcoder, timeout);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#replace(java.lang.String,
   * int, T, net.rubyeye.xmemcached.transcoders.Transcoder)
   */
  public final <T> boolean replace(final String key, final int exp,
      final T value, final Transcoder<T> transcoder)
      throws TimeoutException, InterruptedException, MemcachedException {
    return this.replace(key, exp, value, transcoder, this.opTimeout);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#append(java.lang.String,
   * java.lang.Object)
   */
  public final boolean append(final String key, final Object value)
      throws TimeoutException, InterruptedException, MemcachedException {
    return this.append(key, value, this.opTimeout);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#append(java.lang.String,
   * java.lang.Object, long)
   */
  public final boolean append(String key, final Object value,
      final long timeout) throws TimeoutException, InterruptedException,
      MemcachedException {
    key = this.sanitizeKey(key);
    byte[] keyBytes = this.checkStoreArguments(key, 0, value);
    return this.sendStoreCommand(this.commandFactory.createAppendCommand(
        key, keyBytes, value, false, this.transcoder), timeout);
  }

  public void appendWithNoReply(String key, Object value)
      throws InterruptedException, MemcachedException {
    key = this.sanitizeKey(key);
    byte[] keyBytes = this.checkStoreArguments(key, 0, value);
    try {
      this.sendStoreCommand(this.commandFactory.createAppendCommand(key,
          keyBytes, value, true, this.transcoder), this.opTimeout);
    } catch (TimeoutException e) {
      throw new MemcachedException(e);
    }

  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#prepend(java.lang.String,
   * java.lang.Object)
   */
  public final boolean prepend(final String key, final Object value)
      throws TimeoutException, InterruptedException, MemcachedException {
    return this.prepend(key, value, this.opTimeout);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#prepend(java.lang.String,
   * java.lang.Object, long)
   */
  public final boolean prepend(String key, final Object value,
      final long timeout) throws TimeoutException, InterruptedException,
      MemcachedException {
    key = this.sanitizeKey(key);
    byte[] keyBytes = this.checkStoreArguments(key, 0, value);
    return this.sendStoreCommand(this.commandFactory.createPrependCommand(
        key, keyBytes, value, false, this.transcoder), timeout);
  }

  public void prependWithNoReply(String key, Object value)
      throws InterruptedException, MemcachedException {
    key = this.sanitizeKey(key);
    byte[] keyBytes = this.checkStoreArguments(key, 0, value);
    try {
      this.sendStoreCommand(this.commandFactory.createPrependCommand(key,
          keyBytes, value, true, this.transcoder), this.opTimeout);
    } catch (TimeoutException e) {
      throw new MemcachedException(e);
    }
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#cas(java.lang.String, int,
   * java.lang.Object, long)
   */
  public final boolean cas(final String key, final int exp,
      final Object value, final long cas) throws TimeoutException,
      InterruptedException, MemcachedException {
    return this.cas(key, exp, value, this.opTimeout, cas);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#cas(java.lang.String, int, T,
   * net.rubyeye.xmemcached.transcoders.Transcoder, long, long)
   */
  public final <T> boolean cas(String key, final int exp, final T value,
      final Transcoder<T> transcoder, final long timeout, final long cas)
      throws TimeoutException, InterruptedException, MemcachedException {
    key = this.sanitizeKey(key);
    byte[] keyBytes = this.checkStoreArguments(key, 0, value);
    return this.sendStoreCommand(this.commandFactory.createCASCommand(key,
        keyBytes, exp, value, cas, false, transcoder), timeout);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#cas(java.lang.String, int,
   * java.lang.Object, long, long)
   */
  @SuppressWarnings("unchecked")
  public final boolean cas(final String key, final int exp,
      final Object value, final long timeout, final long cas)
      throws TimeoutException, InterruptedException, MemcachedException {
    return this.cas(key, exp, value, this.transcoder, timeout, cas);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#cas(java.lang.String, int, T,
   * net.rubyeye.xmemcached.transcoders.Transcoder, long)
   */
  public final <T> boolean cas(final String key, final int exp,
      final T value, final Transcoder<T> transcoder, final long cas)
      throws TimeoutException, InterruptedException, MemcachedException {
    return this.cas(key, exp, value, transcoder, this.opTimeout, cas);
  }

  private final <T> boolean cas0(final String key, final int exp,
      GetsResponse<T> getsResponse, final CASOperation<T> operation,
      final Transcoder<T> transcoder, byte[] keyBytes, boolean noreply)
      throws TimeoutException, InterruptedException, MemcachedException {
    if (operation == null) {
      throw new IllegalArgumentException("CASOperation could not be null");
    }
    if (operation.getMaxTries() < 0) {
      throw new IllegalArgumentException(
          "max tries must be greater than 0");
    }
    int tryCount = 0;
    GetsResponse<T> result = getsResponse;
    if (result == null) {
      throw new MemcachedException("Null GetsResponse");
    }
    while (tryCount <= operation.getMaxTries()
        && result != null
        && !this.sendStoreCommand(this.commandFactory.createCASCommand(
            key, keyBytes, exp, operation.getNewValue(result
                .getCas(), result.getValue()), result.getCas(),
            noreply, transcoder), this.opTimeout) && !noreply) {
      tryCount++;
      result = this.gets0(key, keyBytes, transcoder);
      if (result == null) {
        throw new MemcachedException(
            "could not gets the value for Key=" + key + " for cas");
      }
      if (tryCount > operation.getMaxTries()) {
        throw new TimeoutException("CAS try times is greater than max");
      }
    }
    return true;
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#cas(java.lang.String, int,
   * net.rubyeye.xmemcached.CASOperation,
   * net.rubyeye.xmemcached.transcoders.Transcoder)
   */
  public final <T> boolean cas(String key, final int exp,
      final CASOperation<T> operation, final Transcoder<T> transcoder)
      throws TimeoutException, InterruptedException, MemcachedException {
    key = this.sanitizeKey(key);
    byte[] keyBytes = ByteUtils.getBytes(key);
    ByteUtils.checkKey(keyBytes);
    GetsResponse<T> result = this.gets0(key, keyBytes, transcoder);
    return this.cas0(key, exp, result, operation, transcoder, keyBytes,
        false);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#cas(java.lang.String, int,
   * net.rubyeye.xmemcached.GetsResponse, net.rubyeye.xmemcached.CASOperation,
   * net.rubyeye.xmemcached.transcoders.Transcoder)
   */
  public final <T> boolean cas(String key, final int exp,
      GetsResponse<T> getsReponse, final CASOperation<T> operation,
      final Transcoder<T> transcoder) throws TimeoutException,
      InterruptedException, MemcachedException {
    key = this.sanitizeKey(key);
    byte[] keyBytes = ByteUtils.getBytes(key);
    ByteUtils.checkKey(keyBytes);
    return this.cas0(key, exp, getsReponse, operation, transcoder,
        keyBytes, false);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#cas(java.lang.String, int,
   * net.rubyeye.xmemcached.GetsResponse, net.rubyeye.xmemcached.CASOperation)
   */
  @SuppressWarnings("unchecked")
  public final <T> boolean cas(final String key, final int exp,
      GetsResponse<T> getsReponse, final CASOperation<T> operation)
      throws TimeoutException, InterruptedException, MemcachedException {

    return this.cas(key, exp, getsReponse, operation, this.transcoder);
  }

  public <T> void casWithNoReply(String key, CASOperation<T> operation)
      throws TimeoutException, InterruptedException, MemcachedException {
    this.casWithNoReply(key, 0, operation);
  }

  public <T> void casWithNoReply(String key, GetsResponse<T> getsResponse,
      CASOperation<T> operation) throws TimeoutException,
      InterruptedException, MemcachedException {
    this.casWithNoReply(key, 0, getsResponse, operation);

  }

  @SuppressWarnings("unchecked")
  public <T> void casWithNoReply(String key, int exp,
      CASOperation<T> operation) throws TimeoutException,
      InterruptedException, MemcachedException {
    key = this.sanitizeKey(key);
    byte[] keyBytes = ByteUtils.getBytes(key);
    GetsResponse<T> result = this.gets0(key, keyBytes, this.transcoder);
    this.casWithNoReply(key, exp, result, operation);

  }

  @SuppressWarnings("unchecked")
  public <T> void casWithNoReply(String key, int exp,
      GetsResponse<T> getsReponse, CASOperation<T> operation)
      throws TimeoutException, InterruptedException, MemcachedException {
    key = this.sanitizeKey(key);
    byte[] keyBytes = ByteUtils.getBytes(key);
    ByteUtils.checkKey(keyBytes);
    this.cas0(key, exp, getsReponse, operation, this.transcoder, keyBytes,
        true);

  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#cas(java.lang.String,
   * net.rubyeye.xmemcached.GetsResponse, net.rubyeye.xmemcached.CASOperation)
   */
  public final <T> boolean cas(final String key, GetsResponse<T> getsReponse,
      final CASOperation<T> operation) throws TimeoutException,
      InterruptedException, MemcachedException {
    return this.cas(key, 0, getsReponse, operation);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#cas(java.lang.String, int,
   * net.rubyeye.xmemcached.CASOperation)
   */
  @SuppressWarnings("unchecked")
  public final <T> boolean cas(final String key, final int exp,
      final CASOperation<T> operation) throws TimeoutException,
      InterruptedException, MemcachedException {
    return this.cas(key, exp, operation, this.transcoder);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#cas(java.lang.String,
   * net.rubyeye.xmemcached.CASOperation)
   */
  public final <T> boolean cas(final String key,
      final CASOperation<T> operation) throws TimeoutException,
      InterruptedException, MemcachedException {
    return this.cas(key, 0, operation);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#delete(java.lang.String, int)
   */
  public final boolean delete(final String key, final int time)
      throws TimeoutException, InterruptedException, MemcachedException {
    return this.delete0(key, time, false, this.opTimeout);
  }

  public boolean delete(String key, long opTimeout) throws TimeoutException,
      InterruptedException, MemcachedException {
    return this.delete0(key, 0, false, opTimeout);
  }

  /**
   * Delete key's data item from memcached.This method doesn't wait for reply
   *
   * @param key
   * @param time
   * @throws InterruptedException
   * @throws MemcachedException
   */
  public final void deleteWithNoReply(final String key, final int time)
      throws InterruptedException, MemcachedException {
    try {
      this.delete0(key, time, true, this.opTimeout);
    } catch (TimeoutException e) {
      throw new MemcachedException(e);
    }
  }

  public final void deleteWithNoReply(final String key)
      throws InterruptedException, MemcachedException {
    this.deleteWithNoReply(key, 0);
  }

  private boolean delete0(String key, final int time, boolean noreply,
      long opTimeout) throws MemcachedException, InterruptedException,
      TimeoutException {
    key = this.sanitizeKey(key);
    final byte[] keyBytes = ByteUtils.getBytes(key);
    ByteUtils.checkKey(keyBytes);
    final Command command = this.commandFactory.createDeleteCommand(key,
        keyBytes, time, noreply);
    this.sendCommand(command);
    if (!command.isNoreply()) {
      this.latchWait(command, opTimeout);
      command.getIoBuffer().free();
      this.checkException(command);
      if (command.getResult() == null) {
        throw new MemcachedException(
            "Operation fail,may be caused by networking or timeout");
      }
    } else {
      return false;
    }
    return (Boolean) command.getResult();
  }

  void checkException(final Command command) throws MemcachedException {
    if (command.getException() != null) {
      if (command.getException() instanceof MemcachedException)
        throw (MemcachedException) command.getException();
      else
        throw new MemcachedException(command.getException());
    }
  }

  public boolean touch(String key, int exp, long opTimeout)
      throws TimeoutException, InterruptedException, MemcachedException {
    key = this.sanitizeKey(key);
    final byte[] keyBytes = ByteUtils.getBytes(key);
    ByteUtils.checkKey(keyBytes);
    CountDownLatch latch = new CountDownLatch(1);
    final Command command = this.commandFactory.createTouchCommand(key,
        keyBytes, latch, exp, false);
    this.sendCommand(command);
    this.latchWait(command, opTimeout);
    command.getIoBuffer().free();
    this.checkException(command);
    if (command.getResult() == null) {
      throw new MemcachedException(
          "Operation fail,may be caused by networking or timeout");
    }
    return (Boolean) command.getResult();
  }

  public boolean touch(String key, int exp) throws TimeoutException,
      InterruptedException, MemcachedException {
    return this.touch(key, exp, this.opTimeout);
  }

  /**
   * This method still has some problems,i will make it public in the future
   *
   * @param <T>
   * @param keyExpMap
   * @param opTimeout
   * @return
   * @throws TimeoutException
   * @throws InterruptedException
   * @throws MemcachedException
   */
  private <T> Map<String, T> getAndTouch(Map<String, Integer> keyExpMap,
      long opTimeout) throws TimeoutException, InterruptedException,
      MemcachedException {
    if (keyExpMap == null || keyExpMap.isEmpty())
      return null;

    Map<String, Integer> innerKeyExpMap = keyExpMap;
    if (this.sanitizeKeys) {
      innerKeyExpMap = new HashMap<String, Integer>(keyExpMap.size());
      for (Map.Entry<String, Integer> entry : keyExpMap.entrySet()) {
        innerKeyExpMap.put(this.sanitizeKey(entry.getKey()), entry
            .getValue());
      }
    }
    final CountDownLatch latch;
    final List<Command> commands;
    if (this.connector.getSessionSet().size() <= 1) {
      commands = new ArrayList<Command>(1);
      latch = new CountDownLatch(1);
      commands.add(this.sendMultiGATCommand(innerKeyExpMap, latch));

    } else {
      Collection<Map<String, Integer>> catalogKeyExps = this
          .catalogKeys(innerKeyExpMap);
      commands = new ArrayList<Command>(catalogKeyExps.size());
      latch = new CountDownLatch(catalogKeyExps.size());
      for (Map<String, Integer> catalogKeyExpMap : catalogKeyExps) {
        commands.add(this.sendMultiGATCommand(catalogKeyExpMap, latch));
      }
    }
    if (!latch.await(opTimeout, TimeUnit.MILLISECONDS)) {
      for (Command getCmd : commands) {
        getCmd.cancel();
      }
      throw new TimeoutException("Timed out waiting for operation");
    }
    return this.reduceResult(CommandType.GET_MANY, transcoder, commands);

  }

  private Command sendMultiGATCommand(Map<String, Integer> innerKeyExpMap,
      CountDownLatch latch) throws MemcachedException {
    Iterator<String> it = innerKeyExpMap.keySet().iterator();
    String key = null;
    List<com.google.code.yanf4j.buffer.IoBuffer> bufferList = new ArrayList<com.google.code.yanf4j.buffer.IoBuffer>();
    int totalLength = 0;
    while (it.hasNext()) {
      key = it.next();
      if (it.hasNext()) {
        // first n-1 send gatq command
        Command command = this.commandFactory.createGetAndTouchCommand(
            key, ByteUtils.getBytes(key), null, innerKeyExpMap
                .get(key), true);
        command.encode();
        totalLength += command.getIoBuffer().remaining();
        bufferList.add(command.getIoBuffer());
      }
    }
    // last key,create a gat command
    Command lastCommand = this.commandFactory.createGetAndTouchCommand(key,
        ByteUtils.getBytes(key), null, innerKeyExpMap.get(key), false);
    lastCommand.encode();
    bufferList.add(lastCommand.getIoBuffer());
    totalLength += lastCommand.getIoBuffer().remaining();

    IoBuffer mergedBuffer = IoBuffer.allocate(totalLength);
    for (IoBuffer buffer : bufferList) {
      mergedBuffer.put(buffer.buf());
    }
    mergedBuffer.flip();

    Command resultCommand = new BinaryGetMultiCommand(key,
        CommandType.GET_MANY, latch);
    resultCommand.setIoBuffer(mergedBuffer);
    this.sendCommand(resultCommand);
    return resultCommand;
  }

  private <T> Map<String, T> getAndTouch(Map<String, Integer> keyExpMap)
      throws TimeoutException, InterruptedException, MemcachedException {
    return this.getAndTouch(keyExpMap, this.opTimeout);
  }

  @SuppressWarnings("unchecked")
  public <T> T getAndTouch(String key, int newExp, long opTimeout)
      throws TimeoutException, InterruptedException, MemcachedException {
    key = this.sanitizeKey(key);
    final byte[] keyBytes = ByteUtils.getBytes(key);
    ByteUtils.checkKey(keyBytes);
    CountDownLatch latch = new CountDownLatch(1);
    final Command command = this.commandFactory.createGetAndTouchCommand(
        key, keyBytes, latch, newExp, false);
    this.sendCommand(command);
    this.latchWait(command, opTimeout);
    command.getIoBuffer().free();
    this.checkException(command);
    CachedData data = (CachedData) command.getResult();
    if (data == null) {
      return null;
    }
    return (T) transcoder.decode(data);
  }

  @SuppressWarnings("unchecked")
  public <T> T getAndTouch(String key, int newExp) throws TimeoutException,
      InterruptedException, MemcachedException {
    return (T) this.getAndTouch(key, newExp, this.opTimeout);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#incr(java.lang.String, int)
   */
  public final long incr(String key, final long delta)
      throws TimeoutException, InterruptedException, MemcachedException {
    key = this.sanitizeKey(key);
    return this.sendIncrOrDecrCommand(key, delta, 0, CommandType.INCR,
        false, this.opTimeout, 0);
  }

  public long incr(String key, long delta, long initValue)
      throws TimeoutException, InterruptedException, MemcachedException {
    key = this.sanitizeKey(key);
    return this.sendIncrOrDecrCommand(key, delta, initValue,
        CommandType.INCR, false, this.opTimeout, 0);
  }

  public long incr(String key, long delta, long initValue, long timeout)
      throws TimeoutException, InterruptedException, MemcachedException {
    key = this.sanitizeKey(key);
    return this.sendIncrOrDecrCommand(key, delta, initValue,
        CommandType.INCR, false, timeout, 0);
  }

  public long incr(String key, long delta, long initValue, long timeout,
      int exp) throws TimeoutException, InterruptedException,
      MemcachedException {
    key = this.sanitizeKey(key);
    return this.sendIncrOrDecrCommand(key, delta, initValue,
        CommandType.INCR, false, timeout, exp);
  }

  public final void incrWithNoReply(String key, long delta)
      throws InterruptedException, MemcachedException {
    key = this.sanitizeKey(key);
    try {
      this.sendIncrOrDecrCommand(key, delta, 0, CommandType.INCR, true,
          this.opTimeout, 0);
    } catch (TimeoutException e) {
      throw new MemcachedException(e);
    }
  }

  public final void decrWithNoReply(String key, final long delta)
      throws InterruptedException, MemcachedException {
    key = this.sanitizeKey(key);
    try {
      this.sendIncrOrDecrCommand(key, delta, 0, CommandType.DECR, true,
          this.opTimeout, 0);
    } catch (TimeoutException e) {
      throw new MemcachedException(e);
    }
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#decr(java.lang.String, int)
   */
  public final long decr(String key, final long delta)
      throws TimeoutException, InterruptedException, MemcachedException {
    key = this.sanitizeKey(key);
    return this.sendIncrOrDecrCommand(key, delta, 0, CommandType.DECR,
        false, this.opTimeout, 0);
  }

  public long decr(String key, long delta, long initValue)
      throws TimeoutException, InterruptedException, MemcachedException {
    key = this.sanitizeKey(key);
    return this.sendIncrOrDecrCommand(key, delta, initValue,
        CommandType.DECR, false, this.opTimeout, 0);
  }

  public long decr(String key, long delta, long initValue, long timeout)
      throws TimeoutException, InterruptedException, MemcachedException {
    key = this.sanitizeKey(key);
    return this.sendIncrOrDecrCommand(key, delta, initValue,
        CommandType.DECR, false, timeout, 0);
  }

  public long decr(String key, long delta, long initValue, long timeout,
      int exp) throws TimeoutException, InterruptedException,
      MemcachedException {
    key = this.sanitizeKey(key);
    return this.sendIncrOrDecrCommand(key, delta, initValue,
        CommandType.DECR, false, timeout, exp);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#flushAll()
   */
  public final void flushAll() throws TimeoutException, InterruptedException,
      MemcachedException {
    this.flushAll(this.opTimeout);
  }

  public void flushAllWithNoReply() throws InterruptedException,
      MemcachedException {
    try {
      this.flushAllMemcachedServers(this.opTimeout, true, 0);
    } catch (TimeoutException e) {
      throw new MemcachedException(e);
    }
  }

  public void flushAllWithNoReply(int exptime) throws InterruptedException,
      MemcachedException {
    try {
      this.flushAllMemcachedServers(this.opTimeout, true, exptime);
    } catch (TimeoutException e) {
      throw new MemcachedException(e);
    }
  }

  public void flushAllWithNoReply(InetSocketAddress address)
      throws MemcachedException, InterruptedException {
    try {
      this.flushSpecialMemcachedServer(address, this.opTimeout, true, 0);
    } catch (TimeoutException e) {
      throw new MemcachedException(e);
    }
  }

  public void flushAllWithNoReply(InetSocketAddress address, int exptime)
      throws MemcachedException, InterruptedException {
    try {
      this.flushSpecialMemcachedServer(address, this.opTimeout, true,
          exptime);
    } catch (TimeoutException e) {
      throw new MemcachedException(e);
    }
  }

  public final void flushAll(int exptime, long timeout)
      throws TimeoutException, InterruptedException, MemcachedException {
    this.flushAllMemcachedServers(timeout, false, exptime);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#flushAll(long)
   */
  public final void flushAll(long timeout) throws TimeoutException,
      InterruptedException, MemcachedException {
    this.flushAllMemcachedServers(timeout, false, 0);
  }

  private void flushAllMemcachedServers(long timeout, boolean noreply,
      int exptime) throws MemcachedException, InterruptedException,
      TimeoutException {
    final Collection<Session> sessions = this.connector.getSessionSet();
    CountDownLatch latch = new CountDownLatch(sessions.size());
    List<Command> commands = new ArrayList<Command>(sessions.size());
    for (Session session : sessions) {
      if (session != null && !session.isClosed()) {
        Command command = this.commandFactory.createFlushAllCommand(
            latch, exptime, noreply);

        session.write(command);
      } else {
        latch.countDown();
      }
    }
    if (!noreply) {
      if (!latch.await(timeout, TimeUnit.MILLISECONDS)) {
        for (Command cmd : commands) {
          cmd.cancel();
        }
        throw new TimeoutException("Timed out waiting for operation");
      }
    }
  }

  public void setLoggingLevelVerbosity(InetSocketAddress address, int level)
      throws TimeoutException, InterruptedException, MemcachedException {
    this.setMemcachedLoggingLevel(address, level, false);

  }

  private void setMemcachedLoggingLevel(InetSocketAddress address, int level,
      boolean noreply) throws MemcachedException, InterruptedException,
      TimeoutException {
    if (address == null) {
      throw new IllegalArgumentException("Null adderss");
    }
    CountDownLatch latch = new CountDownLatch(1);

    Queue<Session> sessionQueue = this.connector
        .getSessionByAddress(address);
    if (sessionQueue == null || sessionQueue.peek() == null) {
      throw new MemcachedException("could not find session for "
          + SystemUtils.getRawAddress(address) + ":"
          + address.getPort() + ",maybe it have not been connected");
    }

    Command command = this.commandFactory.createVerbosityCommand(latch,
        level, noreply);
    sessionQueue.peek().write(command);
    if (!noreply) {
      this.latchWait(command, this.opTimeout);
    }
  }

  public void setLoggingLevelVerbosityWithNoReply(InetSocketAddress address,
      int level) throws InterruptedException, MemcachedException {
    try {
      this.setMemcachedLoggingLevel(address, level, true);
    } catch (TimeoutException e) {
      throw new MemcachedException(e);
    }

  }

  /*
   * (non-Javadoc)
   *
   * @see
   * net.rubyeye.xmemcached.MemcachedClient#flushAll(java.net.InetSocketAddress
   * )
   */
  public final void flushAll(InetSocketAddress address)
      throws MemcachedException, InterruptedException, TimeoutException {
    this.flushAll(address, this.opTimeout);
  }

  /*
   * (non-Javadoc)
   *
   * @see
   * net.rubyeye.xmemcached.MemcachedClient#flushAll(java.net.InetSocketAddress
   * , long)
   */
  public final void flushAll(InetSocketAddress address, long timeout)
      throws MemcachedException, InterruptedException, TimeoutException {
    this.flushSpecialMemcachedServer(address, timeout, false, 0);
  }

  public final void flushAll(InetSocketAddress address, long timeout,
      int exptime) throws MemcachedException, InterruptedException,
      TimeoutException {
    this.flushSpecialMemcachedServer(address, timeout, false, exptime);
  }

  private void flushSpecialMemcachedServer(InetSocketAddress address,
      long timeout, boolean noreply, int exptime)
      throws MemcachedException, InterruptedException, TimeoutException {
    if (address == null) {
      throw new IllegalArgumentException("Null adderss");
    }
    CountDownLatch latch = new CountDownLatch(1);

    Queue<Session> sessionQueue = this.connector
        .getSessionByAddress(address);
    if (sessionQueue == null || sessionQueue.peek() == null) {
      throw new MemcachedException("could not find session for "
          + SystemUtils.getRawAddress(address) + ":"
          + address.getPort() + ",maybe it have not been connected");
    }
    Command command = this.commandFactory.createFlushAllCommand(latch,
        exptime, noreply);
    sessionQueue.peek().write(command);
    if (!noreply) {
      this.latchWait(command, timeout);
    }
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#flushAll(java.lang.String)
   */
  public final void flushAll(String host) throws TimeoutException,
      InterruptedException, MemcachedException {
    this.flushAll(AddrUtil.getOneAddress(host), this.opTimeout);
  }

  /*
   * (non-Javadoc)
   *
   * @see
   * net.rubyeye.xmemcached.MemcachedClient#stats(java.net.InetSocketAddress)
   */
  public final Map<String, String> stats(InetSocketAddress address)
      throws MemcachedException, InterruptedException, TimeoutException {
    return this.stats(address, this.opTimeout);
  }

  /*
   * (non-Javadoc)
   *
   * @see
   * net.rubyeye.xmemcached.MemcachedClient#stats(java.net.InetSocketAddress,
   * long)
   */
  @SuppressWarnings("unchecked")
  public final Map<String, String> stats(InetSocketAddress address,
      long timeout) throws MemcachedException, InterruptedException,
      TimeoutException {
    if (address == null) {
      throw new IllegalArgumentException("Null inetSocketAddress");
    }
    CountDownLatch latch = new CountDownLatch(1);

    Queue<Session> sessionQueue = this.connector
        .getSessionByAddress(address);
    if (sessionQueue == null || sessionQueue.peek() == null) {
      throw new MemcachedException("could not find session for "
          + SystemUtils.getRawAddress(address) + ":"
          + address.getPort() + ",maybe it have not been connected");
    }
    Command command = this.commandFactory.createStatsCommand(address,
        latch, null);
    sessionQueue.peek().write(command);
    this.latchWait(command, timeout);
    return (Map<String, String>) command.getResult();
  }

  public final Map<InetSocketAddress, Map<String, String>> getStats()
      throws MemcachedException, InterruptedException, TimeoutException {
    return this.getStats(this.opTimeout);
  }

  public final Map<InetSocketAddress, Map<String, String>> getStatsByItem(
      String itemName) throws MemcachedException, InterruptedException,
      TimeoutException {
    return this.getStatsByItem(itemName, this.opTimeout);
  }

  @SuppressWarnings("unchecked")
  public final Map<InetSocketAddress, Map<String, String>> getStatsByItem(
      String itemName, long timeout) throws MemcachedException,
      InterruptedException, TimeoutException {
    final Set<Session> sessionSet = this.connector.getSessionSet();
    final Map<InetSocketAddress, Map<String, String>> collectResult = new HashMap<InetSocketAddress, Map<String, String>>();
    if (sessionSet.size() == 0) {
      return collectResult;
    }
    final CountDownLatch latch = new CountDownLatch(sessionSet.size());
    List<Command> commands = new ArrayList<Command>(sessionSet.size());
    for (Session session : sessionSet) {
      Command command = this.commandFactory.createStatsCommand(session
          .getRemoteSocketAddress(), latch, itemName);

      session.write(command);
      commands.add(command);

    }
    if (!latch.await(timeout, TimeUnit.MILLISECONDS)) {
      for (Command command : commands) {
        command.cancel();
      }
      throw new TimeoutException("Timed out waiting for operation");
    }
    for (Command command : commands) {
      this.checkException(command);
      collectResult.put(((ServerAddressAware) command).getServer(),
          (Map<String, String>) command.getResult());
    }
    return collectResult;
  }

  public final Map<InetSocketAddress, String> getVersions()
      throws TimeoutException, InterruptedException, MemcachedException {
    return this.getVersions(this.opTimeout);
  }

  public final Map<InetSocketAddress, String> getVersions(long timeout)
      throws TimeoutException, InterruptedException, MemcachedException {
    final Set<Session> sessionSet = this.connector.getSessionSet();
    Map<InetSocketAddress, String> collectResult = new HashMap<InetSocketAddress, String>();
    if (sessionSet.size() == 0) {
      return collectResult;
    }
    final CountDownLatch latch = new CountDownLatch(sessionSet.size());
    List<Command> commands = new ArrayList<Command>(sessionSet.size());
    for (Session session : sessionSet) {
      Command command = this.commandFactory.createVersionCommand(latch,
          session.getRemoteSocketAddress());
      session.write(command);
      commands.add(command);

    }

    if (!latch.await(timeout, TimeUnit.MILLISECONDS)) {
      for (Command command : commands) {
        command.cancel();
      }
      throw new TimeoutException("Timed out waiting for operation");
    }
    for (Command command : commands) {
      this.checkException(command);
      collectResult.put(((ServerAddressAware) command).getServer(),
          (String) command.getResult());
    }
    return collectResult;
  }

  public Map<InetSocketAddress, Map<String, String>> getStats(long timeout)
      throws MemcachedException, InterruptedException, TimeoutException {
    return this.getStatsByItem(null, timeout);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#shutdown()
   */
  public final void shutdown() throws IOException {
    if (this.shutdown) {
      return;
    }
    this.shutdown = true;
    this.connector.quitAllSessions();
    this.connector.stop();
    this.memcachedHandler.stop();
    XMemcachedMbeanServer.getInstance().shutdown();
    if (!isHutdownHookCalled) {
      Runtime.getRuntime().removeShutdownHook(shutdownHookThread);
    }
  }

  private long sendIncrOrDecrCommand(final String key, final long delta,
      long initValue, final CommandType cmdType, boolean noreply,
      long operationTimeout, int exp) throws InterruptedException,
      TimeoutException, MemcachedException {
    final byte[] keyBytes = ByteUtils.getBytes(key);
    ByteUtils.checkKey(keyBytes);
    final Command command = this.commandFactory.createIncrDecrCommand(key,
        keyBytes, delta, initValue, exp, cmdType, noreply);
    this.sendCommand(command);
    if (!command.isNoreply()) {
      this.latchWait(command, operationTimeout);
      command.getIoBuffer().free();
      this.checkException(command);
      if (command.getResult() == null) {
        throw new MemcachedException(
            "Operation fail,may be caused by networking or timeout");
      }
      final Object result = command.getResult();
      if (result instanceof String) {
        if (((String) result).equals("NOT_FOUND")) {
          if (this.add(key, exp, String.valueOf(initValue),
              this.opTimeout)) {
            return initValue;
          } else {
            return this.sendIncrOrDecrCommand(key, delta,
                initValue, cmdType, noreply, operationTimeout,
                exp);
          }
        } else {
          throw new MemcachedException(
              "Unknown result type for incr/decr:"
                  + result.getClass() + ",result=" + result);
        }
      } else {
        return (Long) command.getResult();
      }
    } else {
      return -1;
    }
  }

  public void setConnectionPoolSize(int poolSize) {
    if (!this.shutdown && this.getAvaliableServers().size() > 0) {
      throw new IllegalStateException(
          "Xmemcached client has been started");
    }
    if (poolSize <= 0) {
      throw new IllegalArgumentException("poolSize<=0");
    }
    this.connectionPoolSize = poolSize;
    this.connector.setConnectionPoolSize(poolSize);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#delete(java.lang.String)
   */
  public final boolean delete(final String key) throws TimeoutException,
      InterruptedException, MemcachedException {
    return this.delete(key, 0);
  }

  /*
   * (non-Javadoc)
   *
   * @see net.rubyeye.xmemcached.MemcachedClient#getTranscoder()
   */
  @SuppressWarnings("unchecked")
  public final Transcoder getTranscoder() {
    return this.transcoder;
  }

  /*
   * (non-Javadoc)
   *
   * @see
   * net.rubyeye.xmemcached.MemcachedClient#setTranscoder(net.rubyeye.xmemcached
   * .transcoders.Transcoder)
   */
  @SuppressWarnings("unchecked")
  public final void setTranscoder(final Transcoder transcoder) {
    this.transcoder = transcoder;
  }

  private final <T> boolean sendStoreCommand(Command command, long timeout)
      throws InterruptedException, TimeoutException, MemcachedException {

    this.sendCommand(command);
    if (!command.isNoreply()) {
      this.latchWait(command, timeout);
      command.getIoBuffer().free();
      this.checkException(command);
      if (command.getResult() == null) {
        throw new MemcachedException(
            "Operation fail,may be caused by networking or timeout");
      }
    } else {
      return false;
    }
    return (Boolean) command.getResult();
  }

  private void latchWait(final Command cmd, final long timeout)
      throws InterruptedException, TimeoutException {
    if (!cmd.getLatch().await(timeout, TimeUnit.MILLISECONDS)) {
      cmd.cancel();
      throw new TimeoutException("Timed out(" + timeout
          + ") waiting for operation");
    }
  }

  public final Collection<InetSocketAddress> getAvaliableServers() {
    Set<Session> sessionSet = this.connector.getSessionSet();
    Set<InetSocketAddress> result = new HashSet<InetSocketAddress>();
    for (Session session : sessionSet) {
      result.add(session.getRemoteSocketAddress());
    }
    return Collections.unmodifiableSet(result);
  }

  public final int getConnectionSizeBySocketAddress(InetSocketAddress address) {
    Queue<Session> sessionList = this.connector
        .getSessionByAddress(address);
    return sessionList == null ? 0 : sessionList.size();
  }

  public void addStateListener(MemcachedClientStateListener listener) {
    MemcachedClientStateListenerAdapter adapter = new MemcachedClientStateListenerAdapter(
        listener, this);
    this.stateListenerAdapters.add(adapter);
    this.connector.addStateListener(adapter);
  }

  public Collection<MemcachedClientStateListener> getStateListeners() {
    final List<MemcachedClientStateListener> result = new ArrayList<MemcachedClientStateListener>(
        this.stateListenerAdapters.size());
    for (MemcachedClientStateListenerAdapter adapter : this.stateListenerAdapters) {
      result.add(adapter.getMemcachedClientStateListener());
    }
    return result;
  }

  public void setPrimitiveAsString(boolean primitiveAsString) {
    this.transcoder.setPrimitiveAsString(primitiveAsString);
  }

  public void removeStateListener(MemcachedClientStateListener listener) {
    for (MemcachedClientStateListenerAdapter adapter : this.stateListenerAdapters) {
      if (adapter.getMemcachedClientStateListener().equals(listener)) {
        this.stateListenerAdapters.remove(adapter);
        this.connector.removeStateListener(adapter);
      }
    }
  }

  public Protocol getProtocol() {
    return this.commandFactory.getProtocol();
  }

  public boolean isSanitizeKeys() {
    return this.sanitizeKeys;
  }

  public void setSanitizeKeys(boolean sanitizeKeys) {
    this.sanitizeKeys = sanitizeKeys;
  }

  private String decodeKey(String key) throws MemcachedException {
    try {
      return this.sanitizeKeys ? URLDecoder.decode(key, "UTF-8") : key;
    } catch (UnsupportedEncodingException e) {
      throw new MemcachedException(
          "Unsupport encoding utf-8 when decodeKey", e);
    }
  }

  private String sanitizeKey(String key) throws MemcachedException {
    try {
      return this.sanitizeKeys ? URLEncoder.encode(key, "UTF-8") : key;
    } catch (UnsupportedEncodingException e) {
      throw new MemcachedException(
          "Unsupport encoding utf-8 when sanitizeKey", e);
    }
  }

  public Counter getCounter(String key, long initialValue) {
    return new Counter(this, key, initialValue);
  }

  public Counter getCounter(String key) {
    return new Counter(this, key, 0);
  }

  /**
   * @deprecated memcached 1.6.x will remove cachedump stats command,so this
   *             method will be removed in the future
   */
  @SuppressWarnings("unchecked")
  public KeyIterator getKeyIterator(InetSocketAddress address)
      throws MemcachedException, TimeoutException, InterruptedException {
    if (address == null) {
      throw new IllegalArgumentException("null address");
    }
    Queue<Session> sessions = this.connector.getSessionByAddress(address);
    if (sessions == null || sessions.size() == 0) {
      throw new MemcachedException(
          "The special memcached server has not been connected,"
              + address);
    }
    Session session = sessions.peek();
    CountDownLatch latch = new CountDownLatch(1);
    Command command = this.commandFactory.createStatsCommand(session
        .getRemoteSocketAddress(), latch, "items");
    session.write(command);
    if (!latch.await(5000, TimeUnit.MILLISECONDS)) {
      throw new TimeoutException("Operation timeout");
    }
    if (command.getException() != null) {
      if (command.getException() instanceof MemcachedException)
        throw (MemcachedException) command.getException();
      else
        throw new MemcachedException("stats items failed", command
            .getException());
    }
    Map<String, String> result = (Map<String, String>) command.getResult();
    LinkedList<Integer> itemNumberList = new LinkedList<Integer>();
    for (Map.Entry<String, String> entry : result.entrySet()) {
      final String key = entry.getKey();
      final String[] keys = key.split(":");
      if (keys.length == 3 && keys[2].equals("number")
          && keys[0].equals("items")) {
        // has items,then add it to itemNumberList
        if (Integer.parseInt(entry.getValue()) > 0) {
          itemNumberList.add(Integer.parseInt(keys[1]));
        }
      }
    }
    return new KeyIteratorImpl(itemNumberList, this, address);
  }

  public void setFailureMode(boolean failureMode) {
    this.failureMode = failureMode;
    if (this.sessionLocator != null) {
      this.sessionLocator.setFailureMode(failureMode);
    }
    if (this.connector != null) {
      this.connector.setFailureMode(failureMode);
    }
  }

  public boolean isFailureMode() {
    return this.failureMode;
  }

  public Queue<ReconnectRequest> getReconnectRequestQueue() {
    return this.connector != null ? this.connector
        .getReconnectRequestQueue() : null;
  }

}
TOP

Related Classes of net.rubyeye.xmemcached.XMemcachedClient

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.