package com.thinkaurelius.faunus.formats.rexster;
import com.thinkaurelius.faunus.formats.rexster.util.DefaultElementIdHandler;
import com.thinkaurelius.faunus.formats.rexster.util.ElementIdHandler;
import com.thinkaurelius.faunus.formats.rexster.util.OrientElementIdHandler;
import com.thinkaurelius.faunus.formats.rexster.util.TitanBerkeleyJEElementIdHandler;
import com.thinkaurelius.faunus.formats.rexster.util.VertexToFaunusBinary;
import com.tinkerpop.blueprints.Graph;
import com.tinkerpop.blueprints.Vertex;
import com.tinkerpop.rexster.RexsterApplicationGraph;
import com.tinkerpop.rexster.RexsterResourceContext;
import com.tinkerpop.rexster.extension.AbstractRexsterExtension;
import com.tinkerpop.rexster.extension.ExtensionConfiguration;
import com.tinkerpop.rexster.extension.ExtensionDefinition;
import com.tinkerpop.rexster.extension.ExtensionDescriptor;
import com.tinkerpop.rexster.extension.ExtensionNaming;
import com.tinkerpop.rexster.extension.ExtensionPoint;
import com.tinkerpop.rexster.extension.ExtensionResponse;
import com.tinkerpop.rexster.extension.HttpMethod;
import com.tinkerpop.rexster.extension.RexsterContext;
import com.tinkerpop.rexster.util.RequestObjectHelper;
import org.apache.log4j.Logger;
import org.codehaus.jettison.json.JSONObject;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.StreamingOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* Streams the vertex list back in FaunusVertex binary format.
*
* @author Stephen Mallette (http://stephen.genoprime.com)
*/
@ExtensionNaming(namespace = FaunusRexsterInputFormatExtension.EXTENSION_NAMESPACE, name = FaunusRexsterInputFormatExtension.EXTENSION_NAME)
public class FaunusRexsterInputFormatExtension extends AbstractRexsterExtension {
private static final Logger logger = Logger.getLogger(FaunusRexsterInputFormatExtension.class);
private static final ElementIdHandler DEFAULT_ID_HANDLER = new DefaultElementIdHandler();
private static final long WRITE_STATUS_EVERY = 10000;
private static final String CONFIG_ID_HANDLER = "id-handler";
public static final String EXTENSION_NAMESPACE = "faunus";
public static final String EXTENSION_NAME = "rexsterinputformat";
public static final String EXTENSION_METHOD_STREAM = "stream";
public static final String EXTENSION_METHOD_COUNT = "count";
@ExtensionDefinition(extensionPoint = ExtensionPoint.GRAPH, produces = MediaType.APPLICATION_JSON,
method = HttpMethod.GET, path = EXTENSION_METHOD_COUNT)
@ExtensionDescriptor(description = "get true count of vertices to calculate true split size for faunus")
public ExtensionResponse getVertexCount(@RexsterContext final Graph graph) {
logger.info("Faunus is configured to get the true count of vertices in the graph.");
int counter = 0;
final Iterable<Vertex> vertices = graph.getVertices();
for (Vertex v : vertices) {
counter++;
if (logger.isDebugEnabled() && counter % WRITE_STATUS_EVERY == 0) {
logger.debug(String.format("True count at: %s", counter));
}
}
final Map<String, Integer> m = new HashMap<String, Integer>();
m.put(EXTENSION_METHOD_COUNT, counter);
return ExtensionResponse.ok(m);
}
@ExtensionDefinition(extensionPoint = ExtensionPoint.GRAPH, produces = MediaType.APPLICATION_OCTET_STREAM,
method = HttpMethod.GET, path = EXTENSION_METHOD_STREAM)
@ExtensionDescriptor(description = "streaming vertices for faunus")
public ExtensionResponse getVertices(@RexsterContext final RexsterResourceContext context,
@RexsterContext final Graph graph) {
final JSONObject requestObject = context.getRequestObject();
final long start = RequestObjectHelper.getStartOffset(requestObject);
final long end = RequestObjectHelper.getEndOffset(requestObject);
final ElementIdHandler elementIdHandler = this.getElementIdHandler(context.getRexsterApplicationGraph());
final VertexToFaunusBinary vertexToFaunusBinary = new VertexToFaunusBinary(elementIdHandler);
// help with uniquely identifying incoming requests in logs.
final UUID requestIdentifier = UUID.randomUUID();
final String verticesInSplit = end == Long.MAX_VALUE ? "END" : String.valueOf(end - start);
logger.debug(String.format("Request [%s] split between [%s] and [%s].", requestIdentifier, start, end));
return new ExtensionResponse(Response.ok(new StreamingOutput() {
@Override
public void write(OutputStream out) throws IOException {
long counter = 0;
long vertexCount = 0;
final DataOutputStream dos = new DataOutputStream(out);
final Iterable<Vertex> vertices = graph.getVertices();
for (Vertex vertex : vertices) {
if (counter >= start && counter < end) {
vertexToFaunusBinary.writeVertex(vertex, dos);
if (logger.isDebugEnabled() && counter % WRITE_STATUS_EVERY == 0) {
logger.debug(String.format("Request [%s] at [%s] on the way to [%s].",
requestIdentifier, vertexCount, verticesInSplit));
}
vertexCount++;
} else if (counter >= end) {
logger.debug(String.format("Request [%s] completed.", requestIdentifier));
break;
}
counter++;
}
}
}).build());
}
private ElementIdHandler getElementIdHandler(final RexsterApplicationGraph rag) {
final ExtensionConfiguration configuration = rag.findExtensionConfiguration(EXTENSION_NAMESPACE, EXTENSION_NAME);
if (configuration == null) {
return DEFAULT_ID_HANDLER;
}
final Map<String, String> map = configuration.tryGetMapFromConfiguration();
final String idHandlerName = map.get(CONFIG_ID_HANDLER);
if (idHandlerName.equals("orientdb"))
return new OrientElementIdHandler();
else if (idHandlerName.equals("titan-berkeleyje"))
return new TitanBerkeleyJEElementIdHandler();
else
return DEFAULT_ID_HANDLER;
}
}