Package com.thimbleware.jmemcached.protocol

Source Code of com.thimbleware.jmemcached.protocol.MemcachedCommandHandler

/**
*  Copyright 2008 ThimbleWare Inc.
*
*  Licensed under the Apache License, Version 2.0 (the "License");
*  you may not use this file except in compliance with the License.
*  You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/
package com.thimbleware.jmemcached.protocol;

import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandler;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import org.jboss.netty.channel.group.DefaultChannelGroup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.thimbleware.jmemcached.Cache;
import com.thimbleware.jmemcached.CacheElement;
import com.thimbleware.jmemcached.StatsCounter;
import com.thimbleware.jmemcached.protocol.exceptions.DatabaseException;
import com.thimbleware.jmemcached.protocol.exceptions.UnknownCommandException;

// TODO implement flush_all delay

/**
* The actual command handler, which is responsible for processing the
* CommandMessage instances that are inbound from the protocol decoders.
* <p/>
* One instance is shared among the entire pipeline, since this handler is
* stateless, apart from some globals for the entire daemon.
* <p/>
* The command handler produces ResponseMessages which are destined for the
* response encoder.
*/
@ChannelHandler.Sharable
public final class MemcachedCommandHandler<CACHE_ELEMENT extends CacheElement> extends SimpleChannelUpstreamHandler {
  final Logger logger = LoggerFactory.getLogger(MemcachedCommandHandler.class);

  /**
   * The following state variables are universal for the entire daemon. These
   * are used for statistics gathering. In order for these values to work
   * properly, the handler _must_ be declared with a ChannelPipelineCoverage
   * of "all".
   */
  public final String version;

  public final int idle_limit;
  public final boolean verbose;

  /**
   * The actual physical data storage.
   */
  private final Cache<CACHE_ELEMENT> cache;

  /**
   * The channel group for the entire daemon, used for handling global cleanup
   * on shutdown.
   */
  private final DefaultChannelGroup channelGroup;

  /**
   * Construct the server session handler
   *
   * @param cache
   *            the cache to use
   * @param memcachedVersion
   *            the version string to return to clients
   * @param verbosity
   *            verbosity level for debugging
   * @param idle
   *            how long sessions can be idle for
   * @param channelGroup
   */
  public MemcachedCommandHandler(Cache cache, String memcachedVersion, boolean verbosity, int idle,
      DefaultChannelGroup channelGroup) {
    this.cache = cache;

    version = memcachedVersion;
    verbose = verbosity;
    idle_limit = idle;
    this.channelGroup = channelGroup;
  }

  /**
   * On open we manage some statistics, and add this connection to the channel
   * group.
   *
   * @param channelHandlerContext
   * @param channelStateEvent
   * @throws Exception
   */
  @Override
  public void channelOpen(ChannelHandlerContext channelHandlerContext, ChannelStateEvent channelStateEvent)
      throws Exception {
    StatsCounter.total_conns.incrementAndGet();
    StatsCounter.curr_conns.incrementAndGet();
    channelGroup.add(channelHandlerContext.getChannel());
  }

  /**
   * On close we manage some statistics, and remove this connection from the
   * channel group.
   *
   * @param channelHandlerContext
   * @param channelStateEvent
   * @throws Exception
   */
  @Override
  public void channelClosed(ChannelHandlerContext channelHandlerContext, ChannelStateEvent channelStateEvent)
      throws Exception {
    StatsCounter.curr_conns.decrementAndGet();
    channelGroup.remove(channelHandlerContext.getChannel());
  }

  /**
   * The actual meat of the matter. Turn CommandMessages into executions
   * against the physical cache, and then pass on the downstream messages.
   *
   * @param channelHandlerContext
   * @param messageEvent
   * @throws Exception
   */

  @Override
  @SuppressWarnings("unchecked")
  public void messageReceived(ChannelHandlerContext channelHandlerContext, MessageEvent messageEvent)
      throws Exception {
    if (!(messageEvent.getMessage() instanceof CommandMessage)) {
      // Ignore what this encoder can't encode.
      channelHandlerContext.sendUpstream(messageEvent);
      return;
    }

    CommandMessage<CACHE_ELEMENT> command = (CommandMessage<CACHE_ELEMENT>) messageEvent.getMessage();
    Command cmd = command.cmd;
    int cmdKeysSize = command.keys.size();

    // first process any messages in the delete queue
    cache.asyncEventPing();

    // now do the real work
    if (this.verbose) {
      StringBuilder log = new StringBuilder();
      log.append(cmd);
      if (command.element != null) {
        log.append(" ").append(command.element.getKeystring());
      }
      for (int i = 0; i < cmdKeysSize; i++) {
        log.append(" ").append(command.keys.get(i));
      }
      logger.info(log.toString());
    }

    Channel channel = messageEvent.getChannel();
    if (cmd == Command.GET || cmd == Command.GETS) {
      handleGets(channelHandlerContext, command, channel);
    } else if (cmd == Command.SET) {
      handleSet(channelHandlerContext, command, channel);
    } else if (cmd == Command.CAS) {
      handleCas(channelHandlerContext, command, channel);
    } else if (cmd == Command.ADD) {
      handleAdd(channelHandlerContext, command, channel);
    } else if (cmd == Command.REPLACE) {
      handleReplace(channelHandlerContext, command, channel);
    } else if (cmd == Command.APPEND) {
      handleAppend(channelHandlerContext, command, channel);
    } else if (cmd == Command.PREPEND) {
      handlePrepend(channelHandlerContext, command, channel);
    } else if (cmd == Command.INCR) {
      handleIncr(channelHandlerContext, command, channel);
    } else if (cmd == Command.DECR) {
      handleDecr(channelHandlerContext, command, channel);
    } else if (cmd == Command.DELETE) {
      handleDelete(channelHandlerContext, command, channel);
    } else if (cmd == Command.STATS) {
      handleStats(channelHandlerContext, command, cmdKeysSize, channel);
    } else if (cmd == Command.VERSION) {
      handleVersion(channelHandlerContext, command, channel);
    } else if (cmd == Command.QUIT) {
      handleQuit(channel);
    } else if (cmd == Command.FLUSH_ALL) {
      handleFlush(channelHandlerContext, command, channel);
    } else if (cmd == null) {
      // NOOP
      handleNoOp(channelHandlerContext, command);
    } else {
      throw new UnknownCommandException("unknown command:" + cmd);

    }

  }

  protected void handleNoOp(ChannelHandlerContext channelHandlerContext, CommandMessage<CACHE_ELEMENT> command) {
    Channels.fireMessageReceived(channelHandlerContext, new ResponseMessage(command));
  }

  protected void handleFlush(ChannelHandlerContext channelHandlerContext, CommandMessage<CACHE_ELEMENT> command,
      Channel channel) throws DatabaseException, Exception {
    Channels.fireMessageReceived(channelHandlerContext, new ResponseMessage(command).withFlushResponse(cache
        .flush_all(command.time)), channel.getRemoteAddress());
  }

  protected void handleQuit(Channel channel) {
    channel.disconnect();
  }

  protected void handleVersion(ChannelHandlerContext channelHandlerContext, CommandMessage<CACHE_ELEMENT> command,
      Channel channel) {
    ResponseMessage responseMessage = new ResponseMessage(command);
    responseMessage.version = version;
    Channels.fireMessageReceived(channelHandlerContext, responseMessage, channel.getRemoteAddress());
  }

  protected void handleStats(ChannelHandlerContext channelHandlerContext, CommandMessage<CACHE_ELEMENT> command,
      int cmdKeysSize, Channel channel) {
    String option = "";
    if (cmdKeysSize > 0) {
      option = command.keys.get(0);
    }
    Channels.fireMessageReceived(channelHandlerContext, new ResponseMessage(command).withStatResponse(cache.stat(
        option, channelHandlerContext)), channel.getRemoteAddress());
  }

  protected void handleDelete(ChannelHandlerContext channelHandlerContext, CommandMessage<CACHE_ELEMENT> command,
      Channel channel) throws DatabaseException, Exception {
    Cache.DeleteResponse dr = cache.delete(command.keys.get(0), command.time);
    Channels.fireMessageReceived(channelHandlerContext, new ResponseMessage(command).withDeleteResponse(dr),
        channel.getRemoteAddress());
  }

  protected void handleDecr(ChannelHandlerContext channelHandlerContext, CommandMessage<CACHE_ELEMENT> command,
      Channel channel) throws DatabaseException, Exception {
    Integer incrDecrResp = cache.get_add(command.keys.get(0), -1 * command.incrAmount);
    Channels.fireMessageReceived(channelHandlerContext, new ResponseMessage(command)
        .withIncrDecrResponse(incrDecrResp), channel.getRemoteAddress());
  }

  protected void handleIncr(ChannelHandlerContext channelHandlerContext, CommandMessage<CACHE_ELEMENT> command,
      Channel channel) throws DatabaseException, Exception {
    Integer incrDecrResp = cache.get_add(command.keys.get(0), command.incrAmount); // TODO
    // support
    // default
    // value
    // and
    // expiry!!
    Channels.fireMessageReceived(channelHandlerContext, new ResponseMessage(command)
        .withIncrDecrResponse(incrDecrResp), channel.getRemoteAddress());
  }

  protected void handlePrepend(ChannelHandlerContext channelHandlerContext, CommandMessage<CACHE_ELEMENT> command,
      Channel channel) throws DatabaseException, Exception {
    Cache.StoreResponse ret;
    ret = cache.prepend(command.element);
    Channels.fireMessageReceived(channelHandlerContext, new ResponseMessage(command).withResponse(ret), channel
        .getRemoteAddress());
  }

  protected void handleAppend(ChannelHandlerContext channelHandlerContext, CommandMessage<CACHE_ELEMENT> command,
      Channel channel) throws DatabaseException, Exception {
    Cache.StoreResponse ret;
    ret = cache.append(command.element);
    Channels.fireMessageReceived(channelHandlerContext, new ResponseMessage(command).withResponse(ret), channel
        .getRemoteAddress());
  }

  protected void handleReplace(ChannelHandlerContext channelHandlerContext, CommandMessage<CACHE_ELEMENT> command,
      Channel channel) throws DatabaseException, Exception {
    Cache.StoreResponse ret;
    ret = cache.replace(command.element);
    Channels.fireMessageReceived(channelHandlerContext, new ResponseMessage(command).withResponse(ret), channel
        .getRemoteAddress());
  }

  protected void handleAdd(ChannelHandlerContext channelHandlerContext, CommandMessage<CACHE_ELEMENT> command,
      Channel channel) throws DatabaseException, Exception {
    Cache.StoreResponse ret;
    ret = cache.add(command.element);
    Channels.fireMessageReceived(channelHandlerContext, new ResponseMessage(command).withResponse(ret), channel
        .getRemoteAddress());
  }

  protected void handleCas(ChannelHandlerContext channelHandlerContext, CommandMessage<CACHE_ELEMENT> command,
      Channel channel) throws DatabaseException, Exception {
    Cache.StoreResponse ret;
    ret = cache.cas(command.cas_key, command.element);
    Channels.fireMessageReceived(channelHandlerContext, new ResponseMessage(command).withResponse(ret), channel
        .getRemoteAddress());
  }

  protected void handleSet(ChannelHandlerContext channelHandlerContext, CommandMessage<CACHE_ELEMENT> command,
      Channel channel) throws DatabaseException, Exception {
    Cache.StoreResponse ret;
    ret = cache.set(command.element);
    Channels.fireMessageReceived(channelHandlerContext, new ResponseMessage(command).withResponse(ret), channel
        .getRemoteAddress());
  }

  protected void handleGets(ChannelHandlerContext channelHandlerContext, CommandMessage<CACHE_ELEMENT> command,
      Channel channel) {
    CACHE_ELEMENT[] results = get(command.keys.toArray(new String[command.keys.size()]));
    ResponseMessage<CACHE_ELEMENT> resp = new ResponseMessage<CACHE_ELEMENT>(command).withElements(results);
    Channels.fireMessageReceived(channelHandlerContext, resp, channel.getRemoteAddress());
  }

  /**
   * Get an element from the cache
   *
   * @param keys
   *            the key for the element to lookup
   * @return the element, or 'null' in case of cache miss.
   */
  private CACHE_ELEMENT[] get(String... keys) {
    return cache.get(keys);
  }

  /**
   * @return the current time in seconds (from epoch), used for expiries, etc.
   */
  private static int Now() {
    return (int) (System.currentTimeMillis() / 1000);
  }

}
TOP

Related Classes of com.thimbleware.jmemcached.protocol.MemcachedCommandHandler

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.