├── .gitignore ├── README.txt ├── pom.xml └── src ├── main └── java │ └── org │ └── neo4j │ └── smack │ ├── Neo4jServer.java │ ├── Smack.java │ ├── api │ ├── BasePropertyContainerService.java │ ├── DataOperationsDiscoveryService.java │ ├── DataOperationsService.java │ ├── DatabaseService.java │ ├── IndexService.java │ ├── NodeService.java │ ├── RelationshipService.java │ ├── RestService.java │ ├── TransactionService.java │ ├── TraversalService.java │ └── UrlReverseLookerUpper.java │ ├── gcfree │ ├── MutableString.java │ └── MutableStringConverter.java │ ├── pipeline │ ├── CombinedHandler.java │ ├── DaemonThreadFactory.java │ ├── DefaultExceptionHandler.java │ ├── RingBufferWorkPipeline.java │ ├── core │ │ ├── CoreWorkPipeline.java │ │ ├── DeserializationHandler.java │ │ ├── RoutingHandler.java │ │ ├── TransactionPreparationHandler.java │ │ ├── WorkDivisionHandler.java │ │ ├── WorkPublisher.java │ │ ├── WorkTransactionPreparer.java │ │ └── event │ │ │ ├── CorePipelineEvent.java │ │ │ └── TransactionWork.java │ ├── database │ │ ├── DatabaseWorkPerformer.java │ │ ├── DatabaseWorkPipeline.java │ │ ├── ExceptionOutputWriter.java │ │ ├── ThreadTransactionManagement.java │ │ ├── TransactionRegistry.java │ │ └── event │ │ │ ├── DatabaseWork.java │ │ │ ├── DefaultInvocationImpl.java │ │ │ ├── Invocation.java │ │ │ ├── NettyChannelBackedOutput.java │ │ │ └── Output.java │ ├── event │ │ ├── Fallible.java │ │ └── WorkTransactionMode.java │ └── http │ │ ├── CommonHeaderValues.java │ │ ├── HttpDecoder.java │ │ ├── HttpEncoder.java │ │ ├── HttpHeaderContainer.java │ │ ├── HttpHeaderDecoder.java │ │ ├── HttpHeaderName.java │ │ ├── HttpHeaderNames.java │ │ ├── HttpTokens.java │ │ ├── NettyChannelTrackingHandler.java │ │ ├── NettyHttpHandler.java │ │ └── NettyHttpPipelineFactory.java │ ├── routing │ ├── AnnotationBasedRoutingDefinition.java │ ├── Endpoint.java │ ├── InvocationVerb.java │ ├── NotFoundEndpoint.java │ ├── PathVariables.java │ ├── ResettableQueryStringDecoder.java │ ├── ResourceNotFoundException.java │ ├── Routable.java │ ├── RouteDefinitionEntry.java │ ├── RouteEntry.java │ ├── Router.java │ ├── RoutingDefinition.java │ ├── SimpleEndpoint.java │ └── annotation │ │ ├── DeserializeWith.java │ │ ├── SerializeWith.java │ │ └── Transactional.java │ └── serialization │ ├── DeserializationException.java │ ├── DeserializationStrategy.java │ ├── Deserializer.java │ ├── IdentifiableEnum.java │ ├── IdentifiableEnumDeserializer.java │ ├── JsonDeserializer.java │ ├── JsonSerializer.java │ ├── SerializationException.java │ ├── SerializationFactory.java │ ├── SerializationStrategy.java │ ├── Serializer.java │ └── strategy │ ├── ExceptionSerializationStrategy.java │ ├── NodeSerializationStrategy.java │ ├── PropertyContainerDeserialization.java │ ├── PropertyContainerDeserializationStrategy.java │ ├── PropertyContainerSerializationStrategy.java │ ├── PropertyValueDeserializationStrategy.java │ ├── PropertyValueSerializationStrategy.java │ ├── RelationshipCreationDescription.java │ ├── RelationshipCreationDeserializationStrategy.java │ ├── RelationshipSerializationStrategy.java │ ├── TransactionStateDeserialization.java │ └── TransactionStateDeserializationStrategy.java └── test └── java └── org └── neo4j └── smack ├── gcfree └── TestMutableStringConverter.java ├── integration └── api │ ├── ClientIdIT.java │ ├── DataAPIRootIT.java │ ├── ErrorHandlingIT.java │ ├── NodeServiceIT.java │ ├── RelationshipServiceIT.java │ └── TransactionServiceIT.java ├── performance ├── LoadGeneratingRunnable.java ├── NetworkLatency.java ├── NetworkThroughput.java └── PerfTestServer.java ├── pipeline ├── core │ └── TestWorkTransactionPreparer.java └── http │ ├── TestHttpDecoder.java │ ├── TestHttpHeaderContainer.java │ ├── TestHttpHeaderDecoder.java │ └── TestHttpHeaderName.java ├── routing ├── TestAnnotationBasedRoutingDefinition.java ├── TestResettableQueryStringDecoder.java └── TestRouter.java ├── serialization ├── TestDeserializationStrategy.java ├── TestJsonDeserializer.java ├── TestSerializationStrategy.java └── strategy │ ├── SerializationStrategyTestBase.java │ ├── TestNodeSerializationStrategy.java │ ├── TestPropertyContainerDeserializationStrategy.java │ ├── TestRelationshipCreationDeserializationStrategy.java │ ├── TestRelationshipSerializationStrategy.java │ └── TestTransactionStateDeserializationStrategy.java └── test └── util ├── AbstractRestFunctionalTestBase.java ├── FixedRequestClient.java ├── JaxRsResponse.java ├── JsonHelper.java ├── PerformanceRoutes.java ├── PipelinedHttpClient.java ├── REST.java ├── RESTDocsGenerator.java ├── RestRequest.java ├── ServerHelper.java ├── ServerHolder.java ├── SharedSmackServerTestBase.java ├── Transactor.java └── UnitOfWork.java /.gitignore: -------------------------------------------------------------------------------- 1 | .classpath 2 | .project 3 | .settings 4 | target 5 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-contrib/fast-http/7f912fdff7cab9f31dc79a7628d11c2ba80bf0e8/README.txt -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/Neo4jServer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2002-2011 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | package org.neo4j.smack; 21 | 22 | import java.io.IOException; 23 | 24 | import org.neo4j.graphdb.GraphDatabaseService; 25 | import org.neo4j.kernel.EmbeddedGraphDatabase; 26 | import org.neo4j.smack.api.DatabaseService; 27 | 28 | public class Neo4jServer { 29 | 30 | private Smack smackServer; 31 | 32 | public static void main(String[] args) throws IOException { 33 | final Neo4jServer neo4jServer = new Neo4jServer(args[0], Integer.parseInt(args[1]), new EmbeddedGraphDatabase(args[2])); 34 | neo4jServer.start(); 35 | System.in.read(); 36 | neo4jServer.stop(); 37 | } 38 | 39 | public Neo4jServer(String host, int port, GraphDatabaseService db) { 40 | smackServer = new Smack(host, port, db); 41 | smackServer.addRoute("/db/data", new DatabaseService("/db/data")); 42 | } 43 | 44 | public void start() { 45 | smackServer.start(); 46 | } 47 | 48 | public void stop() { 49 | smackServer.stop(); 50 | } 51 | 52 | public Smack getSmackServer() { 53 | return smackServer; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/Smack.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack; 2 | 3 | import java.net.InetSocketAddress; 4 | import java.util.concurrent.Executors; 5 | 6 | import org.jboss.netty.bootstrap.ServerBootstrap; 7 | import org.jboss.netty.channel.group.ChannelGroup; 8 | import org.jboss.netty.channel.group.DefaultChannelGroup; 9 | import org.jboss.netty.channel.socket.ServerSocketChannelFactory; 10 | import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; 11 | import org.neo4j.graphdb.GraphDatabaseService; 12 | import org.neo4j.smack.pipeline.DaemonThreadFactory; 13 | import org.neo4j.smack.pipeline.DefaultExceptionHandler; 14 | import org.neo4j.smack.pipeline.core.CoreWorkPipeline; 15 | import org.neo4j.smack.pipeline.core.DeserializationHandler; 16 | import org.neo4j.smack.pipeline.core.RoutingHandler; 17 | import org.neo4j.smack.pipeline.core.TransactionPreparationHandler; 18 | import org.neo4j.smack.pipeline.core.WorkDivisionHandler; 19 | import org.neo4j.smack.pipeline.http.NettyHttpPipelineFactory; 20 | import org.neo4j.smack.routing.Endpoint; 21 | import org.neo4j.smack.routing.Router; 22 | import org.neo4j.smack.routing.RoutingDefinition; 23 | 24 | public class Smack { 25 | 26 | private int port; 27 | private String host; 28 | private Router router = new Router(); 29 | private ServerBootstrap netty; 30 | 31 | private CoreWorkPipeline inputPipeline; 32 | 33 | private ServerSocketChannelFactory channelFactory; 34 | private ChannelGroup openChannels = new DefaultChannelGroup("SmackServer"); 35 | private GraphDatabaseService database; 36 | private WorkDivisionHandler workDivisionHandler; 37 | private DefaultExceptionHandler exceptionHandler; 38 | 39 | public Smack(String host, int port, GraphDatabaseService db) { 40 | this.host = host; 41 | this.port = port; 42 | this.database = db; 43 | } 44 | 45 | public void start() { 46 | 47 | router.compileRoutes(); 48 | 49 | // MAIN PIPELINE 50 | 51 | exceptionHandler = new DefaultExceptionHandler(); 52 | workDivisionHandler = new WorkDivisionHandler(database, exceptionHandler); 53 | 54 | inputPipeline = new CoreWorkPipeline(exceptionHandler, 55 | new RoutingHandler(router), 56 | new DeserializationHandler(), 57 | new TransactionPreparationHandler(), 58 | workDivisionHandler); 59 | 60 | inputPipeline.start(); 61 | 62 | // NETTY 63 | 64 | channelFactory = 65 | new NioServerSocketChannelFactory( 66 | Executors.newCachedThreadPool(new DaemonThreadFactory("SocketMaster")), 67 | Executors.newCachedThreadPool(new DaemonThreadFactory("SocketSlave"))); 68 | netty = new ServerBootstrap(channelFactory); 69 | 70 | // Set up the event pipeline factory. 71 | netty.setPipelineFactory(new NettyHttpPipelineFactory(inputPipeline, openChannels)); 72 | 73 | // Bind and start to accept incoming connections. 74 | openChannels.add(netty.bind(new InetSocketAddress(host, port))); 75 | 76 | } 77 | 78 | public void stop() { 79 | if (openChannels!=null) openChannels.close().awaitUninterruptibly(); 80 | if (channelFactory!=null) channelFactory.releaseExternalResources(); 81 | if (workDivisionHandler!=null) workDivisionHandler.stop(); 82 | if (inputPipeline!=null) inputPipeline.stop(); 83 | } 84 | 85 | public void addRoute(String route, RoutingDefinition target) { 86 | router.addRoute(route, target); 87 | } 88 | 89 | public void addRoute(String route, Endpoint target) { 90 | router.addRoute(route, target); 91 | } 92 | 93 | public void addRoute(String route, Object target) { 94 | router.addRoute(route, target); 95 | } 96 | 97 | public GraphDatabaseService getDatabase() { 98 | return database; 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/api/BasePropertyContainerService.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.api; 2 | 3 | import org.neo4j.graphdb.PropertyContainer; 4 | import org.neo4j.smack.pipeline.database.event.Invocation; 5 | import org.neo4j.smack.serialization.strategy.PropertyContainerDeserialization; 6 | 7 | public class BasePropertyContainerService 8 | { 9 | 10 | protected UrlReverseLookerUpper url = new UrlReverseLookerUpper(); 11 | 12 | protected long getNodeId(Invocation invocation) 13 | { 14 | return invocation.getLongParameter(UrlReverseLookerUpper.NODE_ID_NAME, -1l); 15 | } 16 | 17 | protected long getRelationshipId(Invocation invocation) 18 | { 19 | return invocation.getLongParameter(UrlReverseLookerUpper.RELATIONSHIP_ID_NAME, -1l); 20 | } 21 | 22 | protected String getPropertyKey(Invocation invocation) 23 | { 24 | return invocation.getStringParameter(UrlReverseLookerUpper.PROPERTY_KEY_NAME, null); 25 | } 26 | 27 | protected void setProperties(PropertyContainer entity, PropertyContainerDeserialization props) 28 | { 29 | while(props.hasMoreProperties()) 30 | { 31 | props.nextProperty(); 32 | entity.setProperty(props.propertyKey(), props.propertyValue()); 33 | } 34 | } 35 | 36 | protected void removeAllProperties(PropertyContainer entity) 37 | { 38 | for(String key: entity.getPropertyKeys()) 39 | { 40 | entity.removeProperty(key); 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/api/DataOperationsDiscoveryService.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.api; 2 | 3 | import javax.ws.rs.GET; 4 | import javax.ws.rs.Path; 5 | 6 | import org.neo4j.smack.pipeline.database.event.Invocation; 7 | import org.neo4j.smack.pipeline.database.event.Output; 8 | 9 | public class DataOperationsDiscoveryService { 10 | 11 | @GET 12 | @Path("") 13 | public void getServiceDescription(Invocation invocation, Output result) 14 | { 15 | result.ok(); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/api/DataOperationsService.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.api; 2 | 3 | import org.neo4j.smack.routing.RoutingDefinition; 4 | 5 | public class DataOperationsService extends RoutingDefinition { 6 | 7 | public DataOperationsService(String dataAPIPath) { 8 | addRoute("", new NodeService()); 9 | addRoute("", new RelationshipService()); 10 | addRoute("", new IndexService(dataAPIPath)); 11 | addRoute("", new TraversalService(dataAPIPath)); 12 | addRoute("", new DataOperationsDiscoveryService()); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/api/DatabaseService.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2002-2011 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | package org.neo4j.smack.api; 21 | 22 | import org.neo4j.smack.routing.RoutingDefinition; 23 | 24 | public class DatabaseService extends RoutingDefinition { 25 | 26 | public DatabaseService(String dataPath) { 27 | addRoute("/tx", new TransactionService()); 28 | 29 | addRoute("", new DataOperationsService(dataPath)); 30 | addRoute("/tx/{tx_id}", new DataOperationsService(dataPath)); 31 | } 32 | } -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/api/NodeService.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.api; 2 | 3 | import javax.ws.rs.DELETE; 4 | import javax.ws.rs.GET; 5 | import javax.ws.rs.POST; 6 | import javax.ws.rs.PUT; 7 | import javax.ws.rs.Path; 8 | 9 | import org.neo4j.graphdb.Node; 10 | import org.neo4j.smack.pipeline.database.event.Invocation; 11 | import org.neo4j.smack.pipeline.database.event.Output; 12 | import org.neo4j.smack.routing.annotation.DeserializeWith; 13 | import org.neo4j.smack.routing.annotation.SerializeWith; 14 | import org.neo4j.smack.routing.annotation.Transactional; 15 | import org.neo4j.smack.serialization.strategy.NodeSerializationStrategy; 16 | import org.neo4j.smack.serialization.strategy.PropertyContainerDeserialization; 17 | import org.neo4j.smack.serialization.strategy.PropertyContainerDeserializationStrategy; 18 | import org.neo4j.smack.serialization.strategy.PropertyContainerSerializationStrategy; 19 | import org.neo4j.smack.serialization.strategy.PropertyValueDeserializationStrategy; 20 | import org.neo4j.smack.serialization.strategy.PropertyValueSerializationStrategy; 21 | 22 | public class NodeService extends BasePropertyContainerService 23 | { 24 | 25 | @POST 26 | @Transactional 27 | @Path(UrlReverseLookerUpper.PATH_NODES) 28 | @DeserializeWith(PropertyContainerDeserializationStrategy.class) 29 | @SerializeWith(NodeSerializationStrategy.class) 30 | public void createNode(Invocation invocation, Output result) 31 | { 32 | Node node = invocation.getDB().createNode(); 33 | 34 | setProperties(node, invocation.getContent()); 35 | 36 | result.createdAt(url.reverse(node), node); 37 | } 38 | 39 | @GET 40 | @Path(UrlReverseLookerUpper.PATH_NODE) 41 | @SerializeWith(NodeSerializationStrategy.class) 42 | public void getNode(Invocation invocation, Output result) 43 | { 44 | result.ok( invocation.getDB().getNodeById(getNodeId(invocation)) ); 45 | } 46 | 47 | @DELETE 48 | @Transactional 49 | @Path(UrlReverseLookerUpper.PATH_NODE) 50 | public void deleteNode(Invocation invocation, Output result) 51 | { 52 | invocation.getDB().getNodeById(getNodeId(invocation)).delete(); 53 | result.ok(); 54 | } 55 | 56 | @PUT 57 | @Transactional 58 | @Path(UrlReverseLookerUpper.PATH_NODE_PROPERTIES) 59 | @DeserializeWith(PropertyContainerDeserializationStrategy.class) 60 | public void setAllNodeProperties(Invocation invocation, Output result) 61 | { 62 | Node node = invocation.getDB().getNodeById(getNodeId(invocation)); 63 | 64 | removeAllProperties(node); 65 | setProperties(node, invocation.getContent()); 66 | 67 | result.okNoContent(); 68 | } 69 | 70 | @GET 71 | @Path(UrlReverseLookerUpper.PATH_NODE_PROPERTIES) 72 | @SerializeWith(PropertyContainerSerializationStrategy.class) 73 | public void getAllNodeProperties(Invocation invocation, Output result) 74 | { 75 | result.ok(invocation.getDB().getNodeById(getNodeId(invocation))); 76 | } 77 | 78 | @PUT 79 | @Transactional 80 | @Path(UrlReverseLookerUpper.PATH_NODE_PROPERTY) 81 | @DeserializeWith(PropertyValueDeserializationStrategy.class) 82 | public void setNodeProperty(Invocation invocation, Output result) 83 | { 84 | Node node = invocation.getDB().getNodeById(getNodeId(invocation)); 85 | 86 | node.setProperty(invocation.getStringParameter(UrlReverseLookerUpper.PROPERTY_KEY_NAME), invocation.getContent()); 87 | 88 | result.okNoContent(); 89 | } 90 | 91 | @GET 92 | @Path(UrlReverseLookerUpper.PATH_NODE_PROPERTY) 93 | @SerializeWith(PropertyValueSerializationStrategy.class) 94 | public void getNodeProperty(Invocation invocation, Output result) 95 | { 96 | Node node = invocation.getDB().getNodeById(getNodeId(invocation)); 97 | result.ok(node.getProperty(invocation.getStringParameter(UrlReverseLookerUpper.PROPERTY_KEY_NAME))); 98 | } 99 | 100 | @DELETE 101 | @Transactional 102 | @Path(UrlReverseLookerUpper.PATH_NODE_PROPERTY) 103 | public void deleteNodeProperty(Invocation invocation, Output result) 104 | { 105 | Node node = invocation.getDB().getNodeById(getNodeId(invocation)); 106 | node.removeProperty(invocation.getStringParameter(UrlReverseLookerUpper.PROPERTY_KEY_NAME)); 107 | result.ok(); 108 | } 109 | 110 | @DELETE 111 | @Transactional 112 | @Path(UrlReverseLookerUpper.PATH_NODE_PROPERTIES) 113 | public void deleteAllNodeProperties(Invocation invocation, Output result) 114 | { 115 | Node node = invocation.getDB().getNodeById(getNodeId(invocation)); 116 | removeAllProperties(node); 117 | result.ok(); 118 | } 119 | } -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/api/RestService.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.api; 2 | 3 | import java.net.URI; 4 | import java.net.URISyntaxException; 5 | import java.util.Arrays; 6 | import java.util.Collections; 7 | import java.util.LinkedHashSet; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | import javax.ws.rs.core.MediaType; 12 | 13 | import org.neo4j.server.rest.paging.LeaseManager; 14 | import org.neo4j.server.rest.paging.RealClock; 15 | import org.neo4j.server.rest.repr.BadInputException; 16 | import org.neo4j.server.rest.repr.ExtensionInjector; 17 | import org.neo4j.server.rest.repr.OutputFormat; 18 | import org.neo4j.server.rest.repr.RepresentationFormatRepository; 19 | import org.neo4j.smack.pipeline.database.event.Invocation; 20 | 21 | /** 22 | * @author mh 23 | * @since 11.12.11 24 | */ 25 | public class RestService 26 | { 27 | private static final LeaseManager leaseManager = new LeaseManager(new RealClock()); 28 | public static final String NODE_ID_NAME = "nodeId"; 29 | protected static final String PATH_NODES = "node"; 30 | protected static final String PATH_NODE = PATH_NODES + "/{" + NODE_ID_NAME + "}"; 31 | public static final String RELATIONSHIP_ID_NAME = "relationshipId"; 32 | protected static final String PATH_NODE_RELATIONSHIPS = PATH_NODE + "/relationships"; 33 | protected static final String PATH_RELATIONSHIP = "relationship/{" + RELATIONSHIP_ID_NAME + "}"; 34 | RepresentationFormatRepository repository = new RepresentationFormatRepository(new ExtensionInjector() { 35 | @Override 36 | public Map> getExensionsFor(Class aClass) { 37 | return Collections.emptyMap(); 38 | } 39 | }); 40 | 41 | private final String dataPath; 42 | 43 | public RestService(String dataPath) { 44 | this.dataPath = dataPath; 45 | } 46 | 47 | long extractId(Object uri) throws BadInputException { 48 | try { 49 | return Long.parseLong(uri.toString().substring(uri.toString().lastIndexOf("/") + 1)); 50 | } catch (NumberFormatException ex) { 51 | throw new BadInputException(ex); 52 | } catch (NullPointerException ex) { 53 | throw new BadInputException(ex); 54 | } 55 | } 56 | 57 | protected OutputFormat createOutputFormat(Invocation invocation) throws URISyntaxException { 58 | final URI uri = new URI(dataPath); 59 | return repository.outputFormat(Arrays.asList(MediaType.APPLICATION_JSON_TYPE), uri); 60 | } 61 | 62 | protected Long getNodeId(Invocation invocation) { 63 | return getLongParameter(invocation, NODE_ID_NAME); 64 | } 65 | protected Long getRelationshipId(Invocation invocation) { 66 | return getLongParameter(invocation, RELATIONSHIP_ID_NAME); 67 | } 68 | 69 | protected Long getLongParameter(Invocation invocation, String key) { 70 | return invocation.getLongParameter(key, -1l); 71 | } 72 | protected Long getLongParameter(Invocation invocation, String key, Long defaultValue) { 73 | final Long value = getLongParameter(invocation, key); 74 | return value == null ? defaultValue : value; 75 | } 76 | 77 | protected String getParameter(Invocation invocation, String key) { 78 | return invocation.getPathVariables().getParameter(key); 79 | } 80 | 81 | protected Long getId(Invocation invocation) { 82 | return getLongParameter(invocation, "id"); 83 | } 84 | 85 | 86 | protected String getKey(Invocation invocation) { 87 | return getParameter(invocation, "key"); 88 | } 89 | 90 | @SuppressWarnings("unchecked") 91 | protected Map readMap(Invocation invocation) { 92 | return invocation.getContent(Map.class); 93 | } 94 | 95 | @SuppressWarnings("serial") 96 | public static class AmpersandSeparatedCollection extends LinkedHashSet { 97 | public AmpersandSeparatedCollection(String path) { 98 | for (String e : path.split("&")) { 99 | if (e.trim() 100 | .length() > 0) { 101 | add(e); 102 | } 103 | } 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/api/TransactionService.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2002-2011 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | package org.neo4j.smack.api; 21 | 22 | import javax.ws.rs.POST; 23 | import javax.ws.rs.PUT; 24 | import javax.ws.rs.Path; 25 | 26 | import org.neo4j.smack.pipeline.database.TransactionRegistry; 27 | import org.neo4j.smack.pipeline.database.event.Invocation; 28 | import org.neo4j.smack.pipeline.database.event.Output; 29 | import org.neo4j.smack.routing.annotation.DeserializeWith; 30 | import org.neo4j.smack.routing.annotation.Transactional; 31 | import org.neo4j.smack.serialization.strategy.TransactionStateDeserialization; 32 | import org.neo4j.smack.serialization.strategy.TransactionStateDeserializationStrategy; 33 | 34 | public class TransactionService { 35 | 36 | private static final UrlReverseLookerUpper url = new UrlReverseLookerUpper(); 37 | 38 | @POST 39 | @Path("") 40 | public void createTransaction(Invocation req, Output res) { 41 | TransactionRegistry txs = req.getTxRegistry(); 42 | Long txId = req.getTxId(); 43 | 44 | txs.createTransaction(txId); 45 | 46 | res.createdAt(url.reverseTransaction(txId)); 47 | } 48 | 49 | @PUT 50 | @Path("/{tx_id}/state") 51 | @Transactional 52 | @DeserializeWith(TransactionStateDeserializationStrategy.class) 53 | public void setTransactionState(Invocation req, Output res) throws Exception { 54 | TransactionRegistry txs = req.getTxRegistry(); 55 | 56 | switch(req.getContent()) { 57 | case COMMITTED: 58 | txs.commitCurrentTransaction(); 59 | break; 60 | case ROLLED_BACK: 61 | txs.rollbackCurrentTransaction(); 62 | break; 63 | default: 64 | throw new IllegalArgumentException("Only COMMITTED and ROLLED_BACK transaction states can be set."); 65 | } 66 | 67 | res.ok(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/api/UrlReverseLookerUpper.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.api; 2 | 3 | import org.neo4j.graphdb.Node; 4 | import org.neo4j.graphdb.Relationship; 5 | 6 | /** 7 | * Central authority for url lookups. 8 | * Dead simple right now, but to be expanded down the road. 9 | * 10 | * TODO: Perhaps move path constants into their own class? 11 | */ 12 | public class UrlReverseLookerUpper { 13 | 14 | // 15 | // Path variable keys 16 | // 17 | 18 | 19 | public static final String PROPERTY_KEY_NAME = "key"; 20 | 21 | public static final String NODE_ID_NAME = "node_id"; 22 | 23 | public static final String RELATIONSHIP_ID_NAME = "relationship_id"; 24 | public static final String RELATIONSHIP_DIRECTION_NAME = "direction"; 25 | public static final String RELATIONSHIP_TYPES_NAME = "types"; 26 | 27 | public static final String PATH_NODES = "node"; 28 | public static final String PATH_NODE = PATH_NODES + "/{" + NODE_ID_NAME + "}"; 29 | public static final String PATH_NODE_PROPERTIES = PATH_NODE + "/properties"; 30 | public static final String PATH_NODE_PROPERTY = PATH_NODE_PROPERTIES + "/{" + PROPERTY_KEY_NAME + "}"; 31 | 32 | // 33 | // Paths 34 | // 35 | 36 | public static final String PATH_NODE_RELATIONSHIPS = PATH_NODE + "/relationships"; 37 | public static final String PATH_RELATIONSHIP = "relationship/{" + RELATIONSHIP_ID_NAME + "}"; 38 | 39 | public static final String PATH_NODE_RELATIONSHIPS_W_DIR = PATH_NODE_RELATIONSHIPS + "/{"+RELATIONSHIP_DIRECTION_NAME+"}"; 40 | public static final String PATH_NODE_RELATIONSHIPS_W_DIR_N_TYPES = PATH_NODE_RELATIONSHIPS_W_DIR + "/{"+RELATIONSHIP_TYPES_NAME+"}"; 41 | public static final String PATH_RELATIONSHIP_PROPERTIES = PATH_RELATIONSHIP + "/properties"; 42 | public static final String PATH_RELATIONSHIP_PROPERTY = PATH_RELATIONSHIP_PROPERTIES + "/{"+PROPERTY_KEY_NAME+"}"; 43 | 44 | public String reverse(Node node) 45 | { 46 | return "/db/data/node/" + node.getId(); 47 | } 48 | 49 | public String reverse(Relationship rel) 50 | { 51 | return "/db/data/relationship/" + rel.getId(); 52 | } 53 | 54 | public String reverseTransaction(Long txId) 55 | { 56 | return "/db/data/tx/" + txId; 57 | } 58 | 59 | // TODO: This is used by deserializers, 60 | // see if we can write a special deserializer 61 | // that extracts this id without creating garbage. 62 | public static long nodeId(String string) 63 | { 64 | String [] parts = string.split("\\/"); 65 | return Long.valueOf(parts[parts.length-1]); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/gcfree/MutableString.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.gcfree; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * A mutable String implementation. Uses more memory than 7 | * native java strings and is slower, but can be recycled. 8 | * 9 | * In high-throughput applications, this can be very useful 10 | * as the performance impact of GC is high, and we can pool 11 | * mutable string objects, so we don't need GC. 12 | */ 13 | public class MutableString { 14 | 15 | private char [] chars; 16 | private int length = 0; 17 | 18 | public MutableString(String string) 19 | { 20 | chars = string.toCharArray(); 21 | length = chars.length; 22 | } 23 | 24 | public MutableString(int initialCapacity) 25 | { 26 | chars = new char[initialCapacity]; 27 | length = 0; 28 | } 29 | 30 | public void append(char character) 31 | { 32 | try { 33 | chars[length++] = character; 34 | } catch(ArrayIndexOutOfBoundsException e) { 35 | ensureCapacity(length); 36 | chars[length] = character; 37 | } 38 | } 39 | 40 | public void setTo(MutableString value) 41 | { 42 | ensureCapacity(value.length); 43 | length = value.length; 44 | char [] other = value.chars; 45 | for(int i=0;i=l;i--) { 23 | digit = Character.digit(chars[i], 10); 24 | 25 | if(digit != -1) { 26 | longValue = longValue + (digit * multiplier); 27 | multiplier *= 10; 28 | } else { 29 | throw new NumberFormatException("I don't know how to convert " + value + " to a long."); 30 | } 31 | } 32 | return longValue; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/pipeline/CombinedHandler.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.pipeline; 2 | 3 | import com.lmax.disruptor.WorkHandler; 4 | 5 | public class CombinedHandler implements WorkHandler { 6 | 7 | private WorkHandler[] handlers; 8 | 9 | public CombinedHandler(WorkHandler ... handlers) { 10 | this.handlers = handlers; 11 | } 12 | 13 | @Override 14 | public void onEvent(E event) throws Exception 15 | { 16 | for(WorkHandler handler : handlers) 17 | { 18 | handler.onEvent(event); 19 | } 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/pipeline/DaemonThreadFactory.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.pipeline; 2 | 3 | import java.util.concurrent.ThreadFactory; 4 | 5 | /** 6 | * @author mh 7 | * @since 14.11.11 8 | */ 9 | public class DaemonThreadFactory implements ThreadFactory { 10 | 11 | private String baseName; 12 | private int threadNo = 0; 13 | 14 | public DaemonThreadFactory(String threadBaseName) { 15 | this.baseName = threadBaseName; 16 | } 17 | 18 | @Override 19 | public Thread newThread(Runnable runnable) { 20 | final Thread thread = new Thread(runnable); 21 | thread.setName(baseName + "-" + threadNo++); 22 | thread.setDaemon(true); 23 | return thread; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/pipeline/DefaultExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.pipeline; 2 | 3 | import org.neo4j.smack.pipeline.event.Fallible; 4 | 5 | import com.lmax.disruptor.ExceptionHandler; 6 | 7 | public class DefaultExceptionHandler implements ExceptionHandler { 8 | 9 | @Override 10 | public void handleEventException(Throwable ex, long sequence, Object event) 11 | { 12 | ex.printStackTrace(); 13 | if(event instanceof Fallible) { 14 | ((Fallible)event).setFailed(ex); 15 | } 16 | } 17 | 18 | @Override 19 | public void handleOnStartException(Throwable ex) 20 | { 21 | ex.printStackTrace(); 22 | } 23 | 24 | @Override 25 | public void handleOnShutdownException(Throwable ex) 26 | { 27 | ex.printStackTrace(); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/pipeline/RingBufferWorkPipeline.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.pipeline; 2 | 3 | /** 4 | * @author mh 5 | * @since 27.11.11 6 | */ 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import java.util.concurrent.ExecutorService; 11 | import java.util.concurrent.Executors; 12 | import java.util.concurrent.atomic.AtomicLong; 13 | 14 | import com.lmax.disruptor.BusySpinWaitStrategy; 15 | import com.lmax.disruptor.EventFactory; 16 | import com.lmax.disruptor.ExceptionHandler; 17 | import com.lmax.disruptor.MultiThreadedClaimStrategy; 18 | import com.lmax.disruptor.RingBuffer; 19 | import com.lmax.disruptor.SequenceBarrier; 20 | import com.lmax.disruptor.Sequencer; 21 | import com.lmax.disruptor.WorkHandler; 22 | import com.lmax.disruptor.WorkProcessor; 23 | 24 | 25 | public class RingBufferWorkPipeline { 26 | 27 | protected RingBuffer ringBuffer; 28 | 29 | private final ExceptionHandler exceptionHandler; 30 | 31 | private List> processors = new ArrayList>(); 32 | 33 | private ExecutorService workers; 34 | 35 | private final List> handlers = new ArrayList>(); 36 | 37 | private final EventFactory eventFactory; 38 | 39 | private String nameForThreads; 40 | 41 | private int bufferSize; 42 | 43 | public RingBufferWorkPipeline(String nameForThreads, final EventFactory eventFactory, final ExceptionHandler exceptionHandler, int bufferSize) { 44 | this.nameForThreads = nameForThreads; 45 | this.eventFactory = eventFactory; 46 | this.exceptionHandler = exceptionHandler; 47 | this.bufferSize = bufferSize; 48 | } 49 | 50 | public void start() { 51 | if (handlers.isEmpty()) throw new IllegalStateException("No Handlers configured on Pipeline"); 52 | final int numEventProcessors = handlers.size(); 53 | workers = Executors.newFixedThreadPool(numEventProcessors, new DaemonThreadFactory(nameForThreads)); 54 | 55 | ringBuffer = new RingBuffer( 56 | eventFactory, 57 | new MultiThreadedClaimStrategy(bufferSize), 58 | new BusySpinWaitStrategy()); 59 | 60 | WorkProcessor processor = null; 61 | for (WorkHandler handler : handlers) 62 | { 63 | processor = scheduleEventProcessor(processor, handler); 64 | processors.add(processor); 65 | } 66 | ringBuffer.setGatingSequences(processor.getSequence()); 67 | 68 | } 69 | 70 | private WorkProcessor scheduleEventProcessor(WorkProcessor predecessor, WorkHandler handler) 71 | { 72 | WorkProcessor newProcessor = new WorkProcessor(ringBuffer, barrierFor(predecessor), handler, exceptionHandler, newSequence()); 73 | workers.submit(newProcessor); 74 | return newProcessor; 75 | } 76 | 77 | private SequenceBarrier barrierFor(WorkProcessor predecessor) { 78 | if (predecessor == null) return ringBuffer.newBarrier(); 79 | return ringBuffer.newBarrier(predecessor.getSequence()); 80 | } 81 | 82 | private AtomicLong newSequence() { 83 | return new AtomicLong(Sequencer.INITIAL_CURSOR_VALUE); 84 | } 85 | 86 | public void stop() { 87 | for (WorkProcessor processor : processors) { 88 | processor.halt(); 89 | } 90 | workers.shutdown(); 91 | } 92 | 93 | public RingBuffer getRingBuffer() { 94 | return ringBuffer; 95 | } 96 | 97 | public void addHandler(WorkHandler handler) 98 | { 99 | handlers.add(handler); 100 | } 101 | 102 | } -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/pipeline/core/CoreWorkPipeline.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.pipeline.core; 2 | 3 | import org.jboss.netty.buffer.ChannelBuffer; 4 | import org.jboss.netty.channel.Channel; 5 | import org.neo4j.smack.pipeline.CombinedHandler; 6 | import org.neo4j.smack.pipeline.RingBufferWorkPipeline; 7 | import org.neo4j.smack.pipeline.core.event.CorePipelineEvent; 8 | import org.neo4j.smack.routing.InvocationVerb; 9 | 10 | import com.lmax.disruptor.ExceptionHandler; 11 | 12 | public class CoreWorkPipeline extends RingBufferWorkPipeline implements WorkPublisher { 13 | 14 | @SuppressWarnings("unchecked") 15 | public CoreWorkPipeline(ExceptionHandler exceptionHandler, 16 | RoutingHandler routingHandler, DeserializationHandler deserializationHandler, 17 | TransactionPreparationHandler tranasctionPreparationHandler, WorkDivisionHandler workDivisionHandler) 18 | { 19 | super("CoreWorkEventHandler", CorePipelineEvent.FACTORY, exceptionHandler, 1024 * 4); 20 | addHandler(new CombinedHandler( 21 | routingHandler, 22 | deserializationHandler)); 23 | addHandler(new CombinedHandler( 24 | tranasctionPreparationHandler, 25 | workDivisionHandler)); 26 | } 27 | 28 | @Override 29 | public void addWork(Long connectionId, InvocationVerb verb, String path, 30 | ChannelBuffer content, Channel channel, boolean keepAlive) 31 | { 32 | long sequenceNo = ringBuffer.next(); 33 | CorePipelineEvent event = ringBuffer.get(sequenceNo); 34 | 35 | event.reset(connectionId, verb, path, content, channel, keepAlive); 36 | 37 | ringBuffer.publish(sequenceNo); 38 | } 39 | 40 | @Override 41 | public void addFailure(Long connectionId, Channel channel, Throwable cause) 42 | { 43 | long sequenceNo = ringBuffer.next(); 44 | CorePipelineEvent event = ringBuffer.get(sequenceNo); 45 | 46 | event.reset(connectionId, channel, cause); 47 | 48 | ringBuffer.publish(sequenceNo); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/pipeline/core/DeserializationHandler.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2002-2011 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | package org.neo4j.smack.pipeline.core; 21 | 22 | import org.neo4j.smack.pipeline.core.event.CorePipelineEvent; 23 | import org.neo4j.smack.serialization.Deserializer; 24 | import org.neo4j.smack.serialization.SerializationFactory; 25 | 26 | import com.lmax.disruptor.WorkHandler; 27 | 28 | 29 | public class DeserializationHandler implements WorkHandler { 30 | 31 | SerializationFactory serializationFactory = new SerializationFactory(); 32 | 33 | public void onEvent(final CorePipelineEvent event) 34 | throws Exception { 35 | if(!event.hasFailed()) { 36 | Deserializer d = serializationFactory.getDeserializer(event.getInputBuffer()); 37 | event.setDeserializedContent(event.getEndpoint().getDeserializationStrategy().deserialize(d)); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/pipeline/core/RoutingHandler.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2002-2011 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | package org.neo4j.smack.pipeline.core; 21 | 22 | import org.neo4j.smack.pipeline.core.event.CorePipelineEvent; 23 | import org.neo4j.smack.routing.Router; 24 | 25 | import com.lmax.disruptor.WorkHandler; 26 | 27 | public class RoutingHandler implements WorkHandler { 28 | 29 | private Router router; 30 | 31 | public RoutingHandler(Router router) { 32 | this.router = router; 33 | } 34 | 35 | public void onEvent(final CorePipelineEvent event) throws Exception 36 | { 37 | if(!event.hasFailed()) 38 | { 39 | event.setEndpoint(router.route(event)); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/pipeline/core/TransactionPreparationHandler.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2002-2011 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | package org.neo4j.smack.pipeline.core; 21 | 22 | import org.neo4j.smack.pipeline.core.event.CorePipelineEvent; 23 | 24 | import com.lmax.disruptor.WorkHandler; 25 | 26 | 27 | public class TransactionPreparationHandler implements WorkHandler 28 | { 29 | private WorkTransactionPreparer txPrepare = new WorkTransactionPreparer(); 30 | 31 | @Override 32 | public void onEvent(CorePipelineEvent event) 33 | { 34 | if(!event.hasFailed()) 35 | { 36 | txPrepare.prepare(event); 37 | } 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/pipeline/core/WorkDivisionHandler.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2002-2011 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | package org.neo4j.smack.pipeline.core; 21 | 22 | import org.apache.log4j.Logger; 23 | import org.neo4j.graphdb.GraphDatabaseService; 24 | import org.neo4j.smack.pipeline.core.event.CorePipelineEvent; 25 | import org.neo4j.smack.pipeline.database.DatabaseWorkPipeline; 26 | import org.neo4j.smack.pipeline.database.ThreadTransactionManagement; 27 | import org.neo4j.smack.pipeline.database.TransactionRegistry; 28 | 29 | import com.lmax.disruptor.ExceptionHandler; 30 | import com.lmax.disruptor.WorkHandler; 31 | 32 | /** 33 | * Takes a prepared RequestEvent, packages it into DatabaseWork, and 34 | * assigns that work to a worker based on the connection ID (same connection 35 | * always goes to the same worker). 36 | */ 37 | public class WorkDivisionHandler implements WorkHandler { 38 | 39 | private final static Logger logger = Logger.getLogger(WorkDivisionHandler.class); 40 | 41 | private static final int NUM_DATABASE_WORK_EXECUTORS = 4; 42 | 43 | private final GraphDatabaseService database; 44 | private final DatabaseWorkPipeline[] workers = new DatabaseWorkPipeline[NUM_DATABASE_WORK_EXECUTORS]; 45 | private final ExceptionHandler exceptionHandler; 46 | 47 | public WorkDivisionHandler(GraphDatabaseService database, ExceptionHandler exceptionHandler) { 48 | this.database = database; 49 | this.exceptionHandler = exceptionHandler; 50 | start(); 51 | } 52 | 53 | @Override 54 | public void onEvent(CorePipelineEvent event) 55 | { 56 | int workerId = (int) (event.getConnectionId() % NUM_DATABASE_WORK_EXECUTORS); 57 | workers[workerId].addWork(event); 58 | } 59 | 60 | public void stop() { 61 | for (DatabaseWorkPipeline worker : workers) { 62 | stopWorker(worker); 63 | } 64 | } 65 | 66 | private void start() { 67 | for (int i = 0; i < NUM_DATABASE_WORK_EXECUTORS; i++) { 68 | TransactionRegistry txs = new TransactionRegistry(database); 69 | ThreadTransactionManagement txManage = new ThreadTransactionManagement(txs); 70 | DatabaseWorkPipeline worker = new DatabaseWorkPipeline(database, txs, txManage, exceptionHandler); 71 | workers[i] = worker; 72 | worker.start(); 73 | } 74 | } 75 | 76 | private void stopWorker(DatabaseWorkPipeline worker) { 77 | try { 78 | if (worker == null) { 79 | logger.warn("Worker is null"); 80 | return; 81 | } 82 | worker.stop(); 83 | } catch (Exception e) { 84 | logger.error("Error stopping worker", e); 85 | } 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/pipeline/core/WorkPublisher.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.pipeline.core; 2 | 3 | import org.jboss.netty.buffer.ChannelBuffer; 4 | import org.jboss.netty.channel.Channel; 5 | import org.neo4j.smack.routing.InvocationVerb; 6 | 7 | public interface WorkPublisher { 8 | 9 | void addWork(Long connectionId, InvocationVerb verb, String path, 10 | ChannelBuffer content, Channel channel, boolean keepAlive); 11 | 12 | void addFailure(Long connectionId, Channel channel, Throwable cause); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/pipeline/core/WorkTransactionPreparer.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.pipeline.core; 2 | 3 | import org.neo4j.smack.pipeline.core.event.TransactionWork; 4 | import org.neo4j.smack.pipeline.event.WorkTransactionMode; 5 | 6 | 7 | public class WorkTransactionPreparer { 8 | 9 | private long txIds = 0l; 10 | 11 | public void prepare(TransactionWork event) 12 | { 13 | WorkTransactionMode txMode = WorkTransactionMode.NO_TRANSACTION; 14 | 15 | if(event.isTransactional()) 16 | { 17 | txMode = WorkTransactionMode.OPEN_TRANSACTION; 18 | } 19 | 20 | long txId = event.getTransactionId(); 21 | 22 | // Did the request come in with a tx id? 23 | if (txId == -1l) 24 | { 25 | // Nope, generate one 26 | txId = txIds ++; 27 | 28 | // If this seemed like an open transaction, since client 29 | // did not provide a tx id, it is a single transaction. 30 | if(txMode == WorkTransactionMode.OPEN_TRANSACTION) { 31 | txMode = WorkTransactionMode.SINGLE_TRANSACTION; 32 | } 33 | 34 | event.setTransactionId(txId); 35 | } 36 | 37 | event.setTransactionMode(txMode); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/pipeline/core/event/TransactionWork.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.pipeline.core.event; 2 | 3 | import org.neo4j.smack.pipeline.event.WorkTransactionMode; 4 | 5 | 6 | public interface TransactionWork { 7 | 8 | boolean isTransactional(); 9 | 10 | long getTransactionId(); 11 | 12 | void setTransactionId(Long txId); 13 | 14 | WorkTransactionMode getTransactionMode(); 15 | 16 | void setTransactionMode(WorkTransactionMode txMode); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/pipeline/database/DatabaseWorkPerformer.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.pipeline.database; 2 | 3 | import org.neo4j.smack.pipeline.database.event.DatabaseWork; 4 | 5 | import com.lmax.disruptor.WorkHandler; 6 | 7 | /** 8 | * Calls work.perform() from within 9 | * an appropriate transactional context. 10 | */ 11 | public class DatabaseWorkPerformer implements WorkHandler { 12 | 13 | @Override 14 | public void onEvent(DatabaseWork work) throws Exception 15 | { 16 | try 17 | { 18 | work.perform(); 19 | } catch(Exception e) { 20 | // TODO: Logging 21 | e.printStackTrace(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/pipeline/database/DatabaseWorkPipeline.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.pipeline.database; 2 | 3 | import org.neo4j.graphdb.GraphDatabaseService; 4 | import org.neo4j.smack.pipeline.RingBufferWorkPipeline; 5 | import org.neo4j.smack.pipeline.core.event.CorePipelineEvent; 6 | import org.neo4j.smack.pipeline.database.event.DatabaseWork; 7 | 8 | import com.lmax.disruptor.ExceptionHandler; 9 | 10 | public class DatabaseWorkPipeline extends RingBufferWorkPipeline 11 | { 12 | 13 | // Each database work pipeline keeps track of its own transactions 14 | private TransactionRegistry txs; 15 | private GraphDatabaseService database; 16 | 17 | private final ThreadTransactionManagement txManage; 18 | 19 | public DatabaseWorkPipeline(GraphDatabaseService database, TransactionRegistry txs, ThreadTransactionManagement txManage, 20 | ExceptionHandler exceptionHandler) 21 | { 22 | super("DatabaseWorkHandler", DatabaseWork.FACTORY, exceptionHandler, 512); 23 | this.txs = txs; 24 | this.txManage = txManage; 25 | this.database = database; 26 | 27 | addHandler(new DatabaseWorkPerformer()); 28 | } 29 | 30 | public void addWork(CorePipelineEvent event) 31 | { 32 | 33 | long sequenceId = ringBuffer.next(); 34 | DatabaseWork work = ringBuffer.get(sequenceId); 35 | 36 | if(!event.hasFailed()) { 37 | work.reset( 38 | event.getEndpoint(), 39 | event.getChannel(), 40 | event.getIsPersistentConnection(), 41 | event.getPath(), 42 | event.getTransactionId(), 43 | event.getTransactionMode(), 44 | event.getPathVariables(), 45 | event.getDeserializedContent(), 46 | database, 47 | txs, 48 | txManage); 49 | } else { 50 | work.reset( 51 | event.getChannel(), 52 | event.getIsPersistentConnection(), 53 | event.getTransactionId(), 54 | event.getTransactionMode(), 55 | database, 56 | txs, 57 | txManage, 58 | event.getFailureCause()); 59 | } 60 | 61 | ringBuffer.publish(sequenceId); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/pipeline/database/ExceptionOutputWriter.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.pipeline.database; 2 | 3 | import java.lang.reflect.InvocationTargetException; 4 | import java.util.Collections; 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | import javax.management.relation.RelationNotFoundException; 9 | 10 | import org.jboss.netty.handler.codec.http.HttpResponseStatus; 11 | import org.neo4j.graphdb.NotFoundException; 12 | import org.neo4j.server.rest.domain.EndNodeNotFoundException; 13 | import org.neo4j.server.rest.domain.StartNodeNotFoundException; 14 | import org.neo4j.server.rest.repr.BadInputException; 15 | import org.neo4j.server.rest.web.NoSuchPropertyException; 16 | import org.neo4j.server.rest.web.NodeNotFoundException; 17 | import org.neo4j.server.rest.web.OperationFailureException; 18 | import org.neo4j.server.rest.web.PropertyValueException; 19 | import org.neo4j.server.rest.web.RelationshipNotFoundException; 20 | import org.neo4j.smack.pipeline.database.event.NettyChannelBackedOutput; 21 | import org.neo4j.smack.routing.ResourceNotFoundException; 22 | import org.neo4j.smack.serialization.SerializationStrategy; 23 | import org.neo4j.smack.serialization.strategy.ExceptionSerializationStrategy; 24 | 25 | /** 26 | * @author mh 27 | * @since 05.12.11 28 | */ 29 | public class ExceptionOutputWriter { 30 | 31 | private static final SerializationStrategy exceptionSerializationStrategy = new ExceptionSerializationStrategy(); 32 | 33 | private static final Map, HttpResponseStatus> exceptionToStatusMap = Collections.unmodifiableMap(new HashMap, HttpResponseStatus>() 34 | { 35 | 36 | private static final long serialVersionUID = -5937199711856466595L; 37 | 38 | { 39 | put(ResourceNotFoundException.class, HttpResponseStatus.NOT_FOUND); 40 | put(NodeNotFoundException.class, HttpResponseStatus.NOT_FOUND); 41 | put(RelationNotFoundException.class, HttpResponseStatus.NOT_FOUND); 42 | put(ArrayStoreException.class, HttpResponseStatus.BAD_REQUEST); 43 | put(BadInputException.class, HttpResponseStatus.BAD_REQUEST); 44 | put(OperationFailureException.class, HttpResponseStatus.INTERNAL_SERVER_ERROR); 45 | put(NoSuchPropertyException.class, HttpResponseStatus.NOT_FOUND); 46 | put(RelationshipNotFoundException.class, HttpResponseStatus.NOT_FOUND); 47 | put(ClassCastException.class, HttpResponseStatus.BAD_REQUEST); 48 | put(StartNodeNotFoundException.class, HttpResponseStatus.NOT_FOUND); 49 | put(EndNodeNotFoundException.class, HttpResponseStatus.NOT_FOUND); 50 | put(PropertyValueException.class, HttpResponseStatus.BAD_REQUEST); 51 | put(UnsupportedOperationException.class, HttpResponseStatus.METHOD_NOT_ALLOWED); 52 | put(NotFoundException.class, HttpResponseStatus.NOT_FOUND); 53 | put(IllegalArgumentException.class, HttpResponseStatus.BAD_REQUEST); 54 | 55 | } 56 | }); 57 | 58 | public void write(NettyChannelBackedOutput output, Throwable e) 59 | { 60 | output.send(getErrorStatus(e), e, null, exceptionSerializationStrategy); 61 | } 62 | 63 | private HttpResponseStatus getErrorStatus(Throwable ex) { 64 | if (ex instanceof InvocationTargetException) { 65 | ex = ((InvocationTargetException)ex).getTargetException(); 66 | } 67 | for (Map.Entry, HttpResponseStatus> entry : exceptionToStatusMap.entrySet()) { 68 | if (entry.getKey().isInstance(ex)) return entry.getValue(); 69 | } 70 | return HttpResponseStatus.INTERNAL_SERVER_ERROR; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/pipeline/database/ThreadTransactionManagement.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.pipeline.database; 2 | 3 | import javax.transaction.HeuristicMixedException; 4 | import javax.transaction.HeuristicRollbackException; 5 | import javax.transaction.InvalidTransactionException; 6 | import javax.transaction.RollbackException; 7 | import javax.transaction.SystemException; 8 | 9 | import org.neo4j.smack.pipeline.event.WorkTransactionMode; 10 | 11 | public class ThreadTransactionManagement { 12 | 13 | private final TransactionRegistry txs; 14 | 15 | public ThreadTransactionManagement(TransactionRegistry txs) 16 | { 17 | this.txs = txs; 18 | } 19 | 20 | public void beforeWork(WorkTransactionMode txMode, long txId) throws SystemException, InvalidTransactionException 21 | { 22 | switch(txMode) 23 | { 24 | case OPEN_TRANSACTION: 25 | txs.selectCurrentTransaction(txId); 26 | break; 27 | 28 | case SINGLE_TRANSACTION: 29 | txs.createTransaction(txId); 30 | txs.selectCurrentTransaction(txId); 31 | break; 32 | 33 | case NO_TRANSACTION: 34 | txs.suspendCurrentTransaction(); 35 | break; 36 | } 37 | } 38 | 39 | public void afterWork(WorkTransactionMode txMode, long txId) throws InvalidTransactionException, RollbackException, SystemException, IllegalStateException, SecurityException, HeuristicMixedException, HeuristicRollbackException 40 | { 41 | switch(txMode) 42 | { 43 | case SINGLE_TRANSACTION: 44 | txs.commitCurrentTransaction(); 45 | break; 46 | 47 | case OPEN_TRANSACTION: 48 | case NO_TRANSACTION: 49 | break; 50 | } 51 | } 52 | 53 | public void onWorkFailure(WorkTransactionMode txMode, long txId) throws InvalidTransactionException, IllegalStateException, SecurityException, HeuristicMixedException, HeuristicRollbackException, RollbackException, SystemException 54 | { 55 | switch(txMode) 56 | { 57 | case OPEN_TRANSACTION: 58 | case SINGLE_TRANSACTION: 59 | txs.rollbackCurrentTransaction(); 60 | break; 61 | 62 | case NO_TRANSACTION: 63 | break; 64 | } 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/pipeline/database/event/DefaultInvocationImpl.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2002-2011 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | package org.neo4j.smack.pipeline.database.event; 21 | 22 | import org.neo4j.graphdb.GraphDatabaseService; 23 | import org.neo4j.smack.pipeline.database.TransactionRegistry; 24 | import org.neo4j.smack.routing.PathVariables; 25 | 26 | /** 27 | * Note: There are lots of these instances, keep it as slim as possible to 28 | * keep memory usage down. 29 | */ 30 | public class DefaultInvocationImpl implements Invocation { 31 | 32 | private PathVariables pathVariables; 33 | private Object content; 34 | 35 | private GraphDatabaseService database; 36 | private TransactionRegistry txRegistry; 37 | private long txId = -1l; 38 | private String path; 39 | 40 | @Override 41 | public PathVariables getPathVariables() { 42 | return pathVariables; 43 | } 44 | 45 | @Override 46 | @SuppressWarnings("unchecked") 47 | public T getContent() { 48 | return (T)content; 49 | } 50 | 51 | @Override 52 | public T getContent(Class type) { 53 | if (content==null) return null; 54 | if (type.isInstance(content)) return type.cast(content); 55 | throw new ClassCastException("Expected "+type+" found "+content.getClass()); 56 | } 57 | 58 | @Override 59 | public GraphDatabaseService getDB() { 60 | return database; 61 | } 62 | 63 | @Override 64 | public TransactionRegistry getTxRegistry() { 65 | return txRegistry; 66 | } 67 | 68 | @Override 69 | public long getTxId() { 70 | return txId; 71 | } 72 | 73 | @Override 74 | public String getPath() { 75 | return path; 76 | } 77 | 78 | @Override 79 | public long getLongParameter(String name, long defaultValue) 80 | { 81 | return pathVariables.getLongParameter(name, defaultValue); 82 | } 83 | 84 | @Override 85 | public String getStringParameter(String name) 86 | { 87 | return pathVariables.getParameter(name); 88 | } 89 | 90 | @Override 91 | public String getStringParameter(String name, String defaultValue) 92 | { 93 | return pathVariables.getParameter(name, defaultValue); 94 | } 95 | 96 | protected void reset(String path, long txId, PathVariables pathVariables, Object content, GraphDatabaseService database, TransactionRegistry txRegistry) 97 | { 98 | this.path = path; 99 | this.pathVariables = pathVariables; 100 | this.content = content; 101 | 102 | this.database = database; 103 | this.txRegistry = txRegistry; 104 | this.txId = txId; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/pipeline/database/event/Invocation.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2002-2011 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | package org.neo4j.smack.pipeline.database.event; 21 | 22 | import org.neo4j.graphdb.GraphDatabaseService; 23 | import org.neo4j.smack.pipeline.database.TransactionRegistry; 24 | import org.neo4j.smack.routing.PathVariables; 25 | 26 | public interface Invocation { 27 | 28 | public PathVariables getPathVariables(); 29 | 30 | public T getContent(); 31 | 32 | public T getContent(Class type); 33 | 34 | public GraphDatabaseService getDB(); 35 | 36 | public TransactionRegistry getTxRegistry(); 37 | 38 | public long getTxId(); 39 | 40 | public String getPath(); 41 | 42 | // Path variable management 43 | 44 | public long getLongParameter(String nodeIdName, long defaultValue); 45 | 46 | public String getStringParameter(String nodePropertyKeyName); 47 | 48 | public String getStringParameter(String nodePropertyKeyName, String defaultValue); 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/pipeline/database/event/Output.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.pipeline.database.event; 2 | 3 | 4 | public interface Output { 5 | 6 | public void created(); 7 | 8 | public void created(Object value); 9 | 10 | public void createdAt(String location); 11 | 12 | public void createdAt(String location, Object value); 13 | 14 | public void ok(); 15 | 16 | public void ok(Object value); 17 | 18 | public void okNoContent(); 19 | 20 | public void okAt(String location, Object value); 21 | 22 | public void notFound(); 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/pipeline/event/Fallible.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.pipeline.event; 2 | 3 | public interface Fallible { 4 | 5 | public void setFailed(Throwable exception); 6 | 7 | public Throwable getFailureCause(); 8 | 9 | public boolean hasFailed(); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/pipeline/event/WorkTransactionMode.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.pipeline.event; 2 | 3 | public enum WorkTransactionMode { 4 | 5 | /** 6 | * Perform an invocation in a single transaction, 7 | * opened and closed specifically for this piece of 8 | * work. 9 | */ 10 | SINGLE_TRANSACTION, 11 | 12 | /** 13 | * Perform an invocation in an ongoing, already opened 14 | * transaction. 15 | */ 16 | OPEN_TRANSACTION, 17 | 18 | /** 19 | * Perform this work outside of the scope of a transaction. 20 | */ 21 | NO_TRANSACTION 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/pipeline/http/CommonHeaderValues.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.pipeline.http; 2 | 3 | import org.neo4j.smack.gcfree.MutableString; 4 | 5 | public class CommonHeaderValues { 6 | 7 | /** 8 | * {@code "base64"} 9 | */ 10 | public static final MutableString BASE64 = new MutableString("base64"); 11 | /** 12 | * {@code "binary"} 13 | */ 14 | public static final MutableString BINARY = new MutableString("binary"); 15 | /** 16 | * {@code "bytes"} 17 | */ 18 | public static final MutableString BYTES = new MutableString("bytes"); 19 | /** 20 | * {@code "charset"} 21 | */ 22 | public static final MutableString CHARSET = new MutableString("charset"); 23 | /** 24 | * {@code "chunked"} 25 | */ 26 | public static final MutableString CHUNKED = new MutableString("chunked"); 27 | /** 28 | * {@code "close"} 29 | */ 30 | public static final MutableString CLOSE = new MutableString("close"); 31 | /** 32 | * {@code "compress"} 33 | */ 34 | public static final MutableString COMPRESS = new MutableString("compress"); 35 | /** 36 | * {@code "100-continue"} 37 | */ 38 | public static final MutableString CONTINUE = new MutableString("100-continue"); 39 | /** 40 | * {@code "deflate"} 41 | */ 42 | public static final MutableString DEFLATE = new MutableString("deflate"); 43 | /** 44 | * {@code "gzip"} 45 | */ 46 | public static final MutableString GZIP = new MutableString("gzip"); 47 | /** 48 | * {@code "identity"} 49 | */ 50 | public static final MutableString IDENTITY = new MutableString("identity"); 51 | /** 52 | * {@code "keep-alive"} 53 | */ 54 | public static final MutableString KEEP_ALIVE = new MutableString("keep-alive"); 55 | /** 56 | * {@code "max-age"} 57 | */ 58 | public static final MutableString MAX_AGE = new MutableString("max-age"); 59 | /** 60 | * {@code "max-stale"} 61 | */ 62 | public static final MutableString MAX_STALE = new MutableString("max-stale"); 63 | /** 64 | * {@code "min-fresh"} 65 | */ 66 | public static final MutableString MIN_FRESH = new MutableString("min-fresh"); 67 | /** 68 | * {@code "must-revalidate"} 69 | */ 70 | public static final MutableString MUST_REVALIDATE = new MutableString("must-revalidate"); 71 | /** 72 | * {@code "no-cache"} 73 | */ 74 | public static final MutableString NO_CACHE = new MutableString("no-cache"); 75 | /** 76 | * {@code "no-store"} 77 | */ 78 | public static final MutableString NO_STORE = new MutableString("no-store"); 79 | /** 80 | * {@code "no-transform"} 81 | */ 82 | public static final MutableString NO_TRANSFORM = new MutableString("no-transform"); 83 | /** 84 | * {@code "none"} 85 | */ 86 | public static final MutableString NONE = new MutableString("none"); 87 | /** 88 | * {@code "only-if-cached"} 89 | */ 90 | public static final MutableString ONLY_IF_CACHED = new MutableString("only-if-cached"); 91 | /** 92 | * {@code "private"} 93 | */ 94 | public static final MutableString PRIVATE = new MutableString("private"); 95 | /** 96 | * {@code "proxy-revalidate"} 97 | */ 98 | public static final MutableString PROXY_REVALIDATE = new MutableString("proxy-revalidate"); 99 | /** 100 | * {@code "public"} 101 | */ 102 | public static final MutableString PUBLIC = new MutableString("public"); 103 | /** 104 | * {@code "quoted-printable"} 105 | */ 106 | public static final MutableString QUOTED_PRINTABLE = new MutableString("quoted-printable"); 107 | /** 108 | * {@code "s-maxage"} 109 | */ 110 | public static final MutableString S_MAXAGE = new MutableString("s-maxage"); 111 | /** 112 | * {@code "trailers"} 113 | */ 114 | public static final MutableString TRAILERS = new MutableString("trailers"); 115 | /** 116 | * {@code "Upgrade"} 117 | */ 118 | public static final MutableString UPGRADE = new MutableString("Upgrade"); 119 | /** 120 | * {@code "WebSocket"} 121 | */ 122 | public static final MutableString WEBSOCKET = new MutableString("WebSocket"); 123 | 124 | } 125 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/pipeline/http/HttpEncoder.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.pipeline.http; 2 | 3 | import org.jboss.netty.channel.Channel; 4 | import org.jboss.netty.channel.ChannelHandlerContext; 5 | 6 | // TODO: Write a garbage free encoder :) 7 | public class HttpEncoder { 8 | 9 | public void encode(ChannelHandlerContext ctx, Channel channel, Object msg) { 10 | 11 | // if (msg instanceof HttpMessage) { 12 | // HttpMessage m = (HttpMessage) msg; 13 | // boolean chunked = this.chunked = HttpCodecUtil.isTransferEncodingChunked(m); 14 | // ChannelBuffer header = ChannelBuffers.dynamicBuffer( 15 | // channel.getConfig().getBufferFactory()); 16 | // encodeInitialLine(header, m); 17 | // encodeHeaders(header, m); 18 | // header.writeByte(CR); 19 | // header.writeByte(LF); 20 | // 21 | // ChannelBuffer content = m.getContent(); 22 | // if (!content.readable()) { 23 | // return header; // no content 24 | // } else if (chunked) { 25 | // throw new IllegalArgumentException( 26 | // "HttpMessage.content must be empty " + 27 | // "if Transfer-Encoding is chunked."); 28 | // } else { 29 | // return wrappedBuffer(header, content); 30 | // } 31 | // } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/pipeline/http/HttpHeaderContainer.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.pipeline.http; 2 | 3 | import java.util.Arrays; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | import org.neo4j.smack.gcfree.MutableString; 8 | 9 | public class HttpHeaderContainer { 10 | 11 | /** 12 | * Garbage free container type for header values. 13 | * Is not iterable (since that would mean we can't be 14 | * garbage free), so instead to loop over, use a pattern like: 15 | * 16 | * MutableString current = values.get(0); 17 | * for( int i=0, l=values.size() ; i 0) { 33 | return values[0]; 34 | } 35 | return null; 36 | } 37 | 38 | /** 39 | * Copies the contents of value into a free slot. 40 | * @param value 41 | */ 42 | public void add(MutableString value) 43 | { 44 | if(numValues == values.length) { 45 | makeInternalValueStoreBigger(); 46 | } 47 | values[numValues++].setTo(value); 48 | } 49 | 50 | public MutableString get(int i) 51 | { 52 | if(i < numValues) { 53 | return values[i]; 54 | } 55 | return null; 56 | } 57 | 58 | public int size() { 59 | return numValues; 60 | } 61 | 62 | public void clear() 63 | { 64 | numValues = 0; 65 | } 66 | 67 | protected int currentCapacity() { 68 | return values.length; 69 | } 70 | 71 | private void makeInternalValueStoreBigger() 72 | { 73 | int newStartIndex = values.length; 74 | values = Arrays.copyOf(values, values.length * 2); 75 | initializeValueStore(newStartIndex, values.length); 76 | } 77 | 78 | private void initializeValueStore(int newStartIndex, int length) 79 | { 80 | for(int i=newStartIndex;i headers = new HashMap(); 88 | 89 | public MutableString getHeader(HttpHeaderName name) 90 | { 91 | if(headers.containsKey(name)) { 92 | return headers.get(name).first(); 93 | } else { 94 | return null; 95 | } 96 | } 97 | 98 | public HttpHeaderValues getHeaders(HttpHeaderName name) 99 | { 100 | return headers.get(name); 101 | } 102 | 103 | public void addHeader(HttpHeaderName headerName, MutableString value) 104 | { 105 | if(!headers.containsKey(headerName)) { 106 | headers.put(headerName, new HttpHeaderValues()); 107 | } 108 | headers.get(headerName).add(value); 109 | } 110 | 111 | public void clear() 112 | { 113 | // TODO: This creates an iterator, refactor. 114 | for(HttpHeaderValues v : headers.values()) { 115 | v.clear(); 116 | } 117 | } 118 | 119 | public void removeHeader(HttpHeaderName name) 120 | { 121 | headers.get(name).clear(); 122 | } 123 | 124 | } 125 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/pipeline/http/HttpHeaderName.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.pipeline.http; 2 | 3 | import org.neo4j.smack.gcfree.MutableString; 4 | 5 | public class HttpHeaderName { 6 | 7 | private MutableString name; 8 | 9 | public HttpHeaderName(String string) 10 | { 11 | this.name = new MutableString(string); 12 | } 13 | 14 | public boolean equalsString(MutableString headerName) 15 | { 16 | return headerName.equals(name); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/pipeline/http/HttpTokens.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.pipeline.http; 2 | 3 | import java.nio.charset.Charset; 4 | 5 | import org.jboss.netty.util.CharsetUtil; 6 | 7 | public class HttpTokens { 8 | 9 | public static final byte SP = 32; 10 | 11 | //tab ' ' 12 | public static final byte HT = 9; 13 | 14 | /** 15 | * Carriage return 16 | */ 17 | public static final byte CR = 13; 18 | 19 | /** 20 | * Equals '=' 21 | */ 22 | public static final byte EQUALS = 61; 23 | 24 | /** 25 | * Line feed character 26 | */ 27 | public static final byte LF = 10; 28 | 29 | /** 30 | * carriage return line feed 31 | */ 32 | public static final byte[] CRLF = new byte[] { CR, LF }; 33 | 34 | /** 35 | * Colon ':' 36 | */ 37 | public static final byte COLON = 58; 38 | 39 | /** 40 | * Semicolon ';' 41 | */ 42 | public static final byte SEMICOLON = 59; 43 | 44 | /** 45 | * comma ',' 46 | */ 47 | public static final byte COMMA = 44; 48 | 49 | public static final byte DOUBLE_QUOTE = '"'; 50 | 51 | public static final Charset DEFAULT_CHARSET = CharsetUtil.UTF_8; 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/pipeline/http/NettyChannelTrackingHandler.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2002-2011 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | package org.neo4j.smack.pipeline.http; 21 | 22 | import org.jboss.netty.channel.ChannelHandlerContext; 23 | import org.jboss.netty.channel.ChannelStateEvent; 24 | import org.jboss.netty.channel.SimpleChannelHandler; 25 | import org.jboss.netty.channel.group.ChannelGroup; 26 | 27 | public class NettyChannelTrackingHandler extends SimpleChannelHandler { 28 | 29 | private final ChannelGroup openChannels; 30 | 31 | public NettyChannelTrackingHandler(ChannelGroup openChannels) { 32 | this.openChannels = openChannels; 33 | } 34 | 35 | @Override 36 | public void channelOpen( 37 | ChannelHandlerContext ctx, ChannelStateEvent e) { 38 | openChannels.add(ctx.getChannel()); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/pipeline/http/NettyHttpHandler.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2002-2011 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | package org.neo4j.smack.pipeline.http; 21 | 22 | import java.util.concurrent.atomic.AtomicLong; 23 | 24 | import org.jboss.netty.channel.Channel; 25 | import org.jboss.netty.channel.ChannelHandlerContext; 26 | import org.jboss.netty.channel.ChannelStateEvent; 27 | import org.jboss.netty.channel.ExceptionEvent; 28 | import org.jboss.netty.channel.MessageEvent; 29 | import org.jboss.netty.channel.SimpleChannelHandler; 30 | import org.neo4j.smack.pipeline.core.WorkPublisher; 31 | 32 | public class NettyHttpHandler extends SimpleChannelHandler { 33 | 34 | private HttpDecoder httpDecoder; 35 | 36 | private AtomicLong connectionId; 37 | 38 | private WorkPublisher workBuffer; 39 | 40 | public NettyHttpHandler(WorkPublisher workBuffer, AtomicLong connectionIdGenerator) { 41 | this.workBuffer = workBuffer; 42 | this.httpDecoder = new HttpDecoder(workBuffer); 43 | this.connectionId = connectionIdGenerator; 44 | } 45 | 46 | @Override 47 | public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) 48 | throws Exception { 49 | httpDecoder.messageReceived(ctx, e); 50 | } 51 | 52 | @Override 53 | public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) { 54 | ctx.setAttachment(connectionId.incrementAndGet()); 55 | } 56 | 57 | // TODO: I think this catches both upstream and downstream 58 | // exceptions. Only upstream exceptions should get added to 59 | // the work buffer like this, down stream exceptions need 60 | // to be handled differently, otherwise we will append 61 | // an error for a requst that failed back to the beginning of 62 | // the list of jobs. 63 | @Override 64 | public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) 65 | throws Exception { 66 | Channel ch = e.getChannel(); 67 | Throwable cause = e.getCause(); 68 | 69 | Long connectionId = (Long)ctx.getAttachment(); 70 | workBuffer.addFailure(connectionId, ch, cause); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/pipeline/http/NettyHttpPipelineFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2002-2011 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | package org.neo4j.smack.pipeline.http; 21 | 22 | import java.util.concurrent.atomic.AtomicLong; 23 | 24 | import org.jboss.netty.channel.ChannelPipeline; 25 | import org.jboss.netty.channel.ChannelPipelineFactory; 26 | import org.jboss.netty.channel.Channels; 27 | import org.jboss.netty.channel.group.ChannelGroup; 28 | import org.jboss.netty.handler.codec.http.HttpResponseEncoder; 29 | import org.neo4j.smack.pipeline.core.WorkPublisher; 30 | 31 | public class NettyHttpPipelineFactory implements ChannelPipelineFactory { 32 | 33 | private WorkPublisher workConsumer; 34 | private ChannelGroup openChannels; 35 | private AtomicLong connectionIdGenerator; 36 | 37 | public NettyHttpPipelineFactory(WorkPublisher workBuffer, ChannelGroup openChannels) { 38 | this.workConsumer = workBuffer; 39 | this.openChannels = openChannels; 40 | this.connectionIdGenerator = new AtomicLong(); 41 | } 42 | 43 | public ChannelPipeline getPipeline() throws Exception { 44 | // Create a default pipeline implementation. 45 | ChannelPipeline pipeline = Channels.pipeline(); 46 | 47 | // Uncomment the following line if you want HTTPS 48 | // SSLEngine engine = 49 | // SecureChatSslContextFactory.getServerContext().createSSLEngine(); 50 | // engine.setUseClientMode(false); 51 | // pipeline.addLast("ssl", new SslHandler(engine)); 52 | 53 | pipeline.addLast("channeltracker",new NettyChannelTrackingHandler(openChannels)); 54 | 55 | // TODO: Replace with our own response encoder 56 | pipeline.addLast("encoder", new HttpResponseEncoder()); 57 | //pipeline.addLast("chunkedWriter", new ChunkedWriteHandler()); 58 | 59 | pipeline.addLast("handler", new NettyHttpHandler(workConsumer, connectionIdGenerator)); 60 | return pipeline; 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/routing/Endpoint.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2002-2011 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | package org.neo4j.smack.routing; 21 | 22 | import org.neo4j.smack.pipeline.database.event.Invocation; 23 | import org.neo4j.smack.pipeline.database.event.Output; 24 | import org.neo4j.smack.serialization.DeserializationStrategy; 25 | import org.neo4j.smack.serialization.SerializationStrategy; 26 | 27 | public interface Endpoint { 28 | 29 | // TODO java7 use method-handle 30 | public void invoke(Invocation invocation, Output result) throws Exception; 31 | public InvocationVerb getVerb(); 32 | public DeserializationStrategy getDeserializationStrategy(); 33 | public SerializationStrategy getSerializationStrategy(); 34 | 35 | public boolean isTransactional(); 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/routing/InvocationVerb.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2002-2011 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | package org.neo4j.smack.routing; 21 | 22 | public enum InvocationVerb { 23 | GET, 24 | PUT, 25 | POST, 26 | DELETE, 27 | HEAD; 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/routing/NotFoundEndpoint.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.routing; 2 | 3 | import org.neo4j.smack.pipeline.database.event.Invocation; 4 | import org.neo4j.smack.pipeline.database.event.Output; 5 | import org.neo4j.smack.serialization.DeserializationStrategy; 6 | import org.neo4j.smack.serialization.SerializationStrategy; 7 | 8 | public class NotFoundEndpoint implements Endpoint { 9 | 10 | @Override 11 | public void invoke(Invocation invocation, Output result) 12 | throws Exception 13 | { 14 | result.notFound(); 15 | } 16 | 17 | @Override 18 | public InvocationVerb getVerb() 19 | { 20 | return InvocationVerb.GET; 21 | } 22 | 23 | @Override 24 | public DeserializationStrategy getDeserializationStrategy() 25 | { 26 | return DeserializationStrategy.NO_OP; 27 | } 28 | 29 | @Override 30 | public SerializationStrategy getSerializationStrategy() 31 | { 32 | return SerializationStrategy.NO_OP; 33 | } 34 | 35 | @Override 36 | public boolean isTransactional() 37 | { 38 | return false; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/routing/PathVariables.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2002-2011 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | package org.neo4j.smack.routing; 21 | 22 | import java.util.HashMap; 23 | import java.util.List; 24 | import java.util.Map; 25 | import java.util.regex.MatchResult; 26 | 27 | import com.sun.jersey.server.impl.uri.PathPattern; 28 | 29 | public class PathVariables { 30 | 31 | private Map pathVariables = new HashMap(); 32 | 33 | public void add(MatchResult matched, PathPattern routePattern) { 34 | List vars = routePattern.getTemplate().getTemplateVariables(); 35 | for(int i=0,l=vars.size();i> parameters) { 57 | for (Map.Entry> entry : parameters.entrySet()) { 58 | if (entry.getValue()==null && entry.getValue().isEmpty()) continue; 59 | pathVariables.put(entry.getKey(),entry.getValue().get(0)); 60 | } 61 | } 62 | 63 | public void reset() { 64 | pathVariables.clear(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/routing/ResourceNotFoundException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2002-2011 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | package org.neo4j.smack.routing; 21 | 22 | public class ResourceNotFoundException extends RuntimeException { 23 | 24 | public ResourceNotFoundException(String string) { 25 | super(string); 26 | } 27 | 28 | /** 29 | * 30 | */ 31 | private static final long serialVersionUID = -2095648980547335471L; 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/routing/Routable.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.routing; 2 | 3 | 4 | public interface Routable { 5 | 6 | String getPath(); 7 | 8 | InvocationVerb getVerb(); 9 | 10 | PathVariables getPathVariables(); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/routing/RouteDefinitionEntry.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2002-2011 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | package org.neo4j.smack.routing; 21 | 22 | 23 | public class RouteDefinitionEntry { 24 | 25 | private final String path; 26 | private final Endpoint endpoint; 27 | 28 | public RouteDefinitionEntry(String path, Endpoint endpoint) { 29 | this.path = path; 30 | this.endpoint = endpoint; 31 | } 32 | 33 | public String getPath() { 34 | return path; 35 | } 36 | 37 | public Endpoint getEndpoint() { 38 | return endpoint; 39 | } 40 | 41 | @Override 42 | public String toString() { 43 | return String.format("RouteDefinitionEntry{path='%s', endpoint=%s}", path, endpoint); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/routing/RouteEntry.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2002-2011 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | package org.neo4j.smack.routing; 21 | 22 | 23 | import com.sun.jersey.server.impl.uri.PathPattern; 24 | 25 | public class RouteEntry { 26 | 27 | protected PathPattern pattern; 28 | 29 | protected Endpoint getEndpoint; 30 | protected Endpoint putEndpoint; 31 | protected Endpoint postEndpoint; 32 | protected Endpoint deleteEndpoint; 33 | protected Endpoint headEndpoint; 34 | 35 | public void setEndpoint(InvocationVerb verb, Endpoint endpoint) { 36 | switch(verb) { 37 | case GET: 38 | getEndpoint = endpoint; 39 | break; 40 | case PUT: 41 | putEndpoint = endpoint; 42 | break; 43 | case POST: 44 | postEndpoint = endpoint; 45 | break; 46 | case DELETE: 47 | deleteEndpoint = endpoint; 48 | break; 49 | case HEAD: 50 | headEndpoint = endpoint; 51 | break; 52 | } 53 | } 54 | 55 | public Endpoint getEndpoint(InvocationVerb verb) { 56 | switch(verb) { 57 | case GET: 58 | return getEndpoint; 59 | case PUT: 60 | return putEndpoint; 61 | case POST: 62 | return postEndpoint; 63 | case DELETE: 64 | return deleteEndpoint; 65 | case HEAD: 66 | return headEndpoint; 67 | } 68 | return null; 69 | } 70 | 71 | public String toString() { 72 | return "Route ["+pattern+"] {GET:"+getEndpoint+", PUT:"+putEndpoint+", POST:"+postEndpoint+", DELETE:"+deleteEndpoint+", HEAD:"+headEndpoint+"}"; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/routing/Router.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2002-2011 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | package org.neo4j.smack.routing; 21 | 22 | import java.util.LinkedHashMap; 23 | import java.util.Map; 24 | import java.util.regex.MatchResult; 25 | 26 | import org.apache.log4j.Logger; 27 | 28 | import com.sun.jersey.server.impl.uri.PathPattern; 29 | import com.sun.jersey.server.impl.uri.PathTemplate; 30 | 31 | public class Router extends RoutingDefinition { 32 | 33 | private RouteEntry [] routes; 34 | private static final Logger logger = Logger.getLogger(Router.class); 35 | 36 | private static Endpoint notFoundEndpoint = new NotFoundEndpoint(); 37 | 38 | private final ResettableQueryStringDecoder queryStringDecoder = new ResettableQueryStringDecoder(); 39 | 40 | public Endpoint route(Routable routable) 41 | { 42 | 43 | String path = routable.getPath(); 44 | 45 | queryStringDecoder.resetWith(path); 46 | routable.getPathVariables().add(queryStringDecoder.getParameters()); 47 | 48 | // TODO: parallelize routing ?? (overhead ?) 49 | // Potentially desert the regex approach and go 50 | // with something that builds a tree structure with the path 51 | // segments of the enpoint paths, traversing it to find endpoint? 52 | for(RouteEntry route : routes) 53 | { 54 | MatchResult matchResult = route.pattern.match(path); 55 | if(matchResult != null) 56 | { 57 | Endpoint endpoint = route.getEndpoint(routable.getVerb()); 58 | if(endpoint != null) { 59 | routable.getPathVariables().add(matchResult, route.pattern); // todo is this the best way ? 60 | return endpoint; 61 | } 62 | return notFoundEndpoint; 63 | } 64 | } 65 | return notFoundEndpoint; 66 | } 67 | 68 | public void compileRoutes() { 69 | Map routeMap = new LinkedHashMap(); 70 | 71 | for(RouteDefinitionEntry definition : getRouteDefinitionEntries()) 72 | { 73 | if(!routeMap.containsKey(definition.getPath())) 74 | { 75 | logger.debug("Adding Route: "+definition.getEndpoint().getVerb() +" to: "+ definition.getPath()); 76 | routeMap.put(definition.getPath(), createRoute(definition)); 77 | } 78 | 79 | RouteEntry route = routeMap.get(definition.getPath()); 80 | route.setEndpoint(definition.getEndpoint().getVerb(), definition.getEndpoint()); 81 | // todo what happens if multiple paths have differnt verbs? 82 | } 83 | 84 | // Debug print routing table 85 | // for(RouteEntry route : routeMap.values()) 86 | // System.out.println(route); 87 | 88 | routes = routeMap.values().toArray(new RouteEntry[routeMap.size()]); 89 | } 90 | 91 | private RouteEntry createRoute(RouteDefinitionEntry definition) { 92 | RouteEntry route = new RouteEntry(); 93 | final PathTemplate template = new PathTemplate(definition.getPath()); 94 | route.pattern = new PathPattern(template, definition.getPath().equals("") ? "/" : ""); 95 | return route; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/routing/RoutingDefinition.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2002-2011 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | package org.neo4j.smack.routing; 21 | 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | 25 | 26 | public class RoutingDefinition { 27 | 28 | protected List entries = new ArrayList(); 29 | 30 | public void addRoute(String route, Object target) { 31 | addRoute(route, new AnnotationBasedRoutingDefinition(target)); 32 | } 33 | 34 | public void addRoute(String route, RoutingDefinition target) { 35 | for(RouteDefinitionEntry subRoute : target.getRouteDefinitionEntries()) { 36 | addRoute(route + subRoute.getPath(), subRoute.getEndpoint()); 37 | } 38 | } 39 | 40 | public void addRoute(String route, Endpoint target) { 41 | entries.add(new RouteDefinitionEntry(route, target)); 42 | } 43 | 44 | public List getRouteDefinitionEntries() { 45 | return entries; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/routing/SimpleEndpoint.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.routing; 2 | 3 | import org.neo4j.smack.serialization.DeserializationStrategy; 4 | import org.neo4j.smack.serialization.SerializationStrategy; 5 | 6 | public abstract class SimpleEndpoint implements Endpoint { 7 | 8 | @Override 9 | public InvocationVerb getVerb() 10 | { 11 | return InvocationVerb.GET; 12 | } 13 | 14 | @Override 15 | public DeserializationStrategy getDeserializationStrategy() 16 | { 17 | return DeserializationStrategy.NO_OP; 18 | } 19 | 20 | @Override 21 | public SerializationStrategy getSerializationStrategy() 22 | { 23 | return SerializationStrategy.NO_OP; 24 | } 25 | 26 | @Override 27 | public boolean isTransactional() 28 | { 29 | return false; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/routing/annotation/DeserializeWith.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2002-2011 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | package org.neo4j.smack.routing.annotation; 21 | 22 | import java.lang.annotation.ElementType; 23 | import java.lang.annotation.Retention; 24 | import java.lang.annotation.RetentionPolicy; 25 | import java.lang.annotation.Target; 26 | 27 | import org.neo4j.smack.serialization.DeserializationStrategy; 28 | 29 | 30 | @Target({ElementType.METHOD}) 31 | @Retention(RetentionPolicy.RUNTIME) 32 | public @interface DeserializeWith { 33 | 34 | Class> value(); 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/routing/annotation/SerializeWith.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2002-2011 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | package org.neo4j.smack.routing.annotation; 21 | 22 | import java.lang.annotation.ElementType; 23 | import java.lang.annotation.Retention; 24 | import java.lang.annotation.RetentionPolicy; 25 | import java.lang.annotation.Target; 26 | 27 | import org.neo4j.smack.serialization.SerializationStrategy; 28 | 29 | @Target({ElementType.METHOD}) 30 | @Retention(RetentionPolicy.RUNTIME) 31 | public @interface SerializeWith { 32 | 33 | Class> value(); 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/routing/annotation/Transactional.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.routing.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Target({ElementType.TYPE, ElementType.METHOD}) 9 | @Retention(RetentionPolicy.RUNTIME) 10 | public @interface Transactional { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/serialization/DeserializationException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2002-2011 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | package org.neo4j.smack.serialization; 21 | 22 | 23 | public class DeserializationException extends RuntimeException { 24 | 25 | /** 26 | * 27 | */ 28 | private static final long serialVersionUID = -1139734926756347307L; 29 | 30 | public DeserializationException(String string) { 31 | this(string,null); 32 | } 33 | 34 | public DeserializationException(String string, Throwable e) { 35 | super(string, e); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/serialization/DeserializationStrategy.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2002-2011 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | package org.neo4j.smack.serialization; 21 | 22 | 23 | public interface DeserializationStrategy { 24 | 25 | public static final DeserializationStrategy NO_OP = new DeserializationStrategy() { 26 | @Override 27 | public Object deserialize(Deserializer in) throws DeserializationException { 28 | return null; 29 | } 30 | }; 31 | 32 | T deserialize(Deserializer in) throws DeserializationException; 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/serialization/Deserializer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2002-2011 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | package org.neo4j.smack.serialization; 21 | 22 | import java.util.Map; 23 | 24 | // TODO: Refactor this to allow streaming deserialization 25 | public interface Deserializer { 26 | 27 | long readLong(); 28 | 29 | String readString(); 30 | 31 | long readInt(); 32 | 33 | Object readObject(); 34 | 35 | T readEnum(IdentifiableEnumDeserializer deserializer); 36 | 37 | Map readMap(); 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/serialization/IdentifiableEnum.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2002-2011 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | package org.neo4j.smack.serialization; 21 | 22 | public interface IdentifiableEnum { 23 | 24 | public int getId(); 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/serialization/IdentifiableEnumDeserializer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2002-2011 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | package org.neo4j.smack.serialization; 21 | 22 | public interface IdentifiableEnumDeserializer { 23 | 24 | T getForId(int id); 25 | T getForName(String name); 26 | 27 | } -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/serialization/JsonDeserializer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2002-2011 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | package org.neo4j.smack.serialization; 21 | 22 | import java.io.EOFException; 23 | import java.io.IOException; 24 | import java.io.InputStream; 25 | import java.util.Map; 26 | 27 | import org.codehaus.jackson.JsonFactory; 28 | import org.codehaus.jackson.JsonParseException; 29 | import org.codehaus.jackson.JsonParser; 30 | public class JsonDeserializer implements Deserializer { 31 | 32 | private JsonParser parser; 33 | 34 | public JsonDeserializer(JsonFactory factory, InputStream stream) { 35 | try { 36 | this.parser = factory.createJsonParser(stream); 37 | } catch (JsonParseException e) { 38 | throw new DeserializationException("Unable to instantiate JSON parser.", e); 39 | } catch (IOException e) { 40 | e.printStackTrace(); 41 | throw new DeserializationException("Unable to instantiate JSON parser.", e); 42 | } 43 | } 44 | 45 | @Override 46 | public long readLong() { 47 | try { 48 | return parser.getLongValue(); 49 | } catch (Exception e) { 50 | throw new DeserializationException("Invalid JSON format, expected Long.", e); 51 | } 52 | } 53 | 54 | @Override 55 | public long readInt() { 56 | try { 57 | return parser.getIntValue(); 58 | } catch (Exception e) { 59 | throw new DeserializationException("Invalid JSON format, expected Integer.", e); 60 | } 61 | } 62 | 63 | @Override 64 | public T readEnum(IdentifiableEnumDeserializer deserializer) { 65 | String str = readString(); 66 | return deserializer.getForName(str); 67 | } 68 | 69 | @Override 70 | public String readString() { 71 | try { 72 | parser.nextValue(); 73 | return parser.getText(); 74 | } catch (JsonParseException e) { 75 | throw new DeserializationException("Invalid JSON format, expected String.", e); 76 | } catch (IOException e) { 77 | throw new DeserializationException("Unable to read expected String value.", e); 78 | } 79 | } 80 | 81 | @Override 82 | public Object readObject() { 83 | try { 84 | return parser.readValueAs(Object.class); 85 | } catch(EOFException e) { 86 | return null; 87 | } catch (JsonParseException e) { 88 | throw new DeserializationException("Invalid JSON format, expected object.", e); 89 | } catch (IOException e) { 90 | throw new DeserializationException("Unable to read expected object value.", e); 91 | } 92 | } 93 | 94 | @Override 95 | @SuppressWarnings("unchecked") 96 | public Map readMap() { 97 | try { 98 | return parser.readValueAs(Map.class); 99 | } catch (JsonParseException e) { 100 | throw new DeserializationException("Invalid JSON format, expected Map.", e); 101 | } catch (IOException e) { 102 | throw new DeserializationException("Unable to read expected Map value.", e); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/serialization/SerializationException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2002-2011 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | package org.neo4j.smack.serialization; 21 | 22 | 23 | public class SerializationException extends RuntimeException { 24 | 25 | /** 26 | * 27 | */ 28 | private static final long serialVersionUID = -1139734926756347307L; 29 | 30 | public SerializationException(String string) { 31 | this(string,null); 32 | } 33 | 34 | public SerializationException(String string, Throwable e) { 35 | super(string, e); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/serialization/SerializationFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2002-2011 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | package org.neo4j.smack.serialization; 21 | 22 | import org.codehaus.jackson.JsonFactory; 23 | import org.codehaus.jackson.map.ObjectMapper; 24 | import org.jboss.netty.buffer.ChannelBuffer; 25 | import org.jboss.netty.buffer.ChannelBufferInputStream; 26 | 27 | public class SerializationFactory { 28 | 29 | JsonFactory jsonFactory = new JsonFactory(new ObjectMapper()); 30 | 31 | public Deserializer getDeserializer(ChannelBuffer input) throws DeserializationException { 32 | return new JsonDeserializer(jsonFactory, new ChannelBufferInputStream(input)); 33 | } 34 | 35 | public Serializer getSerializer(ChannelBuffer output) throws SerializationException { 36 | return new JsonSerializer(jsonFactory, output); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/serialization/SerializationStrategy.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2002-2011 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | package org.neo4j.smack.serialization; 21 | 22 | public interface SerializationStrategy { 23 | 24 | public static SerializationStrategy NO_OP = new SerializationStrategy() { 25 | public void serialize(Object value, Serializer out) 26 | { 27 | 28 | } 29 | }; 30 | 31 | void serialize(T value, Serializer out); 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/serialization/Serializer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2002-2011 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | package org.neo4j.smack.serialization; 21 | 22 | 23 | import java.util.Map; 24 | 25 | import javax.ws.rs.core.MediaType; 26 | 27 | public interface Serializer { 28 | 29 | void putEnum(Enum en); 30 | 31 | // TODO: Look into using SerializedString here for performance 32 | void putString(String string); 33 | 34 | void putBoolean(boolean value); 35 | 36 | void putInteger(int value); 37 | 38 | void putDouble(double value); 39 | 40 | void putFloat(float value); 41 | 42 | void putLong(long value); 43 | 44 | void putMap(Map data); 45 | 46 | void startList(); 47 | 48 | void endList(); 49 | 50 | void startMap(); 51 | 52 | // TODO: Look into using SerializedString here for performance 53 | void putMapPropertyName(String string); 54 | 55 | void endMap(); 56 | 57 | MediaType getContentType(); 58 | 59 | void flush(); 60 | } 61 | 62 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/serialization/strategy/ExceptionSerializationStrategy.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.serialization.strategy; 2 | 3 | import java.util.ArrayList; 4 | import java.util.LinkedHashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | import org.neo4j.smack.serialization.SerializationException; 9 | import org.neo4j.smack.serialization.SerializationStrategy; 10 | import org.neo4j.smack.serialization.Serializer; 11 | 12 | /** 13 | * @author mh 14 | * @since 27.11.11 15 | */ 16 | public class ExceptionSerializationStrategy implements SerializationStrategy { 17 | 18 | @Override 19 | public void serialize(Throwable exception, Serializer out) throws SerializationException { 20 | Map result = new LinkedHashMap(); 21 | addMessage(exception, result); 22 | addException(exception, result); 23 | addStackTrace(exception, result); 24 | out.putMap(result); 25 | } 26 | 27 | private void addException(Throwable exception, Map result) { 28 | result.put("exception", exception.toString()); 29 | } 30 | 31 | private void addMessage(Throwable exception, Map result) { 32 | String message = exception.getMessage(); 33 | if (message == null) return; 34 | result.put("message", message); 35 | } 36 | 37 | private void addStackTrace(Throwable exception, Map result) { 38 | StackTraceElement[] trace = exception.getStackTrace(); 39 | if (trace == null) return; 40 | 41 | List list = new ArrayList(trace.length); 42 | for (StackTraceElement traceElement : trace) { 43 | list.add(traceElement.toString()); 44 | } 45 | result.put("stacktrace", list); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/serialization/strategy/NodeSerializationStrategy.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.serialization.strategy; 2 | 3 | import org.neo4j.graphdb.Node; 4 | import org.neo4j.smack.api.UrlReverseLookerUpper; 5 | import org.neo4j.smack.serialization.SerializationException; 6 | import org.neo4j.smack.serialization.SerializationStrategy; 7 | import org.neo4j.smack.serialization.Serializer; 8 | 9 | public class NodeSerializationStrategy implements SerializationStrategy { 10 | 11 | private static final String SELF_FIELD = "self"; 12 | private static final String DATA_FIELD = "data"; 13 | private static final String EXTENSIONS_FIELD = "extensions"; 14 | 15 | private static final UrlReverseLookerUpper url = new UrlReverseLookerUpper(); 16 | private static final PropertyContainerSerializationStrategy propertySerialization = new PropertyContainerSerializationStrategy(); 17 | 18 | @Override 19 | public void serialize(Node node, Serializer out) throws SerializationException { 20 | out.startMap(); 21 | 22 | out.putMapPropertyName(DATA_FIELD); 23 | propertySerialization.serialize(node, out); 24 | 25 | out.putMapPropertyName(SELF_FIELD); 26 | out.putString(url.reverse(node)); 27 | 28 | out.putMapPropertyName(EXTENSIONS_FIELD); 29 | out.startMap(); 30 | out.endMap(); 31 | 32 | out.endMap(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/serialization/strategy/PropertyContainerDeserialization.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.serialization.strategy; 2 | 3 | import java.util.Iterator; 4 | import java.util.Map; 5 | import java.util.Map.Entry; 6 | 7 | /** 8 | * This is a place holder API, its implementation is meant to be modified 9 | * further down the road. Basically, it is meant to be 10 | * set up to allow streaming deserialization of key/value 11 | * properties, without creating garbage and without holding 12 | * the full set of properties in memory. 13 | * 14 | * As a consumer of these, you can iterate over the properties 15 | * inside. This allows for future extending this to deserialize 16 | * on the fly while properties are being created. 17 | * 18 | * We don't use the iterator interface directly, because that 19 | * would complicate things garbage-wise, since then we have to create 20 | * wrapping Map.Entry instances for each property as well. 21 | */ 22 | public class PropertyContainerDeserialization { 23 | 24 | private Iterator> properties; 25 | private Entry currentProperty; 26 | 27 | // TODO: Replace this, have it take a ChannelBuffer or 28 | // something that does not force us to create new maps 29 | // or hold the full result in memory 30 | protected void setProperties(Iterator> properties) 31 | { 32 | this.properties = properties; 33 | } 34 | 35 | public void nextProperty() 36 | { 37 | this.currentProperty = properties.next(); 38 | } 39 | 40 | public boolean hasMoreProperties() 41 | { 42 | return properties.hasNext(); 43 | } 44 | 45 | // TODO: In the future, we could expand this further, 46 | // by returning some re-useable (eg. garbage-free) data 47 | // structure that allows streaming large values directly 48 | // from kernel-space backed Channel Buffers into the database. 49 | public Object propertyValue() 50 | { 51 | return currentProperty.getValue(); 52 | } 53 | 54 | public String propertyKey() 55 | { 56 | return currentProperty.getKey(); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/serialization/strategy/PropertyContainerDeserializationStrategy.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.serialization.strategy; 2 | 3 | import java.util.Collections; 4 | import java.util.Map; 5 | 6 | import org.neo4j.smack.serialization.DeserializationException; 7 | import org.neo4j.smack.serialization.DeserializationStrategy; 8 | import org.neo4j.smack.serialization.Deserializer; 9 | 10 | public class PropertyContainerDeserializationStrategy implements DeserializationStrategy 11 | { 12 | // TODO: Set up object pooling for PropertyContainerDeserialization 13 | @Override 14 | public PropertyContainerDeserialization deserialize(Deserializer in) throws DeserializationException 15 | { 16 | PropertyContainerDeserialization deserialized = new PropertyContainerDeserialization(); 17 | 18 | Map properties; 19 | try { 20 | properties = in.readMap(); 21 | } catch(DeserializationException e) { 22 | properties = Collections.emptyMap(); 23 | } 24 | 25 | deserialized.setProperties(properties.entrySet().iterator()); 26 | return deserialized; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/serialization/strategy/PropertyContainerSerializationStrategy.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.serialization.strategy; 2 | 3 | import org.neo4j.graphdb.PropertyContainer; 4 | import org.neo4j.smack.serialization.SerializationException; 5 | import org.neo4j.smack.serialization.SerializationStrategy; 6 | import org.neo4j.smack.serialization.Serializer; 7 | 8 | /** 9 | * Serializes a property container into a map. 10 | */ 11 | public class PropertyContainerSerializationStrategy implements 12 | SerializationStrategy { 13 | 14 | private static final PropertyValueSerializationStrategy valueSerialization = new PropertyValueSerializationStrategy(); 15 | 16 | @Override 17 | public void serialize(PropertyContainer entity, Serializer out) 18 | throws SerializationException 19 | { 20 | out.startMap(); 21 | for(String name : entity.getPropertyKeys()) { 22 | // TODO: Look into caching serialized property keys 23 | out.putMapPropertyName(name); 24 | valueSerialization.serialize(entity.getProperty(name), out); 25 | } 26 | out.endMap(); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/serialization/strategy/PropertyValueDeserializationStrategy.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.serialization.strategy; 2 | 3 | import org.neo4j.smack.serialization.DeserializationException; 4 | import org.neo4j.smack.serialization.DeserializationStrategy; 5 | import org.neo4j.smack.serialization.Deserializer; 6 | 7 | // TODO: Once we've added support for entering strings 8 | // and arrays into the database in a streaming manner, 9 | // extend this to allow exploiting that capability. 10 | public class PropertyValueDeserializationStrategy implements DeserializationStrategy 11 | { 12 | 13 | @Override 14 | public Object deserialize(Deserializer in) throws DeserializationException 15 | { 16 | return in.readObject(); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/serialization/strategy/PropertyValueSerializationStrategy.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.serialization.strategy; 2 | 3 | import org.neo4j.smack.serialization.SerializationException; 4 | import org.neo4j.smack.serialization.SerializationStrategy; 5 | import org.neo4j.smack.serialization.Serializer; 6 | 7 | /** 8 | * Serializes a database property value. 9 | */ 10 | public class PropertyValueSerializationStrategy implements 11 | SerializationStrategy { 12 | 13 | @Override 14 | public void serialize(Object value, Serializer out) 15 | { 16 | if(value instanceof String) 17 | { 18 | out.putString((String)value); 19 | } else if( value instanceof Integer) 20 | { 21 | out.putInteger((Integer)value); 22 | } else if( value instanceof Boolean) 23 | { 24 | out.putBoolean((Boolean)value); 25 | } else if( value instanceof Double) 26 | { 27 | out.putDouble((Double)value); 28 | } else if( value instanceof Float) 29 | { 30 | out.putFloat((Float)value); 31 | } else if( value instanceof Long) 32 | { 33 | out.putLong((Long)value); 34 | } else if(value.getClass().isArray()) 35 | { 36 | Class cls = value.getClass(); 37 | 38 | out.startList(); 39 | 40 | if(cls.equals(String[].class)) 41 | { 42 | for(String str : (String[])value) 43 | { 44 | out.putString(str); 45 | } 46 | } else if(cls.equals(int[].class)) 47 | { 48 | for(int integer : (int[])value) 49 | { 50 | out.putInteger(integer); 51 | } 52 | } else if(cls.equals(boolean[].class)) 53 | { 54 | for(boolean item : (boolean[])value) 55 | { 56 | out.putBoolean(item); 57 | } 58 | } else if(cls.equals(double[].class)) 59 | { 60 | for(double item : (double[])value) 61 | { 62 | out.putDouble(item); 63 | } 64 | } else if(cls.equals(float[].class)) 65 | { 66 | for(float item : (float[])value) 67 | { 68 | out.putFloat(item); 69 | } 70 | } else if(cls.equals(long[].class)) 71 | { 72 | for(long item : (long[])value) 73 | { 74 | out.putLong(item); 75 | } 76 | } else 77 | { 78 | throw new SerializationException("Unknown property value array type: " + value.getClass()); 79 | } 80 | 81 | out.endList(); 82 | } else 83 | { 84 | throw new SerializationException("Unknown property value type: " + value.getClass()); 85 | } 86 | } 87 | 88 | public void serialize(String value, Serializer out) 89 | throws SerializationException 90 | { 91 | out.putString(value); 92 | } 93 | 94 | public void serialize(int value, Serializer out) 95 | throws SerializationException 96 | { 97 | out.putInteger(value); 98 | } 99 | 100 | public void serialize(int [] value, Serializer out) 101 | throws SerializationException 102 | { 103 | out.startList(); 104 | for(int i : value) { 105 | out.putInteger(i); 106 | } 107 | out.endList(); 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/serialization/strategy/RelationshipCreationDescription.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.serialization.strategy; 2 | 3 | import org.apache.commons.pool.BasePoolableObjectFactory; 4 | import org.neo4j.graphdb.DynamicRelationshipType; 5 | 6 | public class RelationshipCreationDescription extends PropertyContainerDeserialization { 7 | 8 | public static class Factory extends BasePoolableObjectFactory { 9 | 10 | @Override 11 | public RelationshipCreationDescription makeObject() throws Exception 12 | { 13 | return new RelationshipCreationDescription(); 14 | } 15 | 16 | } 17 | 18 | private DynamicRelationshipType type; 19 | private long endNodeId; 20 | 21 | public DynamicRelationshipType getType() 22 | { 23 | return type; 24 | } 25 | 26 | public void setType(DynamicRelationshipType type) { 27 | this.type = type; 28 | } 29 | 30 | public void setEndNodeId(long nodeId) 31 | { 32 | this.endNodeId = nodeId; 33 | } 34 | 35 | public long getEndNodeId() 36 | { 37 | return endNodeId; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/serialization/strategy/RelationshipCreationDeserializationStrategy.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.serialization.strategy; 2 | 3 | import java.util.Collections; 4 | import java.util.Map; 5 | 6 | import org.neo4j.graphdb.DynamicRelationshipType; 7 | import org.neo4j.smack.api.UrlReverseLookerUpper; 8 | import org.neo4j.smack.serialization.DeserializationStrategy; 9 | import org.neo4j.smack.serialization.Deserializer; 10 | 11 | /** 12 | * Deserializes a relationship 13 | */ 14 | public class RelationshipCreationDeserializationStrategy implements 15 | DeserializationStrategy { 16 | 17 | private static final String TYPE_KEY = "type"; 18 | private static final String TO_KEY = "to"; 19 | private static final String DATA_KEY = "data"; 20 | 21 | // TODO: Use some object pooling pattern to re-use RelationshipCreationDescription instances 22 | @Override 23 | public RelationshipCreationDescription deserialize(Deserializer in) 24 | { 25 | RelationshipCreationDescription desc = new RelationshipCreationDescription(); 26 | Map raw = in.readMap(); 27 | 28 | desc.setType(DynamicRelationshipType.withName((String) raw.get(TYPE_KEY))); 29 | desc.setEndNodeId(UrlReverseLookerUpper.nodeId((String)raw.get(TO_KEY))); 30 | 31 | @SuppressWarnings("unchecked") 32 | Map properties = raw.containsKey(DATA_KEY) ? (Map)raw.get(DATA_KEY) : Collections.emptyMap(); 33 | desc.setProperties(properties.entrySet().iterator()); 34 | 35 | return desc; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/serialization/strategy/RelationshipSerializationStrategy.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.serialization.strategy; 2 | 3 | import org.neo4j.graphdb.Relationship; 4 | import org.neo4j.smack.api.UrlReverseLookerUpper; 5 | import org.neo4j.smack.serialization.SerializationException; 6 | import org.neo4j.smack.serialization.SerializationStrategy; 7 | import org.neo4j.smack.serialization.Serializer; 8 | 9 | public class RelationshipSerializationStrategy implements SerializationStrategy { 10 | 11 | private static final String END_FIELD = "end"; 12 | private static final String START_FIELD = "start"; 13 | private static final String TYPE_FIELD = "type"; 14 | private static final String SELF_FIELD = "self"; 15 | private static final String DATA_FIELD = "data"; 16 | private static final String EXTENSIONS_FIELD = "extensions"; 17 | 18 | private static final UrlReverseLookerUpper url = new UrlReverseLookerUpper(); 19 | private static final PropertyContainerSerializationStrategy propertySerialization = new PropertyContainerSerializationStrategy(); 20 | 21 | @Override 22 | public void serialize(Relationship rel, Serializer out) throws SerializationException { 23 | out.startMap(); 24 | 25 | out.putMapPropertyName(SELF_FIELD); 26 | out.putString(url.reverse(rel)); 27 | 28 | out.putMapPropertyName(TYPE_FIELD); 29 | out.putString(rel.getType().name()); 30 | 31 | out.putMapPropertyName(START_FIELD); 32 | out.putString(url.reverse(rel.getStartNode())); 33 | 34 | out.putMapPropertyName(END_FIELD); 35 | out.putString(url.reverse(rel.getEndNode())); 36 | 37 | out.putMapPropertyName(DATA_FIELD); 38 | propertySerialization.serialize(rel, out); 39 | 40 | out.putMapPropertyName(EXTENSIONS_FIELD); 41 | out.startMap(); 42 | out.endMap(); 43 | 44 | out.endMap(); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/serialization/strategy/TransactionStateDeserialization.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2002-2011 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | package org.neo4j.smack.serialization.strategy; 21 | 22 | import java.util.EnumSet; 23 | import java.util.HashMap; 24 | import java.util.Map; 25 | 26 | import org.neo4j.smack.serialization.IdentifiableEnum; 27 | 28 | public enum TransactionStateDeserialization implements IdentifiableEnum { 29 | OPEN(0), 30 | COMMITTED(1), 31 | ROLLED_BACK(2); 32 | 33 | private static final Map nameToValueMap = new HashMap(); 34 | private static final Map idToValueMap = new HashMap(); 35 | 36 | static { 37 | for (TransactionStateDeserialization value : EnumSet.allOf(TransactionStateDeserialization.class)) { 38 | nameToValueMap.put(value.name(), value); 39 | idToValueMap.put(value.getId(), value); 40 | } 41 | } 42 | 43 | public static TransactionStateDeserialization getForId(int id) { 44 | return idToValueMap.get(id); 45 | } 46 | 47 | public static TransactionStateDeserialization getForName(String name) { 48 | return nameToValueMap.get(name); 49 | } 50 | 51 | private final int id; 52 | 53 | TransactionStateDeserialization(int id) { 54 | this.id = id; 55 | } 56 | 57 | public int getId() { 58 | return id; 59 | } 60 | } -------------------------------------------------------------------------------- /src/main/java/org/neo4j/smack/serialization/strategy/TransactionStateDeserializationStrategy.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2002-2011 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | package org.neo4j.smack.serialization.strategy; 21 | 22 | import org.neo4j.smack.serialization.DeserializationStrategy; 23 | import org.neo4j.smack.serialization.Deserializer; 24 | import org.neo4j.smack.serialization.IdentifiableEnumDeserializer; 25 | 26 | public class TransactionStateDeserializationStrategy implements DeserializationStrategy, IdentifiableEnumDeserializer { 27 | 28 | public TransactionStateDeserializationStrategy() {} 29 | 30 | public TransactionStateDeserialization deserialize(Deserializer in) { 31 | return in.readEnum(this); 32 | } 33 | 34 | @Override 35 | public TransactionStateDeserialization getForId(int id) 36 | { 37 | TransactionStateDeserialization state = TransactionStateDeserialization.getForId(id); 38 | 39 | if(state == null) 40 | { 41 | throw new IllegalArgumentException("Invalid transaction state: '"+id+"'."); 42 | } 43 | return state; 44 | } 45 | 46 | @Override 47 | public TransactionStateDeserialization getForName(String name) { 48 | 49 | TransactionStateDeserialization state = TransactionStateDeserialization.getForName(name); 50 | 51 | if(state == null) 52 | { 53 | throw new IllegalArgumentException("Invalid transaction state: '"+name+"'."); 54 | } 55 | return state; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/test/java/org/neo4j/smack/gcfree/TestMutableStringConverter.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.gcfree; 2 | 3 | import static org.hamcrest.Matchers.is; 4 | import static org.junit.Assert.assertThat; 5 | 6 | import org.junit.Test; 7 | 8 | 9 | public class TestMutableStringConverter { 10 | 11 | @Test 12 | public void testConvertToLongFromPositiveDecimalString() { 13 | assertThat(MutableStringConverter.toLongValue(new MutableString("0")), is(0l)); 14 | assertThat(MutableStringConverter.toLongValue(new MutableString("10")), is(10l)); 15 | assertThat(MutableStringConverter.toLongValue(new MutableString("1234567890")), is(1234567890l)); 16 | } 17 | 18 | @Test(expected=NumberFormatException.class) 19 | public void testConvertingIllegalValueThrowsNumberFormatException() { 20 | assertThat(MutableStringConverter.toLongValue(new MutableString("0aabb")), is(0l)); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/test/java/org/neo4j/smack/integration/api/ClientIdIT.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.integration.api; 2 | 3 | import static org.neo4j.helpers.collection.MapUtil.map; 4 | 5 | import org.junit.Ignore; 6 | import org.junit.Test; 7 | import org.neo4j.smack.test.util.AbstractRestFunctionalTestBase; 8 | 9 | public class ClientIdIT extends AbstractRestFunctionalTestBase { 10 | 11 | @Test 12 | @Ignore 13 | public void testCreateNodeAndRelationshipsWithClientId() throws Exception { 14 | rest.to("/db/data/tx/c1").put(); 15 | 16 | String nodeOne = rest.to("/db/data/tx/c1/node/c2").put().location(); 17 | String nodeTwo = rest.to("/db/data/tx/c1/node/c3").put().location(); 18 | String rel = rest.to("/db/data/tx/c1/node/c2/relationship/c4").put(map("to","/db/data/node/c3","type","KNOWS")).location(); 19 | 20 | rest.to("/db/data/tx/c1/state").put("COMMITTED").ok(); 21 | 22 | rest.to(nodeOne).get().ok(); 23 | rest.to(nodeTwo).get().ok(); 24 | rest.to(rel).get().ok(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/org/neo4j/smack/integration/api/DataAPIRootIT.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.integration.api; 2 | /** 3 | * Copyright (c) 2002-2011 "Neo Technology," 4 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 5 | * 6 | * This file is part of Neo4j. 7 | * 8 | * Neo4j is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | import org.junit.Ignore; 23 | import org.junit.Test; 24 | import org.neo4j.graphdb.Transaction; 25 | import org.neo4j.kernel.AbstractGraphDatabase; 26 | import org.neo4j.kernel.Version; 27 | import org.neo4j.kernel.impl.annotations.Documented; 28 | import org.neo4j.smack.test.util.AbstractRestFunctionalTestBase; 29 | import org.neo4j.smack.test.util.JaxRsResponse; 30 | import org.neo4j.smack.test.util.JsonHelper; 31 | import org.neo4j.smack.test.util.RestRequest; 32 | import org.neo4j.test.GraphDescription.Graph; 33 | import org.neo4j.test.TestData; 34 | 35 | import java.util.Map; 36 | 37 | import static org.junit.Assert.*; 38 | 39 | public class DataAPIRootIT extends AbstractRestFunctionalTestBase 40 | { 41 | /** 42 | * The service root is your starting point to discover the REST API. 43 | * It contains the basic starting points for the databse, and some 44 | * version and extension information. The +reference_node+ entry will 45 | * only be present if there is a reference node set and exists in the database. 46 | */ 47 | @Documented 48 | @Test 49 | @Ignore 50 | @Graph("I know you") 51 | @TestData.Title( "Get service root" ) 52 | public void assert200OkFromGet() throws Exception 53 | { 54 | AbstractGraphDatabase db = (AbstractGraphDatabase)graphdb(); 55 | Transaction tx = db.beginTx(); 56 | db.getConfig().getGraphDbModule().setReferenceNodeId( data.get().get("I").getId() ); 57 | tx.success(); 58 | tx.finish(); 59 | String body = gen.get().expectedStatus( 200 ).get( getDataUri() ).entity(); 60 | Map map = JsonHelper.jsonToMap( body ); 61 | assertEquals( getDataUri() + "node", map.get( "node" ) ); 62 | assertNotNull( map.get( "reference_node" ) ); 63 | assertNotNull( map.get( "node_index" ) ); 64 | assertNotNull( map.get( "relationship_index" ) ); 65 | assertNotNull( map.get( "extensions_info" ) ); 66 | assertNotNull( map.get( "batch" ) ); 67 | assertEquals( Version.getKernelRevision(), map.get( "neo4j_version" ) ); 68 | 69 | // Make sure advertised urls work 70 | JaxRsResponse response = RestRequest.req().get( getDataUri() ); 71 | if ( map.get( "reference_node" ) != null ) 72 | { 73 | response = RestRequest.req().get( 74 | (String) map.get( "reference_node" ) ); 75 | assertEquals( 200, response.getStatus() ); 76 | response.close(); 77 | } 78 | response = RestRequest.req().get( (String) map.get( "node_index" ) ); 79 | assertTrue( response.getStatus() == 200 || response.getStatus() == 204 ); 80 | response.close(); 81 | 82 | response = RestRequest.req().get( 83 | (String) map.get( "relationship_index" ) ); 84 | assertTrue( response.getStatus() == 200 || response.getStatus() == 204 ); 85 | response.close(); 86 | 87 | response = RestRequest.req().get( (String) map.get( "extensions_info" ) ); 88 | assertEquals( 200, response.getStatus() ); 89 | response.close(); 90 | 91 | response = RestRequest.req().post( (String) map.get( "batch" ), "[]" ); 92 | assertEquals( 200, response.getStatus() ); 93 | response.close(); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/test/java/org/neo4j/smack/integration/api/ErrorHandlingIT.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2002-2011 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | package org.neo4j.smack.integration.api; 21 | 22 | import org.junit.Test; 23 | import org.neo4j.smack.pipeline.core.event.CorePipelineEvent; 24 | import org.neo4j.smack.routing.Endpoint; 25 | import org.neo4j.smack.test.util.AbstractRestFunctionalTestBase; 26 | import org.neo4j.smack.test.util.JaxRsResponse; 27 | import org.neo4j.smack.test.util.RestRequest; 28 | 29 | import static org.junit.Assert.assertEquals; 30 | 31 | public class ErrorHandlingIT extends AbstractRestFunctionalTestBase { 32 | 33 | @Test 34 | public void shouldReturn404OnMissingResource() throws Exception { 35 | JaxRsResponse response = RestRequest.req().post(getDataUri() + "some/bs/resource123/lol", "[]"); 36 | assertEquals(404, response.getStatus()); 37 | response.close(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/org/neo4j/smack/integration/api/NodeServiceIT.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.integration.api; 2 | 3 | import static org.neo4j.helpers.collection.MapUtil.map; 4 | 5 | import javax.ws.rs.core.Response; 6 | 7 | import org.junit.Test; 8 | import org.neo4j.smack.test.util.AbstractRestFunctionalTestBase; 9 | 10 | /** 11 | * @author mh 12 | * @since 14.11.11 13 | */ 14 | public class NodeServiceIT extends AbstractRestFunctionalTestBase { 15 | 16 | @Test 17 | public void testCreateNodeWithProperties() throws Exception { 18 | rest.to("/db/data/node").post(map("name","John","age",10)).created().location("/db/data/node/\\d+").expect("data.name", "John").expect("data.age", 10).compareNodeProperties("age", "name"); 19 | } 20 | 21 | @Test 22 | public void testCreateNode() throws Exception { 23 | rest.to("/db/data/node").post().created().location("/db/data/node/\\d+").expect("data", map()).compareNodeProperties("!name"); 24 | } 25 | 26 | @Test 27 | public void testCreateNodeWithInvalidProperty() throws Exception { 28 | rest.to("/db/data/node").post(map("name",null)).assertStatus(Response.Status.BAD_REQUEST); 29 | } 30 | 31 | @Test 32 | public void testSetNodeProperty() throws Exception { 33 | rest.to("/db/data/node/0/properties/foo").put("bar").noContent().checkNodeProperty(0, "foo", "bar"); 34 | } 35 | 36 | @Test 37 | public void testReplaceNodeProperties() throws Exception { 38 | rest.to("/db/data/node/0/properties").put(map("foo","bar")).noContent().checkNodeProperty(0, "foo", "bar").checkNodeProperty(0, "!name", null); 39 | } 40 | 41 | @Test 42 | public void testGetNode() throws Exception { 43 | final String rootNodeUri = "/db/data/node/0"; 44 | rest.from(rootNodeUri).get().ok().expect("self", rootNodeUri); 45 | } 46 | 47 | @Test 48 | public void testGetNonExistingNode() throws Exception { 49 | rest.from("/db/data/node/" + 9999).get().notFound(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/org/neo4j/smack/integration/api/RelationshipServiceIT.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.integration.api; 2 | 3 | import static org.neo4j.helpers.collection.MapUtil.map; 4 | 5 | import javax.ws.rs.core.Response; 6 | 7 | import org.junit.Test; 8 | import org.neo4j.smack.test.util.AbstractRestFunctionalTestBase; 9 | 10 | public class RelationshipServiceIT extends AbstractRestFunctionalTestBase { 11 | 12 | @Test 13 | public void testCreateRelationshipWithProperties() throws Exception 14 | { 15 | String nodeOne = rest.to("/db/data/node").post().created().location(); 16 | String nodeTwo = rest.to("/db/data/node").post().created().location(); 17 | rest.to(nodeOne + "/relationships").post(map("to",nodeTwo,"type","LOVES","data",map("since","2012"))).created() 18 | .location("/db/data/relationship/\\d+") 19 | .expect("data.since", "2012") 20 | .compareRelationshipProperties("since"); 21 | } 22 | 23 | @Test 24 | public void testCreateRelationship() throws Exception { 25 | String nodeOne = rest.to("/db/data/node").post().created().location(); 26 | String nodeTwo = rest.to("/db/data/node").post().created().location(); 27 | rest.to(nodeOne + "/relationships").post(map("to",nodeTwo,"type","LOVES")) 28 | .created().location("/db/data/relationship/\\d+"); 29 | } 30 | 31 | @Test 32 | public void testCreateRelationshipWithInvalidProperty() throws Exception { 33 | String nodeOne = rest.to("/db/data/node").post().created().location(); 34 | String nodeTwo = rest.to("/db/data/node").post().created().location(); 35 | rest.to(nodeOne + "/relationships").post(map("to",nodeTwo,"type","LOVES","data",map("name",null))).assertStatus(Response.Status.BAD_REQUEST); 36 | } 37 | 38 | @Test 39 | public void testSetRelationshipProperty() throws Exception { 40 | String nodeOne = rest.to("/db/data/node").post().created().location(); 41 | String nodeTwo = rest.to("/db/data/node").post().created().location(); 42 | String relationship = rest.to(nodeOne + "/relationships").post(map("to",nodeTwo,"type","LOVES")) 43 | .created().location(); 44 | 45 | rest.to(relationship + "/properties/foo").put("bar").noContent().checkRelationshipProperty(relationship, "foo", "bar"); 46 | } 47 | 48 | @Test 49 | public void testReplaceRelationshipProperties() throws Exception { 50 | String nodeOne = rest.to("/db/data/node").post().created().location(); 51 | String nodeTwo = rest.to("/db/data/node").post().created().location(); 52 | String relationship = rest.to(nodeOne + "/relationships").post(map("to",nodeTwo,"type","LOVES")) 53 | .created().location(); 54 | 55 | rest.to(relationship + "/properties/name").put("bar").noContent().checkRelationshipProperty(relationship, "name", "bar"); 56 | rest.to(relationship + "/properties").put(map("foo","bar")).noContent().checkRelationshipProperty(relationship, "foo", "bar").checkRelationshipProperty(relationship, "!name", null); 57 | } 58 | 59 | @Test 60 | public void testGetRelationship() throws Exception { 61 | String nodeOne = rest.to("/db/data/node").post().created().location(); 62 | String nodeTwo = rest.to("/db/data/node").post().created().location(); 63 | String relationship = rest.to(nodeOne + "/relationships").post(map("to",nodeTwo,"type","LOVES")) 64 | .created().location(); 65 | 66 | rest.from(relationship).get().ok().expect("self", relationship); 67 | } 68 | 69 | @Test 70 | public void testDeleteRelationship() throws Exception { 71 | String nodeOne = rest.to("/db/data/node").post().created().location(); 72 | String nodeTwo = rest.to("/db/data/node").post().created().location(); 73 | String relationship = rest.to(nodeOne + "/relationships").post(map("to",nodeTwo,"type","LOVES")) 74 | .created().location(); 75 | 76 | rest.from(relationship).get().ok().expect("self", relationship); 77 | rest.to(relationship).delete().ok(); 78 | rest.from(relationship).get().notFound(); 79 | } 80 | 81 | @Test 82 | public void testGetNonExistingRelationship() throws Exception { 83 | rest.from("/db/data/relationship/" + 9999).get().notFound(); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/test/java/org/neo4j/smack/integration/api/TransactionServiceIT.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.integration.api; 2 | 3 | import org.junit.Test; 4 | import org.neo4j.smack.test.util.AbstractRestFunctionalTestBase; 5 | 6 | public class TransactionServiceIT extends AbstractRestFunctionalTestBase { 7 | 8 | @Test 9 | public void testCreateAndCommitTx() throws Exception { 10 | String tx = rest.to("/db/data/tx").post().created().location("/db/data/tx/\\d+").location(); 11 | 12 | String node = rest.to(tx + "/node").post().location(); 13 | 14 | rest.to(tx + "/state").put("COMMITTED").ok(); 15 | 16 | rest.to(node).get().ok(); 17 | } 18 | 19 | @Test 20 | public void testCreateAndRollbackTx() throws Exception { 21 | String tx = rest.to("/db/data/tx").post().created().location("/db/data/tx/\\d+").location(); 22 | 23 | String node = rest.to(tx + "/node").post().location(); 24 | 25 | rest.to(tx + "/state").put("ROLLED_BACK").ok(); 26 | 27 | rest.to(node).get().notFound(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/test/java/org/neo4j/smack/performance/LoadGeneratingRunnable.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.performance; 2 | 3 | import org.neo4j.smack.test.util.FixedRequestClient; 4 | import org.neo4j.smack.test.util.PerformanceRoutes; 5 | 6 | public class LoadGeneratingRunnable implements Runnable { 7 | 8 | 9 | private FixedRequestClient client; 10 | private long numRequestsToSend; 11 | private boolean logStuff; 12 | private int numWorkers; 13 | private int requestsPerBatch = 1000; 14 | 15 | public LoadGeneratingRunnable(boolean logStuff, long numRequestsToSend, int numWorkers){ 16 | client = new FixedRequestClient("localhost", 7473, PerformanceRoutes.NO_SERIALIZATION_AND_NO_DESERIALIZATION_AND_NO_INTROSPECTION, requestsPerBatch ); 17 | this.logStuff = logStuff; 18 | this.numRequestsToSend = numRequestsToSend; 19 | this.numWorkers = numWorkers; 20 | } 21 | 22 | @Override 23 | public void run() 24 | { 25 | for(int i=0;i entries = rd.getRouteDefinitionEntries(); 52 | 53 | assertThat(entries.size(), is(5)); 54 | assertThat(entries.get(0).getPath(), is("")); 55 | } 56 | 57 | @Test 58 | public void shouldPickupPathAnnotation() { 59 | Object annotatedObject = new Object() { 60 | @GET 61 | @Path("/hello/world") 62 | @SuppressWarnings("unused") 63 | public void a() { } 64 | }; 65 | 66 | AnnotationBasedRoutingDefinition rd = new AnnotationBasedRoutingDefinition(annotatedObject); 67 | 68 | List entries = rd.getRouteDefinitionEntries(); 69 | 70 | assertThat(entries.size(), is(1)); 71 | assertThat(entries.get(0).getPath(), is("/hello/world")); 72 | } 73 | 74 | @Test 75 | public void shouldPickUpTransactionalAnnotation() { 76 | Object transactionalAnnotatedObject = new Object() { 77 | 78 | @PUT 79 | @Path("/{tx_id}/state") 80 | @Transactional 81 | @SuppressWarnings("unused") 82 | public void someRandomMethod() { } 83 | 84 | }; 85 | 86 | Object notTransactionalAnnotatedObject = new Object() { 87 | 88 | @PUT 89 | @Path("/{tx_id}/state") 90 | @SuppressWarnings("unused") 91 | public void someRandomMethod() { } 92 | 93 | }; 94 | 95 | AnnotationBasedRoutingDefinition transactionalDef = new AnnotationBasedRoutingDefinition(transactionalAnnotatedObject); 96 | AnnotationBasedRoutingDefinition nonTransactionalDef = new AnnotationBasedRoutingDefinition(notTransactionalAnnotatedObject); 97 | 98 | Endpoint transactional = transactionalDef.getRouteDefinitionEntries().get(0).getEndpoint(); 99 | Endpoint nontransactional = nonTransactionalDef.getRouteDefinitionEntries().get(0).getEndpoint(); 100 | 101 | assertThat(transactional.isTransactional(), is(true)); 102 | assertThat(nontransactional.isTransactional(), is(false)); 103 | 104 | 105 | 106 | } 107 | 108 | @Test 109 | public void shouldPickUpAppropriateSerializationAndDeserializationStrategies() { 110 | Object annotatedObject = new Object() { 111 | @GET 112 | @Path("/hello/world") 113 | @SuppressWarnings("unused") 114 | @SerializeWith(NodeSerializationStrategy.class) 115 | @DeserializeWith(PropertyContainerDeserializationStrategy.class) 116 | public void a() { } 117 | }; 118 | 119 | AnnotationBasedRoutingDefinition rd = new AnnotationBasedRoutingDefinition(annotatedObject); 120 | RouteDefinitionEntry route = rd.getRouteDefinitionEntries().get(0); 121 | 122 | assertThat(route.getEndpoint().getSerializationStrategy(), is(NodeSerializationStrategy.class)); 123 | assertThat(route.getEndpoint().getDeserializationStrategy(), is(PropertyContainerDeserializationStrategy.class)); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/test/java/org/neo4j/smack/routing/TestResettableQueryStringDecoder.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.routing; 2 | 3 | import static org.hamcrest.Matchers.is; 4 | import static org.junit.Assert.assertThat; 5 | 6 | import org.jboss.netty.util.CharsetUtil; 7 | import org.junit.Test; 8 | 9 | 10 | public class TestResettableQueryStringDecoder { 11 | 12 | @Test 13 | public void testDecodeAndReset() { 14 | ResettableQueryStringDecoder decoder = new ResettableQueryStringDecoder(); 15 | decoder.reset("/db/data", CharsetUtil.UTF_8); 16 | 17 | assertThat(decoder.getParameters().size(), is(0)); 18 | 19 | decoder.reset("/db/da", CharsetUtil.UTF_8); 20 | 21 | assertThat(decoder.getParameters().size(), is(0)); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/org/neo4j/smack/routing/TestRouter.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.routing; 2 | 3 | import static org.hamcrest.Matchers.instanceOf; 4 | import static org.hamcrest.Matchers.is; 5 | import static org.junit.Assert.assertNotNull; 6 | import static org.junit.Assert.assertThat; 7 | 8 | import org.junit.Test; 9 | import org.neo4j.smack.pipeline.database.event.Invocation; 10 | import org.neo4j.smack.pipeline.database.event.Output; 11 | import org.neo4j.smack.serialization.DeserializationStrategy; 12 | import org.neo4j.smack.serialization.SerializationStrategy; 13 | 14 | public class TestRouter { 15 | 16 | class Routling implements Routable { 17 | private String path; 18 | private InvocationVerb verb; 19 | private PathVariables pathVariables = new PathVariables(); 20 | 21 | Routling(InvocationVerb verb, String path) { 22 | this.verb = verb; 23 | this.path = path; 24 | } 25 | 26 | @Override 27 | public String getPath() 28 | { 29 | return path; 30 | } 31 | 32 | @Override 33 | public InvocationVerb getVerb() 34 | { 35 | return verb; 36 | } 37 | 38 | @Override 39 | public PathVariables getPathVariables() 40 | { 41 | return pathVariables; 42 | } 43 | } 44 | 45 | @Test 46 | public void shouldRouteVerbsCorrectly() { 47 | Endpoint e = new Endpoint() { 48 | 49 | @Override 50 | public void invoke(Invocation ctx, Output result) throws Exception { } 51 | 52 | @Override 53 | public InvocationVerb getVerb() { 54 | return InvocationVerb.GET; 55 | } 56 | 57 | @Override 58 | public DeserializationStrategy getDeserializationStrategy() { 59 | return DeserializationStrategy.NO_OP; 60 | } 61 | 62 | public SerializationStrategy getSerializationStrategy() { 63 | return SerializationStrategy.NO_OP; 64 | } 65 | 66 | public boolean isTransactional() { 67 | return false; 68 | } 69 | }; 70 | 71 | Router r = new Router(); 72 | r.addRoute("/db/data", e); 73 | r.compileRoutes(); 74 | 75 | Endpoint found = r.route(new Routling(InvocationVerb.GET, "/db/data")); 76 | assertNotNull(found); 77 | 78 | Endpoint endpoint = r.route(new Routling(InvocationVerb.POST, "/db/data")); 79 | assertThat(endpoint, instanceOf(NotFoundEndpoint.class)); 80 | } 81 | 82 | @Test 83 | public void shouldRouteSimplePathsCorrectly() { 84 | Endpoint e = new Endpoint() { 85 | 86 | @Override 87 | public void invoke(Invocation ctx, 88 | Output response) throws Exception { } 89 | 90 | @Override 91 | public InvocationVerb getVerb() { 92 | return InvocationVerb.GET; 93 | } 94 | 95 | @Override 96 | public DeserializationStrategy getDeserializationStrategy() { 97 | return DeserializationStrategy.NO_OP; 98 | } 99 | 100 | public SerializationStrategy getSerializationStrategy() { 101 | return SerializationStrategy.NO_OP; 102 | } 103 | 104 | public boolean isTransactional() { 105 | return false; 106 | } 107 | }; 108 | 109 | Router r = new Router(); 110 | r.addRoute("/db/data", e); 111 | r.compileRoutes(); 112 | 113 | Endpoint found = r.route(new Routling(InvocationVerb.GET, "/db/data")); 114 | assertThat(found, is(e)); 115 | 116 | Endpoint endpoint = r.route(new Routling(InvocationVerb.GET, "/db/da")); 117 | assertThat(endpoint, instanceOf(NotFoundEndpoint.class)); 118 | } 119 | 120 | @Test 121 | public void shouldRoutePathsWithParamsProperly() { 122 | Endpoint e = new SimpleEndpoint() { 123 | 124 | @Override 125 | public void invoke(Invocation ctx, 126 | Output response) throws Exception { } 127 | 128 | }; 129 | 130 | Router r = new Router(); 131 | r.addRoute("/db/data/{prop}", e); 132 | r.compileRoutes(); 133 | 134 | Endpoint found = r.route(new Routling(InvocationVerb.GET, "/db/data/somestuff")); 135 | assertThat(found, is(e)); 136 | 137 | Endpoint endpoint = r.route(new Routling(InvocationVerb.GET, "/db/data/somestuff/whaa")); 138 | assertThat(endpoint, instanceOf(NotFoundEndpoint.class)); 139 | } 140 | 141 | } 142 | -------------------------------------------------------------------------------- /src/test/java/org/neo4j/smack/serialization/TestDeserializationStrategy.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.serialization; 2 | 3 | import static org.hamcrest.Matchers.not; 4 | import static org.hamcrest.Matchers.nullValue; 5 | import static org.junit.Assert.assertThat; 6 | 7 | import java.io.ByteArrayInputStream; 8 | import java.io.InputStream; 9 | 10 | import org.codehaus.jackson.JsonFactory; 11 | import org.codehaus.jackson.map.ObjectMapper; 12 | import org.junit.Test; 13 | 14 | public class TestDeserializationStrategy { 15 | 16 | @Test 17 | public void testSimpleDeserializationStrategy() throws Exception 18 | { 19 | InputStream in = new ByteArrayInputStream("{\"firstkey\":1,\"secondkey\":2}".getBytes("UTF-8")); 20 | JsonDeserializer d = new JsonDeserializer(new JsonFactory(new ObjectMapper()), in); 21 | 22 | DeserializationStrategy objectStrategy = new DeserializationStrategy() { 23 | @Override 24 | public Object deserialize(Deserializer in) 25 | throws DeserializationException 26 | { 27 | return in.readObject(); 28 | } 29 | }; 30 | 31 | assertThat(objectStrategy.deserialize(d),not(nullValue())); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/org/neo4j/smack/serialization/TestJsonDeserializer.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.serialization; 2 | 3 | import static org.hamcrest.Matchers.is; 4 | import static org.junit.Assert.assertThat; 5 | 6 | import java.io.ByteArrayInputStream; 7 | import java.io.InputStream; 8 | import java.util.Map; 9 | 10 | import org.codehaus.jackson.JsonFactory; 11 | import org.codehaus.jackson.map.ObjectMapper; 12 | import org.junit.Test; 13 | import org.neo4j.smack.serialization.JsonDeserializer; 14 | 15 | public class TestJsonDeserializer { 16 | 17 | @Test 18 | public void testReadingMap() throws Exception { 19 | InputStream in = new ByteArrayInputStream("{\"firstkey\":1,\"secondkey\":2}".getBytes("UTF-8")); 20 | 21 | JsonDeserializer d = new JsonDeserializer(new JsonFactory(new ObjectMapper()), in); 22 | 23 | Map deserialized = d.readMap(); 24 | 25 | assertThat(deserialized.containsKey("firstkey"), is(true)); 26 | assertThat(deserialized.containsKey("secondkey"), is(true)); 27 | 28 | assertThat((Integer)deserialized.get("firstkey"), is(1)); 29 | assertThat((Integer)deserialized.get("secondkey"), is(2)); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/org/neo4j/smack/serialization/TestSerializationStrategy.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.serialization; 2 | 3 | import static org.hamcrest.Matchers.is; 4 | import static org.junit.Assert.assertThat; 5 | 6 | import org.apache.commons.io.output.ByteArrayOutputStream; 7 | import org.codehaus.jackson.JsonFactory; 8 | import org.junit.Test; 9 | 10 | public class TestSerializationStrategy { 11 | 12 | @Test 13 | public void testSimpleSerialization() throws Exception 14 | { 15 | ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 16 | JsonSerializer serializer = new JsonSerializer(new JsonFactory(), buffer); 17 | 18 | SerializationStrategy simpleStrategy = new SerializationStrategy() { 19 | @Override 20 | public void serialize(Object value, Serializer out) 21 | throws SerializationException 22 | { 23 | out.startList(); 24 | out.putString("Hello!"); 25 | out.endList(); 26 | out.flush(); 27 | } 28 | }; 29 | 30 | simpleStrategy.serialize(null, serializer); 31 | 32 | assertThat(new String(buffer.toByteArray()),is("[\"Hello!\"]")); 33 | 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/org/neo4j/smack/serialization/strategy/SerializationStrategyTestBase.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.serialization.strategy; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.InputStream; 5 | 6 | import org.codehaus.jackson.JsonFactory; 7 | import org.codehaus.jackson.map.ObjectMapper; 8 | import org.neo4j.smack.serialization.DeserializationStrategy; 9 | import org.neo4j.smack.serialization.JsonDeserializer; 10 | 11 | public class SerializationStrategyTestBase { 12 | 13 | JsonFactory jsonFactory = new JsonFactory(new ObjectMapper()); 14 | 15 | public T deserialize(byte[] bytes, 16 | DeserializationStrategy strategy) 17 | { 18 | InputStream in = new ByteArrayInputStream(bytes); 19 | JsonDeserializer deserializer = new JsonDeserializer(jsonFactory, in); 20 | return strategy.deserialize(deserializer); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/test/java/org/neo4j/smack/serialization/strategy/TestNodeSerializationStrategy.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.serialization.strategy; 2 | 3 | import static org.hamcrest.Matchers.is; 4 | import static org.junit.Assert.assertThat; 5 | import static org.mockito.Mockito.mock; 6 | import static org.mockito.Mockito.when; 7 | 8 | import java.util.ArrayList; 9 | import java.util.Collections; 10 | 11 | import org.apache.commons.io.output.ByteArrayOutputStream; 12 | import org.codehaus.jackson.JsonFactory; 13 | import org.junit.Test; 14 | import org.neo4j.graphdb.Node; 15 | import org.neo4j.smack.serialization.JsonSerializer; 16 | 17 | public class TestNodeSerializationStrategy { 18 | 19 | @Test 20 | public void shouldSerializeNodeWithNoProperties() { 21 | 22 | ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 23 | JsonSerializer serializer = new JsonSerializer(new JsonFactory(), buffer); 24 | 25 | Node mockNode = mock(Node.class); 26 | when(mockNode.getId()).thenReturn(0l); 27 | when(mockNode.getPropertyKeys()).thenReturn(Collections. emptyList()); 28 | 29 | NodeSerializationStrategy strategy = new NodeSerializationStrategy(); 30 | strategy.serialize(mockNode, serializer); 31 | serializer.flush(); 32 | 33 | assertThat(new String(buffer.toByteArray()),is("{" + 34 | "\"data\":{}," + 35 | "\"self\":\"/db/data/node/0\"," + 36 | "\"extensions\":{}" + 37 | "}")); 38 | } 39 | 40 | @Test 41 | public void shouldSerializeNodeWithProperties() { 42 | 43 | ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 44 | JsonSerializer serializer = new JsonSerializer(new JsonFactory(), buffer); 45 | 46 | Node mockNode = mock(Node.class); 47 | when(mockNode.getId()).thenReturn(0l); 48 | when(mockNode.getPropertyKeys()).thenReturn(new ArrayList() { 49 | private static final long serialVersionUID = 1248180220593789023L; 50 | { 51 | add("name"); 52 | add("age"); 53 | add("favorite_numbers"); 54 | }}); 55 | 56 | when(mockNode.getProperty("name")).thenReturn("bob"); 57 | when(mockNode.getProperty("age")).thenReturn(12); 58 | when(mockNode.getProperty("favorite_numbers")).thenReturn(new int [] {1,2,3}); 59 | 60 | NodeSerializationStrategy strategy = new NodeSerializationStrategy(); 61 | strategy.serialize(mockNode, serializer); 62 | serializer.flush(); 63 | 64 | assertThat(new String(buffer.toByteArray()),is( 65 | "{" + 66 | "\"data\":{" + 67 | "\"name\":\"bob\"," + 68 | "\"age\":12," + 69 | "\"favorite_numbers\":[1,2,3]" + 70 | "}," + 71 | "\"self\":\"/db/data/node/0\"," + 72 | "\"extensions\":{}" + 73 | "}")); 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/test/java/org/neo4j/smack/serialization/strategy/TestPropertyContainerDeserializationStrategy.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.serialization.strategy; 2 | 3 | import static org.hamcrest.Matchers.is; 4 | import static org.junit.Assert.assertThat; 5 | 6 | import java.io.UnsupportedEncodingException; 7 | 8 | import org.junit.Test; 9 | 10 | 11 | public class TestPropertyContainerDeserializationStrategy extends SerializationStrategyTestBase { 12 | 13 | @Test 14 | public void shouldDeserializeEmptyMap() throws UnsupportedEncodingException 15 | { 16 | byte[] message = ("{}").getBytes("UTF-8"); 17 | 18 | PropertyContainerDeserialization deserialized = deserialize(message, new PropertyContainerDeserializationStrategy()); 19 | 20 | assertThat(deserialized.hasMoreProperties(), is(false)); 21 | } 22 | 23 | @Test 24 | public void shouldDeserializePrimitiveValues() throws UnsupportedEncodingException 25 | { 26 | byte[] message = ("{" + 27 | "\"name\":\"Steven\"," + 28 | "\"age\":12," + 29 | "\"human\":true," + 30 | "\"height\":4.5" + 31 | "}").getBytes("UTF-8"); 32 | 33 | PropertyContainerDeserialization deserialized = deserialize(message, new PropertyContainerDeserializationStrategy()); 34 | 35 | assertThat(deserialized.hasMoreProperties(), is(true)); 36 | deserialized.nextProperty(); 37 | 38 | assertThat(deserialized.propertyKey(), is("name")); 39 | assertThat((String)deserialized.propertyValue(), is("Steven")); 40 | assertThat(deserialized.hasMoreProperties(), is(true)); 41 | deserialized.nextProperty(); 42 | 43 | assertThat(deserialized.propertyKey(), is("age")); 44 | assertThat((Integer)deserialized.propertyValue(), is(12)); 45 | assertThat(deserialized.hasMoreProperties(), is(true)); 46 | deserialized.nextProperty(); 47 | 48 | assertThat(deserialized.propertyKey(), is("human")); 49 | assertThat((Boolean)deserialized.propertyValue(), is(true)); 50 | assertThat(deserialized.hasMoreProperties(), is(true)); 51 | deserialized.nextProperty(); 52 | 53 | assertThat(deserialized.propertyKey(), is("height")); 54 | assertThat((Double)deserialized.propertyValue(), is(4.5d)); 55 | 56 | assertThat(deserialized.hasMoreProperties(), is(false)); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/test/java/org/neo4j/smack/serialization/strategy/TestRelationshipCreationDeserializationStrategy.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.serialization.strategy; 2 | 3 | import static org.hamcrest.Matchers.is; 4 | import static org.junit.Assert.assertThat; 5 | 6 | import java.io.UnsupportedEncodingException; 7 | 8 | import org.junit.Test; 9 | 10 | 11 | public class TestRelationshipCreationDeserializationStrategy extends SerializationStrategyTestBase { 12 | 13 | @Test 14 | public void shouldDeserializeOnlyTypeAndTo() throws UnsupportedEncodingException 15 | { 16 | byte[] message = ("{" + 17 | "\"to\":\"/db/data/node/44\"," + 18 | "\"type\":\"LOVES\"" + 19 | "}").getBytes("UTF-8"); 20 | 21 | RelationshipCreationDescription deserialized = deserialize(message, new RelationshipCreationDeserializationStrategy()); 22 | 23 | assertThat(deserialized.getType().name(), is("LOVES")); 24 | assertThat(deserialized.getEndNodeId(), is(44l)); 25 | 26 | assertThat(deserialized.hasMoreProperties(), is(false)); 27 | } 28 | 29 | @Test 30 | public void shouldDeserializeTypeToAndData() throws UnsupportedEncodingException 31 | { 32 | byte[] message = ("{" + 33 | "\"to\":\"/db/data/node/44\"," + 34 | "\"type\":\"LOVES\"," + 35 | "\"data\": {" + 36 | "\"name\":\"bob\"," + 37 | "\"age\":12" + 38 | "}" + 39 | "}").getBytes("UTF-8"); 40 | 41 | RelationshipCreationDescription deserialized = deserialize(message, new RelationshipCreationDeserializationStrategy()); 42 | 43 | assertThat(deserialized.getType().name(), is("LOVES")); 44 | assertThat(deserialized.getEndNodeId(), is(44l)); 45 | 46 | deserialized.nextProperty(); 47 | assertThat(deserialized.propertyKey(), is("name")); 48 | assertThat((String)deserialized.propertyValue(), is("bob")); 49 | 50 | assertThat(deserialized.hasMoreProperties(), is(true)); 51 | 52 | deserialized.nextProperty(); 53 | assertThat(deserialized.propertyKey(), is("age")); 54 | assertThat((Integer)deserialized.propertyValue(), is(12)); 55 | 56 | assertThat(deserialized.hasMoreProperties(), is(false)); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/test/java/org/neo4j/smack/serialization/strategy/TestRelationshipSerializationStrategy.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.serialization.strategy; 2 | 3 | import static org.hamcrest.Matchers.is; 4 | import static org.junit.Assert.assertThat; 5 | import static org.mockito.Mockito.mock; 6 | import static org.mockito.Mockito.when; 7 | 8 | import java.util.ArrayList; 9 | import java.util.Collections; 10 | 11 | import org.apache.commons.io.output.ByteArrayOutputStream; 12 | import org.codehaus.jackson.JsonFactory; 13 | import org.junit.Test; 14 | import org.neo4j.graphdb.DynamicRelationshipType; 15 | import org.neo4j.graphdb.Node; 16 | import org.neo4j.graphdb.Relationship; 17 | import org.neo4j.smack.serialization.JsonSerializer; 18 | 19 | 20 | public class TestRelationshipSerializationStrategy { 21 | 22 | @Test 23 | public void shouldSerializeRelationshipWithNoProperties() { 24 | 25 | ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 26 | JsonSerializer serializer = new JsonSerializer(new JsonFactory(), buffer); 27 | 28 | Node fromNode = mock(Node.class); 29 | when(fromNode.getId()).thenReturn(0l); 30 | 31 | Node toNode = mock(Node.class); 32 | when(toNode.getId()).thenReturn(1l); 33 | 34 | Relationship mockRel = mock(Relationship.class); 35 | when(mockRel.getId()).thenReturn(0l); 36 | when(mockRel.getType()).thenReturn(DynamicRelationshipType.withName("LOVES")); 37 | when(mockRel.getStartNode()).thenReturn(fromNode); 38 | when(mockRel.getEndNode()).thenReturn(toNode); 39 | 40 | when(mockRel.getPropertyKeys()).thenReturn(Collections. emptyList()); 41 | 42 | RelationshipSerializationStrategy strategy = new RelationshipSerializationStrategy(); 43 | strategy.serialize(mockRel, serializer); 44 | serializer.flush(); 45 | 46 | assertThat(new String(buffer.toByteArray()),is("{" + 47 | "\"self\":\"/db/data/relationship/0\"," + 48 | "\"type\":\"LOVES\"," + 49 | "\"start\":\"/db/data/node/0\"," + 50 | "\"end\":\"/db/data/node/1\"," + 51 | "\"data\":{}," + 52 | "\"extensions\":{}" + 53 | "}")); 54 | } 55 | 56 | @Test 57 | public void shouldSerializeNodeWithProperties() { 58 | 59 | ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 60 | JsonSerializer serializer = new JsonSerializer(new JsonFactory(), buffer); 61 | 62 | Node fromNode = mock(Node.class); 63 | when(fromNode.getId()).thenReturn(0l); 64 | 65 | Node toNode = mock(Node.class); 66 | when(toNode.getId()).thenReturn(1l); 67 | 68 | Relationship mockRel = mock(Relationship.class); 69 | when(mockRel.getId()).thenReturn(0l); 70 | when(mockRel.getType()).thenReturn(DynamicRelationshipType.withName("LOVES")); 71 | when(mockRel.getStartNode()).thenReturn(fromNode); 72 | when(mockRel.getEndNode()).thenReturn(toNode); 73 | 74 | when(mockRel.getPropertyKeys()).thenReturn(new ArrayList() { 75 | private static final long serialVersionUID = 1248180220593789023L; 76 | { 77 | add("name"); 78 | add("age"); 79 | add("favorite_numbers"); 80 | }}); 81 | 82 | when(mockRel.getProperty("name")).thenReturn("bob"); 83 | when(mockRel.getProperty("age")).thenReturn(12); 84 | when(mockRel.getProperty("favorite_numbers")).thenReturn(new int [] {1,2,3}); 85 | 86 | RelationshipSerializationStrategy strategy = new RelationshipSerializationStrategy(); 87 | strategy.serialize(mockRel, serializer); 88 | serializer.flush(); 89 | 90 | assertThat(new String(buffer.toByteArray()),is( 91 | "{" + 92 | "\"self\":\"/db/data/relationship/0\"," + 93 | "\"type\":\"LOVES\"," + 94 | "\"start\":\"/db/data/node/0\"," + 95 | "\"end\":\"/db/data/node/1\"," + 96 | "\"data\":{" + 97 | "\"name\":\"bob\"," + 98 | "\"age\":12," + 99 | "\"favorite_numbers\":[1,2,3]" + 100 | "}," + 101 | "\"extensions\":{}" + 102 | "}")); 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /src/test/java/org/neo4j/smack/serialization/strategy/TestTransactionStateDeserializationStrategy.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.serialization.strategy; 2 | 3 | import static org.hamcrest.Matchers.is; 4 | import static org.junit.Assert.assertThat; 5 | 6 | import java.io.UnsupportedEncodingException; 7 | 8 | import org.junit.Test; 9 | 10 | 11 | public class TestTransactionStateDeserializationStrategy extends SerializationStrategyTestBase { 12 | 13 | @Test 14 | public void shouldDeserializeCommittedValue() throws UnsupportedEncodingException 15 | { 16 | assertThat(deserialize("\"COMMITTED\"".getBytes("UTF-8"), new TransactionStateDeserializationStrategy()), 17 | is(TransactionStateDeserialization.COMMITTED)); 18 | } 19 | 20 | @Test 21 | public void shouldDeserializeRollbackValue() throws UnsupportedEncodingException 22 | { 23 | assertThat(deserialize("\"ROLLED_BACK\"".getBytes("UTF-8"), new TransactionStateDeserializationStrategy()), 24 | is(TransactionStateDeserialization.ROLLED_BACK)); 25 | } 26 | 27 | @Test(expected=IllegalArgumentException.class) 28 | public void shouldThrowIllegalArgumentExceptionOnInvalidState() throws UnsupportedEncodingException 29 | { 30 | deserialize("\"BLAH\"".getBytes("UTF-8"), new TransactionStateDeserializationStrategy()); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/org/neo4j/smack/test/util/FixedRequestClient.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.test.util; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | 5 | import org.jboss.netty.buffer.ChannelBuffer; 6 | import org.jboss.netty.buffer.ChannelBuffers; 7 | import org.jboss.netty.handler.codec.http.HttpHeaders; 8 | import org.jboss.netty.handler.codec.http.HttpVersion; 9 | 10 | /** 11 | * A http client for performance testing, 12 | * builds a byte array upon instantiation with 13 | * a fixed request to send, saving precious microseconds. 14 | */ 15 | public class FixedRequestClient extends PipelinedHttpClient { 16 | 17 | private ChannelBuffer buf; 18 | public FixedRequestClient(String host, int port, String path, int numRequestsPerBatch) 19 | { 20 | super(host, port); 21 | initRequestBuffer(numRequestsPerBatch, path, host); 22 | } 23 | 24 | private void initRequestBuffer(int numRequestsPerBatch, String path, String host) 25 | { 26 | buf = ChannelBuffers.dynamicBuffer(100); 27 | try { 28 | for(int i=0;i metaData; 19 | private final MultivaluedMap headers; 20 | private final URI location; 21 | private String data; 22 | private MediaType type; 23 | 24 | public JaxRsResponse( ClientResponse response ) 25 | { 26 | this(response, extractContent(response)); 27 | } 28 | 29 | private static String extractContent(ClientResponse response) { 30 | if (response.getStatus() == Status.NO_CONTENT.getStatusCode()) return null; 31 | return response.getEntity(String.class); 32 | } 33 | 34 | public JaxRsResponse(ClientResponse response, String entity) { 35 | status = response.getStatus(); 36 | metaData = extractMetaData(response); 37 | headers = extractHeaders(response); 38 | location = response.getLocation(); 39 | type = response.getType(); 40 | data = entity; 41 | response.close(); 42 | } 43 | 44 | @Override 45 | public String getEntity() 46 | { 47 | return data; 48 | } 49 | 50 | @Override 51 | public int getStatus() 52 | { 53 | return status; 54 | } 55 | 56 | @Override 57 | public MultivaluedMap getMetadata() 58 | { 59 | return metaData; 60 | } 61 | 62 | private MultivaluedMap extractMetaData(ClientResponse jettyResponse) { 63 | MultivaluedMap metadata = new StringKeyObjectValueIgnoreCaseMultivaluedMap(); 64 | for ( Map.Entry> header : jettyResponse.getHeaders() 65 | .entrySet() ) 66 | { 67 | for ( Object value : header.getValue() ) 68 | { 69 | metadata.putSingle( header.getKey(), value ); 70 | } 71 | } 72 | return metadata; 73 | } 74 | 75 | public MultivaluedMap getHeaders() 76 | { 77 | return headers; 78 | } 79 | 80 | private MultivaluedMap extractHeaders(ClientResponse jettyResponse) { 81 | return jettyResponse.getHeaders(); 82 | } 83 | 84 | // new URI( getHeaders().get( HttpHeaders.LOCATION ).get(0)); 85 | public URI getLocation() 86 | { 87 | return location; 88 | } 89 | 90 | public void close() 91 | { 92 | 93 | } 94 | 95 | public static JaxRsResponse extractFrom(ClientResponse clientResponse) { 96 | return new JaxRsResponse(clientResponse); 97 | } 98 | 99 | public MediaType getType() { 100 | return type; 101 | } 102 | 103 | public String getEntity(Class type) { 104 | return getEntity(); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/test/java/org/neo4j/smack/test/util/JsonHelper.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.test.util; 2 | 3 | import java.io.IOException; 4 | import java.io.StringWriter; 5 | import java.util.Collection; 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | import org.codehaus.jackson.JsonGenerator; 10 | import org.codehaus.jackson.JsonParseException; 11 | import org.codehaus.jackson.map.ObjectMapper; 12 | 13 | public class JsonHelper 14 | { 15 | 16 | static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); 17 | 18 | @SuppressWarnings( "unchecked" ) 19 | public static Map jsonToMap( String json ) throws JsonParseException, IOException 20 | { 21 | return (Map) readJson( json ); 22 | } 23 | 24 | @SuppressWarnings( "unchecked" ) 25 | public static List> jsonToList( String json ) throws JsonParseException, IOException 26 | { 27 | return (List>) readJson( json ); 28 | } 29 | 30 | public static Object readJson( String json ) throws JsonParseException, IOException 31 | { 32 | ObjectMapper mapper = new ObjectMapper(); 33 | return mapper.readValue( json, Object.class ); 34 | } 35 | 36 | public static Object jsonToSingleValue( String json ) throws JsonParseException, IOException 37 | { 38 | Object jsonObject = readJson( json ); 39 | return jsonObject instanceof Collection ? jsonObject : assertSupportedPropertyValue( jsonObject ); 40 | } 41 | 42 | private static Object assertSupportedPropertyValue( Object jsonObject ) 43 | { 44 | if ( jsonObject == null ) 45 | { 46 | throw new RuntimeException( "null value not supported" ); 47 | 48 | } 49 | 50 | if ( jsonObject instanceof String ) 51 | { 52 | } 53 | else if ( jsonObject instanceof Number ) 54 | { 55 | } 56 | else if ( jsonObject instanceof Boolean ) 57 | { 58 | } 59 | else 60 | { 61 | throw new RuntimeException( 62 | "Unsupported value type " + jsonObject.getClass() + "." 63 | + " Supported value types are all java primitives (byte, char, short, int, " 64 | + "long, float, double) and String, as well as arrays of all those types" ); 65 | } 66 | return jsonObject; 67 | } 68 | 69 | public static String createJsonFrom( Object data ) throws IOException 70 | { 71 | StringWriter writer = new StringWriter(); 72 | JsonGenerator generator = OBJECT_MAPPER.getJsonFactory() 73 | .createJsonGenerator( writer ) 74 | .useDefaultPrettyPrinter(); 75 | OBJECT_MAPPER.writeValue( generator, data ); 76 | writer.close(); 77 | return writer.getBuffer() 78 | .toString(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/test/java/org/neo4j/smack/test/util/PerformanceRoutes.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.test.util; 2 | 3 | import javax.ws.rs.GET; 4 | import javax.ws.rs.Path; 5 | 6 | import org.neo4j.smack.pipeline.database.event.Invocation; 7 | import org.neo4j.smack.pipeline.database.event.NettyChannelBackedOutput; 8 | import org.neo4j.smack.pipeline.database.event.Output; 9 | import org.neo4j.smack.routing.Endpoint; 10 | import org.neo4j.smack.routing.InvocationVerb; 11 | import org.neo4j.smack.routing.RoutingDefinition; 12 | import org.neo4j.smack.serialization.DeserializationStrategy; 13 | import org.neo4j.smack.serialization.SerializationStrategy; 14 | 15 | public class PerformanceRoutes extends RoutingDefinition { 16 | 17 | public static final String NO_SERIALIZATION_AND_NO_DESERIALIZATION = "/noserialnodeserial"; 18 | public static final String NO_SERIALIZATION_AND_NO_DESERIALIZATION_AND_NO_INTROSPECTION = "/noserialnodeserialnointro"; 19 | 20 | { 21 | addRoute("", new Object() { 22 | @GET 23 | @Path(NO_SERIALIZATION_AND_NO_DESERIALIZATION) 24 | public void noSerializationAndNoDeserialization(Invocation req, NettyChannelBackedOutput res) { 25 | res.ok(); 26 | } 27 | }); 28 | 29 | addRoute(NO_SERIALIZATION_AND_NO_DESERIALIZATION_AND_NO_INTROSPECTION, new Endpoint() { 30 | 31 | @Override 32 | public void invoke(Invocation invocation, Output result) 33 | throws Exception 34 | { 35 | result.ok(); 36 | } 37 | 38 | @Override 39 | public InvocationVerb getVerb() 40 | { 41 | return InvocationVerb.GET; 42 | } 43 | 44 | @Override 45 | public DeserializationStrategy getDeserializationStrategy() 46 | { 47 | return DeserializationStrategy.NO_OP; 48 | } 49 | 50 | @Override 51 | public SerializationStrategy getSerializationStrategy() 52 | { 53 | return SerializationStrategy.NO_OP; 54 | } 55 | 56 | @Override 57 | public boolean isTransactional() 58 | { 59 | return false; 60 | } 61 | 62 | }); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/test/java/org/neo4j/smack/test/util/ServerHelper.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.test.util; 2 | 3 | import org.neo4j.graphdb.Node; 4 | import org.neo4j.graphdb.Relationship; 5 | import org.neo4j.smack.Neo4jServer; 6 | import org.neo4j.smack.Smack; 7 | import org.neo4j.test.ImpermanentGraphDatabase; 8 | import org.neo4j.tooling.GlobalGraphOperations; 9 | 10 | public class ServerHelper { 11 | 12 | public static final String HOST = "localhost"; 13 | public static final int PORT = 7473; 14 | 15 | public static Neo4jServer createServer() { 16 | return new Neo4jServer(HOST, PORT, new ImpermanentGraphDatabase()); 17 | } 18 | 19 | public static void cleanTheDatabase(final Neo4jServer server) { 20 | new Transactor( server.getSmackServer().getDatabase(), new UnitOfWork() 21 | { 22 | @Override 23 | public void doWork() 24 | { 25 | deleteAllNodesAndRelationships( server.getSmackServer() ); 26 | 27 | deleteAllIndexes( server.getSmackServer() ); 28 | } 29 | 30 | private void deleteAllNodesAndRelationships( final Smack server ) 31 | { 32 | Iterable allNodes = GlobalGraphOperations.at( server.getDatabase() ).getAllNodes(); 33 | for ( Node n : allNodes ) 34 | { 35 | Iterable relationships = n.getRelationships(); 36 | for ( Relationship rel : relationships ) 37 | { 38 | rel.delete(); 39 | } 40 | if ( n.getId() != 0 ) 41 | { // Don't delete the reference node - tests depend on it 42 | // :-( 43 | n.delete(); 44 | } 45 | else 46 | { // Remove all state from the reference node instead 47 | for ( String key : n.getPropertyKeys() ) 48 | { 49 | n.removeProperty( key ); 50 | } 51 | } 52 | } 53 | } 54 | 55 | private void deleteAllIndexes( final Smack server ) 56 | { 57 | for ( String indexName : server.getDatabase().index() 58 | .nodeIndexNames() ) 59 | { 60 | try{ 61 | server.getDatabase().index() 62 | .forNodes( indexName ) 63 | .delete(); 64 | } catch(UnsupportedOperationException e) { 65 | // Encountered a read-only index. 66 | } 67 | } 68 | 69 | for ( String indexName : server.getDatabase().index() 70 | .relationshipIndexNames() ) 71 | { 72 | try { 73 | server.getDatabase().index() 74 | .forRelationships( indexName ) 75 | .delete(); 76 | } catch(UnsupportedOperationException e) { 77 | // Encountered a read-only index. 78 | } 79 | } 80 | } 81 | } ).execute(); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/test/java/org/neo4j/smack/test/util/ServerHolder.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.test.util; 2 | 3 | import java.io.IOException; 4 | 5 | import org.neo4j.smack.Neo4jServer; 6 | 7 | public class ServerHolder extends Thread 8 | { 9 | private static AssertionError allocation; 10 | private static Neo4jServer server; 11 | 12 | static synchronized Neo4jServer allocate() throws IOException 13 | { 14 | if ( allocation != null ) throw allocation; 15 | if ( server == null ) server = startServer(); 16 | allocation = new AssertionError( "The server was allocated from here but not released properly" ); 17 | return server; 18 | } 19 | 20 | static synchronized void release( Neo4jServer server ) 21 | { 22 | if ( server == null ) return; 23 | if ( server != ServerHolder.server ) 24 | throw new AssertionError( "trying to release a server not allocated from here" ); 25 | if ( allocation == null ) throw new AssertionError( "releasing the server although it is not allocated" ); 26 | allocation = null; 27 | } 28 | 29 | static synchronized void ensureNotRunning() 30 | { 31 | if ( allocation != null ) throw allocation; 32 | shutdown(); 33 | } 34 | 35 | private static Neo4jServer startServer() throws IOException 36 | { 37 | Neo4jServer server = ServerHelper.createServer(); 38 | server.start(); 39 | return server; 40 | } 41 | 42 | private static synchronized void shutdown() 43 | { 44 | allocation = null; 45 | try 46 | { 47 | if ( server != null ) server.stop(); 48 | } 49 | finally 50 | { 51 | server = null; 52 | } 53 | } 54 | 55 | @Override 56 | public void run() 57 | { 58 | shutdown(); 59 | } 60 | 61 | static 62 | { 63 | Runtime.getRuntime().addShutdownHook( new ServerHolder() ); 64 | } 65 | 66 | private ServerHolder() 67 | { 68 | super( ServerHolder.class.getName() ); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/test/java/org/neo4j/smack/test/util/SharedSmackServerTestBase.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.test.util; 2 | 3 | import java.io.IOException; 4 | 5 | import org.junit.AfterClass; 6 | import org.junit.BeforeClass; 7 | import org.neo4j.smack.Neo4jServer; 8 | 9 | 10 | public class SharedSmackServerTestBase { 11 | 12 | private static Neo4jServer server; 13 | 14 | protected static final Neo4jServer server() 15 | { 16 | return server; 17 | } 18 | 19 | @BeforeClass 20 | public static void allocateServer() throws IOException 21 | { 22 | server = ServerHolder.allocate(); 23 | } 24 | 25 | @AfterClass 26 | public static final void releaseServer() 27 | { 28 | try 29 | { 30 | ServerHolder.release( server ); 31 | } 32 | finally 33 | { 34 | server = null; 35 | } 36 | } 37 | 38 | protected final void cleanDatabase() 39 | { 40 | ServerHelper.cleanTheDatabase( server ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/org/neo4j/smack/test/util/Transactor.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.test.util; 2 | 3 | import org.neo4j.graphdb.GraphDatabaseService; 4 | import org.neo4j.graphdb.Transaction; 5 | 6 | public class Transactor { 7 | private final UnitOfWork unitOfWork; 8 | private final GraphDatabaseService graphDb; 9 | 10 | public Transactor( GraphDatabaseService graphDb, UnitOfWork unitOfWork ) 11 | { 12 | this.unitOfWork = unitOfWork; 13 | this.graphDb = graphDb; 14 | } 15 | 16 | public void execute() 17 | { 18 | Transaction tx = graphDb.beginTx(); 19 | 20 | try 21 | { 22 | unitOfWork.doWork(); 23 | tx.success(); 24 | } 25 | finally 26 | { 27 | tx.finish(); 28 | } 29 | 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/org/neo4j/smack/test/util/UnitOfWork.java: -------------------------------------------------------------------------------- 1 | package org.neo4j.smack.test.util; 2 | 3 | public interface UnitOfWork { 4 | void doWork(); 5 | } 6 | --------------------------------------------------------------------------------