forked from hugegraph/hugegraph-sync
HugeGraph-89: implement rest framework based on jersey
Change-Id: I89d033abcec4398ed08e1c8cd261757a4a9b78cd
This commit is contained in:
parent
717cbb9597
commit
bc1cbb3a7e
|
|
@ -0,0 +1,19 @@
|
|||
package com.baidu.hugegraph.api;
|
||||
|
||||
import javax.ws.rs.NotFoundException;
|
||||
|
||||
import org.apache.tinkerpop.gremlin.structure.Graph;
|
||||
|
||||
import com.baidu.hugegraph.core.GraphManager;
|
||||
|
||||
public class API {
|
||||
|
||||
public static Graph graph(GraphManager manager, String graph) {
|
||||
Graph g = manager.graph(graph);
|
||||
if (g == null) {
|
||||
String msg = String.format( "Not found graph '%s'", graph);
|
||||
throw new NotFoundException(msg);
|
||||
}
|
||||
return g;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package com.baidu.hugegraph.api;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.ws.rs.ApplicationPath;
|
||||
|
||||
import org.glassfish.hk2.api.Factory;
|
||||
import org.glassfish.hk2.utilities.binding.AbstractBinder;
|
||||
import org.glassfish.jersey.process.internal.RequestScoped;
|
||||
import org.glassfish.jersey.server.ResourceConfig;
|
||||
|
||||
import com.baidu.hugegraph.core.GraphManager;
|
||||
|
||||
@ApplicationPath("/")
|
||||
public class Application extends ResourceConfig {
|
||||
|
||||
public Application() {
|
||||
packages("com.baidu.hugegraph.api");
|
||||
|
||||
// TODO: read from conf
|
||||
Map<String, String> graphConfs = new HashMap<>();
|
||||
graphConfs.put("hugegraph",
|
||||
"../hugegraph-dist/src/assembly/static/conf/hugegraph.properties");
|
||||
register(new GraphManagerFactory(graphConfs));
|
||||
}
|
||||
|
||||
static class GraphManagerFactory extends AbstractBinder
|
||||
implements Factory<GraphManager> {
|
||||
|
||||
private GraphManager manager = null;
|
||||
|
||||
public GraphManagerFactory(final Map<String, String> graphConfs) {
|
||||
this.manager = new GraphManager(graphConfs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bindFactory(this).to(GraphManager.class).in(RequestScoped.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphManager provide() {
|
||||
return this.manager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose(GraphManager manager) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
package com.baidu.hugegraph.api.filter;
|
||||
|
||||
import javax.json.Json;
|
||||
import javax.json.JsonObject;
|
||||
import javax.ws.rs.NotFoundException;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.ext.ExceptionMapper;
|
||||
import javax.ws.rs.ext.Provider;
|
||||
|
||||
import com.baidu.hugegraph.HugeException;
|
||||
|
||||
public class ExceptionFilter {
|
||||
|
||||
@Provider
|
||||
public static class HugeExceptionMapper
|
||||
implements ExceptionMapper<HugeException> {
|
||||
|
||||
@Override
|
||||
public Response toResponse(HugeException exception) {
|
||||
return Response.status(400)
|
||||
.type(MediaType.APPLICATION_JSON)
|
||||
.entity(formatException(exception))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@Provider
|
||||
public static class IllegalArgumentExceptionMapper
|
||||
implements ExceptionMapper<IllegalArgumentException> {
|
||||
|
||||
@Override
|
||||
public Response toResponse(IllegalArgumentException exception) {
|
||||
return Response.status(400)
|
||||
.type(MediaType.APPLICATION_JSON)
|
||||
.entity(formatException(exception))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@Provider
|
||||
public static class NotFoundExceptionExceptionMapper
|
||||
implements ExceptionMapper<NotFoundException> {
|
||||
|
||||
@Override
|
||||
public Response toResponse(NotFoundException exception) {
|
||||
return Response.status(404)
|
||||
.type(MediaType.APPLICATION_JSON)
|
||||
.entity(formatException(exception))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@Provider
|
||||
public static class UnknownExceptionMapper
|
||||
implements ExceptionMapper<Exception> {
|
||||
|
||||
@Override
|
||||
public Response toResponse(Exception exception) {
|
||||
return Response.status(500)
|
||||
.type(MediaType.APPLICATION_JSON)
|
||||
.entity(formatException(exception))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
public static String formatException(Exception exception) {
|
||||
JsonObject json = Json.createObjectBuilder()
|
||||
.add("exception", exception.getClass().toString())
|
||||
.add("message", exception.getMessage())
|
||||
.add("cause", (exception.getCause() != null
|
||||
? exception.getCause().toString() : ""))
|
||||
.build();
|
||||
return json.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package com.baidu.hugegraph.api.filter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
import javax.ws.rs.NameBinding;
|
||||
import javax.ws.rs.container.ContainerRequestContext;
|
||||
import javax.ws.rs.container.ContainerResponseContext;
|
||||
import javax.ws.rs.container.ContainerResponseFilter;
|
||||
import javax.ws.rs.ext.Provider;
|
||||
|
||||
@Provider
|
||||
public class StatusFilter implements ContainerResponseFilter {
|
||||
|
||||
@Override
|
||||
public void filter(ContainerRequestContext requestContext,
|
||||
ContainerResponseContext responseContext) throws IOException {
|
||||
if (responseContext.getStatus() == 200) {
|
||||
for (Annotation annotation : responseContext.getEntityAnnotations()) {
|
||||
if (annotation instanceof Status) {
|
||||
responseContext.setStatus(((Status) annotation).value());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NameBinding
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Status {
|
||||
final int CREATED = 201;
|
||||
|
||||
int value();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
package com.baidu.hugegraph.api.graph;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.DefaultValue;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import org.apache.tinkerpop.gremlin.structure.Graph;
|
||||
import org.apache.tinkerpop.gremlin.structure.T;
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.baidu.hugegraph.api.API;
|
||||
import com.baidu.hugegraph.api.filter.StatusFilter.Status;
|
||||
import com.baidu.hugegraph.core.GraphManager;
|
||||
import com.baidu.hugegraph.server.HugeServer;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
@Path("graphs/{graph}/graph/vertices")
|
||||
@Singleton
|
||||
public class VertexAPI extends API {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(HugeServer.class);
|
||||
|
||||
@POST
|
||||
@Status(Status.CREATED)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public String create(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
String keyValues) {
|
||||
logger.debug("Graph [{}] create vertex: {}", graph, keyValues);
|
||||
|
||||
Graph g = graph(manager, graph);
|
||||
|
||||
// TODO: improve keyValues parse
|
||||
Object[] props = keyValues.split(",");
|
||||
if (props[0].equals("T.label")) {
|
||||
props = ImmutableList.copyOf(props).toArray();
|
||||
props[0] = T.label;
|
||||
}
|
||||
|
||||
return manager.serializer(g).writeVertex(g.addVertex(props));
|
||||
}
|
||||
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public String list(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@DefaultValue("100") @QueryParam("limit") long limit) {
|
||||
logger.debug("Graph [{}] get vertices", graph);
|
||||
|
||||
Graph g = graph(manager, graph);
|
||||
List<Vertex> rs = g.traversal().V().limit(limit).toList();
|
||||
return manager.serializer(g).writeVertices(rs);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{id}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public String get(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("id") String id) {
|
||||
logger.debug("Graph [{}] get vertex by id '{}'", graph, id);
|
||||
|
||||
Graph g = graph(manager, graph);
|
||||
return manager.serializer(g).writeVertex(g.vertices(id).next());
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public void delete(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
@PathParam("id") String id) {
|
||||
logger.debug("Graph [{}] remove vertex by id '{}'", graph, id);
|
||||
|
||||
Graph g = graph(manager, graph);
|
||||
// TODO: add removeVertex(id) to improve
|
||||
g.vertices(id).next().remove();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
package com.baidu.hugegraph.core;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
|
||||
import org.apache.tinkerpop.gremlin.structure.Graph;
|
||||
import org.apache.tinkerpop.gremlin.structure.Transaction;
|
||||
import org.apache.tinkerpop.gremlin.structure.io.IoCore;
|
||||
import org.apache.tinkerpop.gremlin.structure.util.GraphFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.baidu.hugegraph.serializer.JsonSerializer;
|
||||
import com.baidu.hugegraph.serializer.Serializer;
|
||||
import com.baidu.hugegraph.server.HugeServer;
|
||||
|
||||
public final class GraphManager {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(HugeServer.class);
|
||||
|
||||
private final Map<String, Graph> graphs;
|
||||
private final Map<String, TraversalSource> traversalSources;
|
||||
|
||||
public GraphManager(final Map<String, String> graphConfs) {
|
||||
this.graphs = new ConcurrentHashMap<>();
|
||||
this.traversalSources = new ConcurrentHashMap<>();
|
||||
|
||||
loadGraphs(graphConfs);
|
||||
}
|
||||
|
||||
protected void loadGraphs(final Map<String, String> graphConfs) {
|
||||
graphConfs.entrySet().forEach(conf -> {
|
||||
try {
|
||||
final Graph newGraph = GraphFactory.open(conf.getValue());
|
||||
this.graphs.put(conf.getKey(), newGraph);
|
||||
logger.info("Graph '{}' was successfully configured via '{}'.",
|
||||
conf.getKey(), conf.getValue());
|
||||
} catch (RuntimeException e) {
|
||||
logger.warn("Graph '{}': '{}' could not be instantiated",
|
||||
conf.getKey(), conf.getValue(), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Map<String, Graph> graphs() {
|
||||
return this.graphs;
|
||||
}
|
||||
|
||||
public Graph graph(String name) {
|
||||
return this.graphs.get(name);
|
||||
}
|
||||
|
||||
public Serializer serializer(Graph g) {
|
||||
// TODO: cache Serializer
|
||||
return new JsonSerializer(g.io(IoCore.graphson()).writer().create());
|
||||
}
|
||||
|
||||
public Map<String, TraversalSource> traversalSources() {
|
||||
return this.traversalSources;
|
||||
}
|
||||
|
||||
public void rollbackAll() {
|
||||
this.graphs.entrySet().forEach(e -> {
|
||||
final Graph graph = e.getValue();
|
||||
if (graph.features().graph().supportsTransactions()
|
||||
&& graph.tx().isOpen()) {
|
||||
graph.tx().rollback();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void rollback(final Set<String> graphSourceNamesToCloseTxOn) {
|
||||
closeTx(graphSourceNamesToCloseTxOn, Transaction.Status.ROLLBACK);
|
||||
}
|
||||
|
||||
public void commitAll() {
|
||||
this.graphs.entrySet().forEach(e -> {
|
||||
final Graph graph = e.getValue();
|
||||
if (graph.features().graph().supportsTransactions()
|
||||
&& graph.tx().isOpen()) {
|
||||
graph.tx().commit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void commit(final Set<String> graphSourceNamesToCloseTxOn) {
|
||||
closeTx(graphSourceNamesToCloseTxOn, Transaction.Status.COMMIT);
|
||||
}
|
||||
|
||||
private void closeTx(final Set<String> graphSourceNamesToCloseTxOn,
|
||||
final Transaction.Status tx) {
|
||||
final Set<Graph> graphsToCloseTxOn = new HashSet<>();
|
||||
|
||||
graphSourceNamesToCloseTxOn.forEach(r -> {
|
||||
if (this.graphs.containsKey(r))
|
||||
graphsToCloseTxOn.add(this.graphs.get(r));
|
||||
else
|
||||
graphsToCloseTxOn.add(this.traversalSources.get(r).getGraph());
|
||||
});
|
||||
|
||||
graphsToCloseTxOn.forEach(graph -> {
|
||||
if (graph.features().graph().supportsTransactions()
|
||||
&& graph.tx().isOpen()) {
|
||||
if (tx == Transaction.Status.COMMIT) {
|
||||
graph.tx().commit();
|
||||
} else {
|
||||
graph.tx().rollback();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package com.baidu.hugegraph.serializer;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONWriter;
|
||||
|
||||
import com.baidu.hugegraph.HugeException;
|
||||
|
||||
public class JsonSerializer implements Serializer {
|
||||
|
||||
private GraphSONWriter writer;
|
||||
|
||||
public JsonSerializer(GraphSONWriter writer) {
|
||||
this.writer = writer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String writeVertex(Vertex v) {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
try {
|
||||
this.writer.writeVertex(out, v);
|
||||
} catch (IOException e) {
|
||||
throw new HugeException("Failed to serialize vertex", e);
|
||||
}
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String writeVertices(List<Vertex> vertices) {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
try {
|
||||
this.writer.writeVertices(out, vertices.iterator());
|
||||
} catch (IOException e) {
|
||||
throw new HugeException("Failed to serialize vertices", e);
|
||||
}
|
||||
return out.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.baidu.hugegraph.serializer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
|
||||
public interface Serializer {
|
||||
|
||||
public String writeVertex(Vertex v);
|
||||
|
||||
public String writeVertices(List<Vertex> vertices);
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
package com.baidu.hugegraph.server;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
|
||||
import org.glassfish.grizzly.http.server.HttpServer;
|
||||
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
|
||||
import org.glassfish.jersey.server.ResourceConfig;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.baidu.hugegraph.api.Application;
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
public class HugeServer {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(HugeServer.class);
|
||||
|
||||
private HttpServer httpServer = null;
|
||||
|
||||
public void start() throws IllegalArgumentException, IOException {
|
||||
// TODO: read from conf
|
||||
URI uri = UriBuilder.fromUri("http://127.0.0.1").port(8080).build();
|
||||
|
||||
ResourceConfig rc = new Application();
|
||||
|
||||
this.httpServer = GrizzlyHttpServerFactory.createHttpServer(uri, rc);
|
||||
this.httpServer.start();
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
Preconditions.checkNotNull(this.httpServer);
|
||||
this.httpServer.stop();
|
||||
}
|
||||
|
||||
public static void initEnv(String[] args) {
|
||||
// pass
|
||||
}
|
||||
|
||||
public static HugeServer start(String[] args) {
|
||||
logger.info("HugeServer starting...");
|
||||
// HugeServer.loadConf(args);
|
||||
HugeServer.initEnv(args);
|
||||
|
||||
HugeServer server = new HugeServer();
|
||||
try {
|
||||
server.start();
|
||||
logger.info("HugeServer started");
|
||||
} catch (IOException e) {
|
||||
logger.error("Failed to start HugeServer", e);
|
||||
}
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
HugeServer.start(args);
|
||||
Thread.currentThread().join();
|
||||
logger.info("HugeServer stopped");
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue