com.tinkerpop.rexster.protocol.server.RexProRequest Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of rexster-protocol Show documentation
Show all versions of rexster-protocol Show documentation
RexPro is a binary protocol for Rexster graph server.
package com.tinkerpop.rexster.protocol.server;
import com.tinkerpop.rexster.gremlin.converter.ConsoleResultConverter;
import com.tinkerpop.rexster.protocol.msg.ErrorResponseMessage;
import com.tinkerpop.rexster.protocol.msg.MessageTokens;
import com.tinkerpop.rexster.protocol.msg.MessageType;
import com.tinkerpop.rexster.protocol.msg.MessageUtil;
import com.tinkerpop.rexster.protocol.msg.RexProBindings;
import com.tinkerpop.rexster.protocol.msg.RexProMessage;
import com.tinkerpop.rexster.protocol.msg.RexProMessageMeta;
import com.tinkerpop.rexster.protocol.msg.RexProScriptResult;
import com.tinkerpop.rexster.protocol.msg.ScriptRequestMessage;
import com.tinkerpop.rexster.protocol.msg.ScriptResponseMessage;
import com.tinkerpop.rexster.protocol.msg.SessionRequestMessage;
import com.tinkerpop.rexster.protocol.msg.SessionResponseMessage;
import com.tinkerpop.rexster.protocol.serializer.RexProSerializer;
import com.tinkerpop.rexster.protocol.serializer.json.JSONSerializer;
import com.tinkerpop.rexster.protocol.serializer.msgpack.MsgPackSerializer;
import com.tinkerpop.rexster.protocol.serializer.msgpack.templates.MetaTemplate;
import com.tinkerpop.rexster.protocol.serializer.msgpack.templates.ResultsTemplate;
import com.tinkerpop.rexster.protocol.session.RexProSession;
import com.tinkerpop.rexster.server.RexsterApplication;
import org.apache.log4j.Logger;
import org.glassfish.grizzly.Buffer;
import org.msgpack.MessagePack;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.ByteBuffer;
import java.util.List;
/**
* Contains logic for deserializing and executing
* rexpro requests, and serializing the results
*
* @author Blake Eggleston (bdeggleston.github.com)
*/
public class RexProRequest {
private static final Logger logger = Logger.getLogger(RexProRequest.class);
private static final MessagePack msgpack = new MessagePack();
static {
msgpack.register(RexProMessageMeta.class, MetaTemplate.getInstance());
msgpack.register(RexProBindings.class, RexProBindings.SerializationTemplate.getInstance());
msgpack.register(RexProScriptResult.class, ResultsTemplate.getInstance());
}
private ByteBuffer requestBuffer;
private RexsterApplication rexsterApplication;
private final byte serializerType;
private final byte messageType;
private final int bodyLength;
private final int completeMessageLength;
//the raw bytes of the request message
//does not include message envelope
private final byte[] requestBytes;
private RexProMessage requestMessage = null;
//version byte
//serializer byte
//reserved byte (4x)
//message type byte
//body length int
private static int ENVELOPE_LENGTH = 1 + 1 + 4 + 1 + 4;
private RexProSession session = null;
private RexProMessage responseMessage = null;
//the raw bytes of the response message
//does not include message envelope
private byte[] responseBytes = null;
private static byte BYTE_VERSION = 1;
private static byte[] BYTES_RESERVED = new byte[] {0, 0, 0, 0};
public RexProRequest(ByteBuffer buffer, int bufferSize, RexsterApplication application) throws IncompleteRexProRequestException {
requestBuffer = buffer;
rexsterApplication = application;
if (bufferSize < ENVELOPE_LENGTH) {
throw new IncompleteRexProRequestException();
}
serializerType = requestBuffer.get(1);
//bytes 2,3,4,5 are reserved for future use
messageType = requestBuffer.get(6);
bodyLength = requestBuffer.getInt(7);
completeMessageLength = ENVELOPE_LENGTH + bodyLength;
// If the source message doesn't contain entire body
if (bufferSize < completeMessageLength) {
throw new IncompleteRexProRequestException(String.format("Message envelope is incomplete. Message length set to %s but the buffer only contains %s", completeMessageLength, bufferSize));
}
if (bodyLength < 0) {
throw new IncompleteRexProRequestException(String.format("Message body length in the envelope is negative: %s.", completeMessageLength));
}
//get the actual message requestBytes
requestBytes = new byte[bodyLength];
buffer.position(ENVELOPE_LENGTH);
buffer.get(requestBytes);
}
public int getCompleteRequestMessageLength() {
return completeMessageLength;
}
public byte getRequestMessageType() {
return messageType;
}
public byte[] getRequestMessageBytes() {
return requestBytes;
}
public int getRequestBodyLength() {
return bodyLength;
}
public void setSession(RexProSession session) {
this.session = session;
}
public RexProMessage getRequestMessage() {
return requestMessage;
}
private static MsgPackSerializer msgPackSerializer = new MsgPackSerializer();
private static JSONSerializer jsonSerializer = new JSONSerializer();
protected RexProSerializer getSerializer() {
if (serializerType == jsonSerializer.getSerializerId()){
return jsonSerializer;
} else {
return msgPackSerializer;
}
}
private void deserializeMessage() throws IOException{
try {
if (messageType == MessageType.SCRIPT_REQUEST) {
requestMessage = getSerializer().deserialize(requestBytes, ScriptRequestMessage.class);
} else if (messageType == MessageType.SESSION_REQUEST) {
requestMessage = getSerializer().deserialize(requestBytes, SessionRequestMessage.class);
}
if (requestMessage == null) {
logger.warn(String.format("Message did not match the specified type [%s]", messageType));
writeResponseMessage(
MessageUtil.createErrorResponse(
RexProMessage.EMPTY_REQUEST_AS_BYTES,
RexProMessage.EMPTY_SESSION_AS_BYTES,
ErrorResponseMessage.INVALID_MESSAGE_ERROR,
MessageTokens.ERROR_UNEXPECTED_MESSAGE_TYPE
)
);
}
} catch (Exception ex) {
logger.error(String.format("Error during message deserialization of a message of type [%s].", messageType), ex);
writeResponseMessage(
MessageUtil.createErrorResponse(
RexProMessage.EMPTY_REQUEST_AS_BYTES,
RexProMessage.EMPTY_SESSION_AS_BYTES,
ErrorResponseMessage.INVALID_MESSAGE_ERROR,
ex.toString()
)
);
}
}
public void process() {
// may have been processed by the security filter already.
if (requestMessage != null) return;
try {
deserializeMessage();
} catch (Exception ex) {
logger.warn("error deserializing message: " + ex.toString());
}
//did the deserializer set the response message?
//(probably an error response), if so, return early
if (responseMessage != null) return;
try {
if (requestMessage instanceof SessionRequestMessage) {
SessionServer sessionServer = new SessionServer(rexsterApplication);
sessionServer.handleRequest((SessionRequestMessage) requestMessage, this);
} else if (requestMessage instanceof ScriptRequestMessage) {
ScriptServer scriptServer = new ScriptServer(rexsterApplication);
scriptServer.handleRequest((ScriptRequestMessage) requestMessage, this);
}
} catch (Exception ex) {
logger.warn("error processing rexpro request: " + ex.toString());
try {
//try to send an error message
if (responseMessage == null) {
writeResponseMessage(
MessageUtil.createErrorResponse(
requestMessage.Request,
requestMessage.Session,
ErrorResponseMessage.UNKNOWN_ERROR,
ex.toString()
)
);
}
} catch (IOException ex2) {
//don't do anything
}
}
//TODO: do something if the response message hasn't been set yet
}
/**
* Writes the result of a script request to it's message
* type and serializes the result immediately
*
* @param result
*/
public void writeScriptResults(Object result) throws Exception {
ScriptRequestMessage message = ((ScriptRequestMessage) requestMessage);
if (message.metaGetConsole()) {
writeResponseMessage(formatForConsoleChannel(message, session, result));
} else {
writeResponseMessage(formatForMsgPackChannel(message, session, result));
}
}
/**
* Writes a response message
*
* @param response
*/
public void writeResponseMessage(RexProMessage response) throws IOException {
responseMessage = response;
serializeMessage();
}
private void serializeMessage() throws IOException {
try {
RexProSerializer serializer = getSerializer();
if (responseMessage instanceof SessionResponseMessage) {
responseBytes = serializer.serialize((SessionResponseMessage) responseMessage, SessionResponseMessage.class);
} else if (responseMessage instanceof ErrorResponseMessage) {
responseBytes = serializer.serialize((ErrorResponseMessage) responseMessage, ErrorResponseMessage.class);
} else if (responseMessage instanceof ScriptResponseMessage) {
responseBytes = serializer.serialize((ScriptResponseMessage) responseMessage, ScriptResponseMessage.class);
} else {
throw new Exception();
}
} catch (Exception ex) {
// if there's an error during serialization with msgpack this could tank. the script will already
// have executed and likely committed with success. just means the response won't get back cleanly
// to the client.
ErrorResponseMessage errorMsg = MessageUtil.createErrorResponse(
responseMessage.Request,
responseMessage.Session,
ErrorResponseMessage.RESULT_SERIALIZATION_ERROR,
MessageTokens.ERROR_RESULT_SERIALIZATION
);
try {
responseBytes = getSerializer().serialize(errorMsg, ErrorResponseMessage.class);
responseMessage = errorMsg;
} catch (IOException ex2) {
logger.error(String.format(
"Could not serialize error message for request [%s] session [%s]. Should have reported flag [%s] message [%s] to client",
errorMsg.requestAsUUID(),
errorMsg.sessionAsUUID(),
errorMsg.metaGetFlag(),
errorMsg.ErrorMessage
));
}
}
}
public int getResponseSize() {
return ENVELOPE_LENGTH + responseBytes.length;
}
public void writeToBuffer(final Buffer bb) {
//add version
bb.put(BYTE_VERSION);
//add serializer
bb.put(serializerType);
//add reserved bytes
bb.put(BYTES_RESERVED);
if (responseMessage instanceof SessionResponseMessage) {
bb.put(MessageType.SESSION_RESPONSE);
} else if (responseMessage instanceof ErrorResponseMessage) {
bb.put(MessageType.ERROR);
} else if (responseMessage instanceof ScriptResponseMessage) {
bb.put(MessageType.SCRIPT_RESPONSE);
}
bb.putInt(responseBytes.length);
bb.put(responseBytes);
}
private static ScriptResponseMessage formatForMsgPackChannel(final ScriptRequestMessage specificMessage, final RexProSession session, final Object result) throws Exception {
final ScriptResponseMessage msgPackScriptResponseMessage = new ScriptResponseMessage();
if (specificMessage.metaGetInSession()){
msgPackScriptResponseMessage.Session = specificMessage.Session;
} else {
msgPackScriptResponseMessage.setSessionAsUUID(RexProMessage.EMPTY_SESSION);
}
msgPackScriptResponseMessage.Request = specificMessage.Request;
msgPackScriptResponseMessage.Results.set(result);
if (session != null){
msgPackScriptResponseMessage.Bindings.putAll(session.getBindings());
}
msgPackScriptResponseMessage.validateMetaData();
return msgPackScriptResponseMessage;
}
public static List convertResultToConsoleLines(final Object result) throws Exception {
final ConsoleResultConverter converter = new ConsoleResultConverter(new StringWriter());
return converter.convert(result);
}
private static ScriptResponseMessage formatForConsoleChannel(final ScriptRequestMessage specificMessage, final RexProSession session, final Object result) throws Exception {
final ScriptResponseMessage consoleScriptResponseMessage = new ScriptResponseMessage();
if (specificMessage.metaGetInSession()){
consoleScriptResponseMessage.Session = specificMessage.Session;
} else {
consoleScriptResponseMessage.setSessionAsUUID(RexProMessage.EMPTY_SESSION);
}
consoleScriptResponseMessage.Request = specificMessage.Request;
final List consoleLines = convertResultToConsoleLines(result);
consoleScriptResponseMessage.Results.set(consoleLines);
if (session != null) {
consoleScriptResponseMessage.Bindings.putAll(session.getBindings());
}
consoleScriptResponseMessage.validateMetaData();
return consoleScriptResponseMessage;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy