package com.quickserverlab.quickcached; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.SocketException; import java.net.SocketTimeoutException; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import com.quickserverlab.quickcached.binary.BinaryPacket; import com.quickserverlab.quickcached.binary.Extras; import com.quickserverlab.quickcached.binary.OpCode; import com.quickserverlab.quickcached.binary.ResponseHeader; import com.quickserverlab.quickcached.cache.CacheException; import com.quickserverlab.quickcached.cache.CacheInterface; import org.quickserver.net.server.ClientHandler; /** * * @author akshath */ public class BinaryCommandProcessor { private static final Logger logger = Logger.getLogger(BinaryCommandProcessor.class.getName()); private static byte[] version = null; static { try { version = QuickCached.version.getBytes(HexUtil.getCharset()); } catch (UnsupportedEncodingException ex) { Logger.getLogger(BinaryCommandProcessor.class.getName()).log(Level.SEVERE, null, ex); } } private CacheInterface cache; public void setCache(CacheInterface cache) { this.cache = cache; } public void handleBinaryCommand(ClientHandler handler, BinaryPacket command) throws SocketTimeoutException, IOException, CacheException { if (QuickCached.DEBUG) { logger.log(Level.FINE, "command: {0}", command); } String opcode = command.getHeader().getOpcode(); if (QuickCached.DEBUG == false) { logger.log(Level.FINE, "opcode: {0}, key: {1}", new Object[]{opcode, command.getKey()}); } opcode = opcode.toUpperCase(); ResponseHeader rh = new ResponseHeader(); rh.setMagic("81"); rh.setOpcode(opcode); rh.setOpaque(command.getHeader().getOpaque()); BinaryPacket binaryPacket = new BinaryPacket(); binaryPacket.setHeader(rh); if (OpCode.GET.equals(opcode) || OpCode.GET_Q.equals(opcode)//Get,GetQ, || OpCode.GET_K.equals(opcode) || OpCode.GET_K_Q.equals(opcode) //GetK, GetKQ || OpCode.GAT.equals(opcode) || OpCode.GAT_Q.equals(opcode)) {//GAT, GATQ if (OpCode.GET_K.equals(opcode) || OpCode.GET_K_Q.equals(opcode)) {//GetK, GetKQ binaryPacket.setKey(command.getKey()); rh.setKeyLength(binaryPacket.getKey().length()); } DataCarrier dc = (DataCarrier) cache.get(command.getKey()); if (dc == null) { if (OpCode.GET_Q.equals(opcode) == false && OpCode.GET_K_Q.equals(opcode) == false //GetQ, GetKQ && OpCode.GAT_Q.equals(opcode) == false) { //GATQ rh.setStatus(ResponseHeader.KEY_NOT_FOUND); sendResponse(handler, binaryPacket); } } else { rh.setStatus(ResponseHeader.STATUS_NO_ERROR); if (OpCode.GAT.equals(opcode) || OpCode.GAT_Q.equals(opcode)) {//Gat, GatQ if (command.getExtras() != null) { cache.touch(command.getKey(), command.getExtras().getExpirationInSec()); } else { logger.log(Level.WARNING, "Extras not passed!!"); rh.setStatus(ResponseHeader.INVALID_ARGUMENTS); sendResponse(handler, binaryPacket); return; } } Extras extras = new Extras(); extras.setFlags(dc.getFlags()); binaryPacket.setExtras(extras); rh.setExtrasLength(4); rh.setCas(dc.getCas()); binaryPacket.setValue(dc.getData()); rh.setTotalBodyLength(rh.getKeyLength() + rh.getExtrasLength() + binaryPacket.getValue().length); rh.setCas(dc.getCas()); sendResponse(handler, binaryPacket); } } else if (OpCode.NOOP.equals(opcode)) { rh.setStatus(ResponseHeader.STATUS_NO_ERROR); sendResponse(handler, binaryPacket); } else if (OpCode.VERSION.equals(opcode)) { binaryPacket.setValue(version); rh.setStatus(ResponseHeader.STATUS_NO_ERROR); rh.setTotalBodyLength(rh.getKeyLength() + rh.getExtrasLength() + binaryPacket.getValue().length); sendResponse(handler, binaryPacket); } else if (OpCode.SET.equals(opcode) || OpCode.SET_Q.equals(opcode)) {//Set,SetQ DataCarrier dc = (DataCarrier) cache.get(command.getKey(), false); if (command.getHeader().getCas() != null && "0000000000000000".equals(command.getHeader().getCas()) == false) { if (dc == null) { CommandHandler.casMisses++; if (OpCode.SET.equals(opcode)) { //set rh.setStatus(ResponseHeader.KEY_NOT_FOUND); sendResponse(handler, binaryPacket); } return; } dc.readLock.lock(); try { if (dc.checkCas(command.getHeader().getCas()) == false) { CommandHandler.casBadval++; if(QuickCached.DEBUG) { logger.log(Level.FINE, "Cas did not match! OldCas:{0}NewCAS:{1}", new Object[]{dc.getCas(), command.getHeader().getCas()}); } if (OpCode.SET.equals(opcode)) { //set rh.setStatus(ResponseHeader.ITEM_NOT_STORED); //return; //no return as we need to reply } else { return; } } else { CommandHandler.casHits++; } } finally { dc.readLock.unlock(); } if(rh.getStatus()!=null) { sendResponse(handler, binaryPacket); return; } } if (dc == null) { dc = new DataCarrier(command.getValue()); dc.setFlags(command.getExtras().getFlags()); cache.set(command.getKey(), dc, dc.getSize(), command.getExtras().getExpirationInSec()); } else { dc.writeLock.lock(); try { dc.setData(command.getValue()); dc.setFlags(command.getExtras().getFlags()); cache.update(command.getKey(), dc, dc.getSize(), command.getExtras().getExpirationInSec()); } finally { dc.writeLock.unlock(); } } if (OpCode.SET.equals(opcode)) { rh.setStatus(ResponseHeader.STATUS_NO_ERROR); rh.setCas(dc.getCas()); sendResponse(handler, binaryPacket); } } else if (OpCode.ADD.equals(opcode) || OpCode.ADD_Q.equals(opcode)) { DataCarrier olddc = (DataCarrier) cache.get(command.getKey(), false); if (olddc != null) { if (OpCode.ADD.equals(opcode)) { rh.setStatus(ResponseHeader.KEY_EXISTS); sendResponse(handler, binaryPacket); } return; } DataCarrier dc = new DataCarrier(command.getValue()); dc.writeLock.lock(); try { if (command.getExtras().getFlags() != null) { dc.setFlags(command.getExtras().getFlags()); } cache.set(command.getKey(), dc, dc.getSize(), command.getExtras().getExpirationInSec()); } finally { dc.writeLock.unlock(); } if (OpCode.ADD.equals(opcode)) { rh.setStatus(ResponseHeader.STATUS_NO_ERROR); sendResponse(handler, binaryPacket); } } else if (OpCode.REPLACE.equals(opcode) || OpCode.REPLACE_Q.equals(opcode)) { DataCarrier olddc = (DataCarrier) cache.get(command.getKey(), false); if (olddc == null) { if (OpCode.REPLACE.equals(opcode)) { rh.setStatus(ResponseHeader.KEY_NOT_FOUND); sendResponse(handler, binaryPacket); } return; } if (command.getHeader().getCas() != null && "0000000000000000".equals(command.getHeader().getCas()) == false) { if (olddc != null) { olddc.readLock.lock(); try { if (olddc.checkCas(command.getHeader().getCas()) == false) { CommandHandler.casBadval++; if(QuickCached.DEBUG) { logger.log(Level.FINE, "Cas did not match! OldCas:{0}NewCAS:{1}", new Object[]{olddc.getCas(), command.getHeader().getCas()}); } if(OpCode.REPLACE.equals(opcode)) { rh.setStatus(ResponseHeader.ITEM_NOT_STORED); //no return since we have to reply } else { return; } } else { CommandHandler.casHits++; } } finally { olddc.readLock.unlock(); } if(rh.getStatus()!=null) { sendResponse(handler, binaryPacket); return; } } else { CommandHandler.casMisses++; } } olddc.writeLock.lock(); try { if (command.getExtras().getFlags() != null) { olddc.setFlags(command.getExtras().getFlags()); } olddc.setData(command.getValue()); cache.update(command.getKey(), olddc, olddc.getSize()); } finally { olddc.writeLock.unlock(); } if (OpCode.REPLACE.equals(opcode)) { rh.setStatus(ResponseHeader.STATUS_NO_ERROR); rh.setCas(olddc.getCas()); sendResponse(handler, binaryPacket); } } else if (OpCode.APPEND.equals(opcode) || OpCode.APPEND_Q.equals(opcode)) { DataCarrier olddc = (DataCarrier) cache.get(command.getKey(), false); if (olddc == null) { if (OpCode.APPEND.equals(opcode)) { rh.setStatus(ResponseHeader.ITEM_NOT_STORED); sendResponse(handler, binaryPacket); } return; } if (command.getHeader().getCas() != null && "0000000000000000".equals(command.getHeader().getCas()) == false) { if (olddc != null) { olddc.readLock.lock(); try { if (olddc.checkCas(command.getHeader().getCas()) == false) { CommandHandler.casBadval++; if (QuickCached.DEBUG) { logger.log(Level.FINE, "Cas did not match! OldCas:{0}NewCAS:{1}", new Object[]{olddc.getCas(), command.getHeader().getCas()}); } if (OpCode.APPEND.equals(opcode)) { rh.setStatus(ResponseHeader.ITEM_NOT_STORED); //no return since we need to reply } else { return; } } else { CommandHandler.casHits++; } } finally { olddc.readLock.unlock(); } if(rh.getStatus()!=null) { sendResponse(handler, binaryPacket); return; } } else { CommandHandler.casMisses++; } } olddc.writeLock.lock(); try { olddc.append(command.getValue()); cache.update(command.getKey(), olddc, olddc.getSize()); } finally { olddc.writeLock.unlock(); } if (OpCode.APPEND.equals(opcode)) { rh.setStatus(ResponseHeader.STATUS_NO_ERROR); rh.setCas(olddc.getCas()); sendResponse(handler, binaryPacket); } } else if (OpCode.PREPEND.equals(opcode) || OpCode.PREPEND_Q.equals(opcode)) { DataCarrier olddc = (DataCarrier) cache.get(command.getKey(), false); if (olddc == null) { if (OpCode.PREPEND.equals(opcode)) { rh.setStatus(ResponseHeader.ITEM_NOT_STORED); sendResponse(handler, binaryPacket); } return; } if (command.getHeader().getCas() != null && "0000000000000000".equals(command.getHeader().getCas()) == false) { if (olddc != null) { olddc.readLock.lock(); try { if (olddc.checkCas(command.getHeader().getCas()) == false) { CommandHandler.casBadval++; if(QuickCached.DEBUG) { logger.log(Level.FINE, "Cas did not match! OldCas:{0}NewCAS:{1}", new Object[]{olddc.getCas(), command.getHeader().getCas()}); } if(OpCode.PREPEND.equals(opcode)) { rh.setStatus(ResponseHeader.ITEM_NOT_STORED); //no return since we need to reply } else { return; } } else { CommandHandler.casHits++; } } finally { olddc.readLock.unlock(); } if(rh.getStatus()!=null) { sendResponse(handler, binaryPacket); return; } } else { CommandHandler.casMisses++; } } olddc.writeLock.lock(); try { olddc.prepend(command.getValue()); cache.update(command.getKey(), olddc, olddc.getSize()); } finally { olddc.writeLock.unlock(); } if (OpCode.PREPEND.equals(opcode)) { rh.setStatus(ResponseHeader.STATUS_NO_ERROR); rh.setCas(olddc.getCas()); sendResponse(handler, binaryPacket); } } else if (OpCode.DELETE.equals(opcode) || OpCode.DELETE_Q.equals(opcode)) {//Delete, DeleteQ boolean falg = cache.delete(command.getKey()); if (falg == false) { rh.setStatus(ResponseHeader.KEY_NOT_FOUND); sendResponse(handler, binaryPacket); } else { if (OpCode.DELETE.equals(opcode)) { rh.setStatus(ResponseHeader.STATUS_NO_ERROR); sendResponse(handler, binaryPacket); } } } else if (OpCode.INCREMENT.equals(opcode) || OpCode.DECREMENT.equals(opcode) ||//Increment, Decrement OpCode.INCREMENT_Q.equals(opcode) || OpCode.DECREMENT_Q.equals(opcode)) {//IncrementQ, DecrementQ Extras extras = command.getExtras(); char op = ' '; if (opcode.equals(OpCode.DECREMENT) || opcode.equals(OpCode.DECREMENT_Q)) { op = 'D'; } else if (opcode.equals(OpCode.INCREMENT) || opcode.equals(OpCode.INCREMENT_Q)) { op = 'I'; } else { throw new IllegalStateException("We should not be here!! " + opcode); } DataCarrier olddc = (DataCarrier) cache.get(command.getKey(), false); if (olddc == null) { if (extras.getExpiration().equals("ffffffff") == true) {//as per protocol if (op == 'I') { CommandHandler.incrMisses++; } else { CommandHandler.decrMisses++; } if (OpCode.INCREMENT.equals(opcode) || OpCode.DECREMENT.equals(opcode)) { //Increment, Decrement rh.setStatus(ResponseHeader.KEY_NOT_FOUND); sendResponse(handler, binaryPacket); } return; } else { String value = "" + extras.getInitalValueInDec(); olddc = new DataCarrier(value.getBytes(HexUtil.getCharset())); /* if (extras.getFlags() != null) { olddc.setFlags(extras.getFlags()); } */ olddc.setFlags("0");//as per protocol cache.set(command.getKey(), olddc, olddc.getSize(), extras.getExpirationInSec()); if (op == 'I') { CommandHandler.incrHits++; } else { CommandHandler.decrHits++; } if (OpCode.INCREMENT.equals(opcode) || OpCode.DECREMENT.equals(opcode)) { binaryPacket.setValue(olddc.getData()); rh.setTotalBodyLength(rh.getKeyLength() + rh.getExtrasLength() + binaryPacket.getValue().length); rh.setStatus(ResponseHeader.STATUS_NO_ERROR); rh.setCas(olddc.getCas()); sendResponse(handler, binaryPacket); } return; } } long value = 0; try { value = extras.getDeltaInDec(); } catch (Exception e) { if(QuickCached.DEBUG) { logger.log(Level.WARNING, "Error: {0}", e); } rh.setStatus(ResponseHeader.INVALID_ARGUMENTS); sendResponse(handler, binaryPacket); return; } try { olddc.writeLock.lock(); try { long oldvalue = Long.parseLong(new String(olddc.getData(), HexUtil.getCharset())); if (op == 'I') { value = oldvalue + value; } else { value = oldvalue - value; } if (value < 0) { value = 0; } olddc.setData(("" + value).getBytes(HexUtil.getCharset())); cache.update(command.getKey(), olddc, olddc.getSize()); } finally { olddc.writeLock.unlock(); } if (op == 'I') { CommandHandler.incrHits++; } else { CommandHandler.decrHits++; } } catch (Exception e) { if (op == 'I') { CommandHandler.incrMisses++; } else { CommandHandler.decrMisses++; } if (OpCode.INCREMENT.equals(opcode) || OpCode.DECREMENT.equals(opcode)) { rh.setStatus(ResponseHeader.INTERNAL_ERROR); sendResponse(handler, binaryPacket); return; } return; } cache.update(command.getKey(), olddc, olddc.getSize()); if (OpCode.INCREMENT_Q.equals(opcode) || OpCode.DECREMENT_Q.equals(opcode)) { return; } binaryPacket.setValue(Util.prefixZerros( new String(olddc.getData(), HexUtil.getCharset()), 8).getBytes(HexUtil.getCharset())); rh.setTotalBodyLength(rh.getKeyLength() + rh.getExtrasLength() + binaryPacket.getValue().length); rh.setStatus(ResponseHeader.STATUS_NO_ERROR); rh.setCas(olddc.getCas()); sendResponse(handler, binaryPacket); } else if (OpCode.QUIT.equals(opcode) || OpCode.QUIT_Q.equals(opcode)) {//Quit,QuitQ if (OpCode.QUIT.equals(opcode)) { rh.setStatus(ResponseHeader.STATUS_NO_ERROR); sendResponse(handler, binaryPacket); } handler.closeConnection(); } else if (OpCode.FLUSH.equals(opcode) || OpCode.FLUSH_Q.equals(opcode)) {//Flush, FlushQ cache.flush(); if (OpCode.FLUSH.equals(opcode)) { rh.setStatus(ResponseHeader.STATUS_NO_ERROR); sendResponse(handler, binaryPacket); } } else if (OpCode.STAT.equals(opcode)) {//Stat rh.setStatus(ResponseHeader.STATUS_NO_ERROR); Map stats = CommandHandler.getStats(handler.getServer()); Set keySet = stats.keySet(); Iterator iterator = keySet.iterator(); String key = null; String value = null; while (iterator.hasNext()) { key = (String) iterator.next(); value = (String) stats.get(key); binaryPacket.setKey(key); rh.setKeyLength(binaryPacket.getKey().length()); binaryPacket.setValue(value.getBytes(HexUtil.getCharset())); rh.setTotalBodyLength(rh.getKeyLength() + rh.getExtrasLength() + binaryPacket.getValue().length); sendResponse(handler, binaryPacket); } binaryPacket.setKey(null); rh.setKeyLength(0); binaryPacket.setValue(null); rh.setTotalBodyLength(0); sendResponse(handler, binaryPacket); } else if (OpCode.TOUCH.equals(opcode)) { //Touch - TODO revist after protocol docs are updated.. touch or touchq with key ? /* if ("0C".equals(opcode) || "0D".equals(opcode)) {//TouchK, TouchKQ binaryPacket.setKey(command.getKey()); rh.setKeyLength(binaryPacket.getKey().length()); } */ if (command.getExtras() == null) { logger.log(Level.WARNING, "Extras not passed!!"); rh.setStatus(ResponseHeader.INVALID_ARGUMENTS); sendResponse(handler, binaryPacket); return; } boolean flag = cache.touch(command.getKey(), command.getExtras().getExpirationInSec()); if (flag == false) { //if ("09".equals(opcode) == false && "0D".equals(opcode) == false) { //TouchQ, TouchKQ touch with key ? rh.setStatus(ResponseHeader.KEY_NOT_FOUND); sendResponse(handler, binaryPacket); //} } else { rh.setStatus(ResponseHeader.STATUS_NO_ERROR); /* Extras extras = new Extras(); extras.setFlags(dc.getFlags()); binaryPacket.setExtras(extras); rh.setExtrasLength(4); rh.setCas(dc.getCas()); binaryPacket.setValue(dc.getData()); rh.setTotalBodyLength(rh.getKeyLength() + rh.getExtrasLength() + binaryPacket.getValue().length); rh.setCas(dc.getCas()); * */ sendResponse(handler, binaryPacket); } } else { logger.log(Level.WARNING, "unknown binary command! {0}", opcode); rh.setStatus(ResponseHeader.UNKNOWN_COMMAND); sendResponse(handler, binaryPacket); } } public void sendResponse(ClientHandler handler, BinaryPacket binaryPacket) throws SocketTimeoutException, IOException { if (QuickCached.DEBUG) { logger.log(Level.FINE, "Res BinaryPacket: {0}", binaryPacket); } else { ResponseHeader rh = (ResponseHeader) binaryPacket.getHeader(); logger.log(Level.FINE, "S: Status {0}", rh.getStatus()); } byte data[] = binaryPacket.toBinaryByte(); if (handler.getCommunicationLogging() || QuickCached.DEBUG) { logger.log(Level.FINE, "S: {0}", new String(data, HexUtil.getCharset())); logger.log(Level.FINE, "H: {0}", HexUtil.encode(new String(data, HexUtil.getCharset()))); } else { logger.log(Level.FINE, "S: {0} bytes", data.length); } handler.sendClientBinary(data); } }