// Copyright (c) 2006 Dustin Sallings <dustin@spy.net>
package net.spy.memcached.protocol.ascii;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.apache.commons.lang.CharEncoding;
import net.spy.memcached.KeyUtil;
import net.spy.memcached.ops.Operation;
import net.spy.memcached.ops.OperationCallback;
import net.spy.memcached.ops.OperationErrorType;
import net.spy.memcached.ops.OperationState;
import net.spy.memcached.ops.OperationStatus;
import net.spy.memcached.protocol.BaseOperationImpl;
/**
* Operations on a memcached connection.
*/
abstract class OperationImpl extends BaseOperationImpl implements Operation {
protected static final byte[] CRLF={'\r', '\n'};
private final ByteArrayOutputStream byteBuffer=new ByteArrayOutputStream();
OperationReadType readType=OperationReadType.LINE;
boolean foundCr=false;
protected OperationImpl() {
super();
}
protected OperationImpl(OperationCallback cb) {
super();
callback=cb;
}
/**
* Match the status line provided against one of the given
* OperationStatus objects. If none match, return a failure status with
* the given line.
*
* @param line the current line
* @param statii several status objects
* @return the appropriate status object
*/
protected final OperationStatus matchStatus(String line,
OperationStatus... statii) {
OperationStatus rv=null;
for(OperationStatus status : statii) {
if(line.equals(status.getMessage())) {
rv=status;
}
}
if(rv == null) {
rv=new OperationStatus(false, line);
}
return rv;
}
/* (non-Javadoc)
* @see net.spy.memcached.protocol.ascii.Operation#getReadType()
*/
protected final OperationReadType getReadType() {
return readType;
}
/**
* Set the read type of this operation.
*/
protected final void setReadType(OperationReadType to) {
readType=to;
}
/**
* Set some arguments for an operation into the given byte buffer.
*/
protected final void setArguments(ByteBuffer bb, Object... args) {
boolean wasFirst=true;
for(Object o : args) {
if(wasFirst) {
wasFirst=false;
} else {
bb.put((byte)' ');
}
bb.put(KeyUtil.getKeyBytes(String.valueOf(o)));
}
bb.put(CRLF);
}
OperationErrorType classifyError(String line) {
OperationErrorType rv=null;
if(line.startsWith("ERROR")) {
rv=OperationErrorType.GENERAL;
} else if(line.startsWith("CLIENT_ERROR")) {
rv=OperationErrorType.CLIENT;
} else if(line.startsWith("SERVER_ERROR")) {
rv=OperationErrorType.SERVER;
}
return rv;
}
@Override
public void readFromBuffer(ByteBuffer data) throws IOException {
// Loop while there's data remaining to get it all drained.
while(getState() != OperationState.COMPLETE && data.remaining() > 0) {
if(readType == OperationReadType.DATA) {
handleRead(data);
} else {
int offset=-1;
for(int i=0; data.remaining() > 0; i++) {
byte b=data.get();
if(b == '\r') {
foundCr=true;
} else if(b == '\n') {
assert foundCr: "got a \\n without a \\r";
offset=i;
foundCr=false;
break;
} else {
assert !foundCr : "got a \\r without a \\n";
byteBuffer.write(b);
}
}
if(offset >= 0) {
String line = new String(byteBuffer.toByteArray(), CharEncoding.UTF_8);
byteBuffer.reset();
OperationErrorType eType=classifyError(line);
if(eType != null) {
handleError(eType, line);
} else {
handleLine(line);
}
}
}
}
}
/* (non-Javadoc)
* @see net.spy.memcached.protocol.ascii.Operation#handleLine(java.lang.String)
*/
public abstract void handleLine(String line);
}