-
Notifications
You must be signed in to change notification settings - Fork 116
Extension Points
Extension points refer to the places from which Rexster can be extended and are defined by the @ExtensionDefinition
annotation which has an extensionPoint
parameter. This parameter must be defined on any method to be exposed as an extension on Rexster. The value of the extensionPoint
parameter is a selection from the ExtensionPoint
enumeration. The following extension point options are available:
extension point | uri extends from |
---|---|
GRAPH | http://{base}/graphs/{graph} |
EDGE | http://{base}/graphs/{graph}/edges/{id] |
VERTEX | http://{base}/graphs/{graph}/vertices/{id} |
By default, the @ExtensionDefinition
assumes that the ExtensionResponse
will return an entity of type application/json
. To override that value, simply supply the revised mime-type to the produces
parameter, as in:
@ExtensionDefinition(extensionPoint = ExtensionPoint.VERTEX, produces = MediaType.APPLICATION_XML)
@ExtensionDescriptor(description = "Turn a vertex into XML")
public ExtensionResponse doVertexToXml(@RexsterContext Vertex vertex) {
String xml = "<vertex><id>" + vertex.getId().toString() + "</id></vertex>";
return new ExtensionResponse(Response.ok(xml).build());
}
The code above is taken from the ProducesXmlExtension
in the Sample Kibbles project.
When the extension method produces JSON, Rexster tries to add some standard attributes to the response. It will look to add the version
and queryTime
as in:
{
"some-key":"some-value",
"other-key":"other-value",
"version":"*.*",
"queryTime":24.940088
}
To override this default behavior, Rexster can be instructed to not add these values to JSON responses by setting the tryIncludeRexsterAttributes
on the ExtensionDefinition
to false
. As stated earlier, this value is true
by default. In the event that the produces
value is set to something other than application/json
, Rexster will behave as though the value of tryIncludeRexsterAttributes
is false
.
The ExtensionDefinition
provides control over the HTTP method on the request that it will respond to through the method
parameter:
@ExtensionDefinition(extensionPoint = ExtensionPoint.GRAPH, method = HttpMethod.GET)
In the above example, the specification of HttpMethod.GET
makes it so that Rexster will only relay GET
based requests to this method. The HttpMethod
enumeration has the full range of standard HTTP methods: GET, PUT, POST, DELETE, HEAD, and OPTIONS. It is up to the Extension developer to ensure that the returned response meets HTTP standards as necessary (ie. HEAD will not return and entity body). By default the value of method
is set to HttpMethod.ANY
which means that all requests, regardless of the HTTP method, will be funneled through to the Extension.
It’s important to note that when implementing methods other than GET or POST that you should also consider implementation of the OPTIONS method. Depending upon your environment, HTTP Access Control may come into play and prevent advanced methods like DELETE and PUT from working properly. Typically, this scenario occurs when cross-site HTTP requests are made and requests to the extension are met with an error like:
Method DELETE is not allowed by Access-Control-Allow-Methods
By implementing OPTIONS, the pre-flight check made by some browsers will succeed and allow the advanced methods to be serviced. The
ExtensionResponse
helper class has a static method called availableOptions
to help construct the required response.
By default, Rexster will auto-commit a transaction with success if the extension method returns a non-error ExtensionResponse
. If the ExtensionResponse
is an error or the extension throws an untrapped exception it will auto-commit with failure.
To turn off this behavior, set the autoCommitTransaction
value on the ExtensionDefinition
to false
. When set to false, the extension method itself is responsible for committing transaction, though untrapped exceptions will still commit with failure.
There are multiple types of extensions. The type of extension is driven by a combination of the settings in the ExtensionDefinition
.
type | defined by |
---|---|
Root | An ExtensionDefinition defined with an ExtensionPoint of GRAPH , VERTEX or EDGE with no path. |
Path | An ExtensionDefinition defined with an ExtensionPoint of GRAPH , VERTEX or EDGE and a specified path. |
Consider the following code snippet from the SimpleRootExtension
extension class in the Sample Kibbles project:
ExtensionNaming(name = SimpleRootExtension.EXTENSION_NAME, namespace = SimpleRootExtension.EXTENSION_NAMESPACE)
public class SimpleRootExtension extends AbstractRexsterExtension {
public static final String EXTENSION_NAME = "simple-root";
public static final String EXTENSION_NAMESPACE = "tp";
@ExtensionDefinition(extensionPoint = ExtensionPoint.GRAPH)
@ExtensionDescriptor(description = "Do something on graph")
public ExtensionResponse doWorkOnGraph(@RexsterContext Graph graph) {
return toStringIt(graph);
}
}
will serve at:
http://{base}/graphs/{graph}/tp/simple-root
When defining an ExtensionDefinition
in this way, it is referred to as a “root extension” where the extension serves from the root of the ExtensionPoint
. A single root extension can be defined for a single extension point within a method, namespace and extension class. Therefore, the following extension would not be valid:
ExtensionNaming(name = SimpleRootExtension.EXTENSION_NAME, namespace = SimpleRootExtension.EXTENSION_NAMESPACE)
public class SimpleRootExtension extends AbstractRexsterExtension {
public static final String EXTENSION_NAME = "simple-root";
public static final String EXTENSION_NAMESPACE = "tp";
@ExtensionDefinition(extensionPoint = ExtensionPoint.GRAPH)
@ExtensionDescriptor(description = "Do something on the graph")
public ExtensionResponse doWorkOnGraph(@RexsterContext Graph graph) {
return toStringIt(graph);
}
@ExtensionDefinition(extensionPoint = ExtensionPoint.GRAPH)
@ExtensionDescriptor(description = "Do some more stuff on the graph")
public ExtensionResponse doWorkSomeNonsenseWorkOnGraph(@RexsterContext Graph graph) {
// DON'T INCLUDE TWO METHODS THAT EXTEND FROM THE SAME EXTENSIONPOINT
// UNLESS SPECIFYING A PATH EXTENSION (described below)
return toStringIt(graph.getVertex(1));
}
}
While Rexster does not complain if it comes across the above scenario, it may not behave as expected. Rexster will simply choose the first and best method match to the request.
In addition to root extensions, there are also path extensions. Path extensions allow an extension to have multiple service methods exposed from the same namespace, extension name and extension point. Consider the following code snippet from the SimplePathExtension
extension class in Sample Kibbles project:
@ExtensionNaming(name = SimplePathExtension.EXTENSION_NAME, namespace = SimplePathExtension.EXTENSION_NAMESPACE)
public class SimplePathExtension extends AbstractRexsterExtension {
public static final String EXTENSION_NAME = "simple-path";
public static final String EXTENSION_NAMESPACE = "tp";
@ExtensionDefinition(extensionPoint = ExtensionPoint.GRAPH, path = "some-work")
@ExtensionDescriptor(description = "Do something on the graph")
public ExtensionResponse doSomeWorkOnGraph(@RexsterContext Graph graph) {
return toStringIt(graph, "some");
}
@ExtensionDefinition(extensionPoint = ExtensionPoint.GRAPH, path = "other-work")
@ExtensionDescriptor(description = "Do more stuff on the graph")
public ExtensionResponse doOtherWorkOnGraph(@RexsterContext Graph graph){
return toStringIt(graph, "other");
}
}
Note that there are two ExtensionDefinition
annotations on two different methods that both specify the ExtensionPoint.GRAPH
. It was previously established in the root extension section that this specification is not permitted, however, the difference here is the usage of the path
parameter on the annotation. The value of the path
parameter helps to identify the method to be exposed on the extension, such that Rexster will serve those methods on the following URIs:
http://{base}/graphs/{graph}/tp/simple-path/some-work
http://{base}/graphs/{graph}/tp/simple-path/other-work
Like the root extension, the path extension will behave inconsistenly if there is more than one method on the extension that has the same namespace, extension name, extension point, method and path. Rexster will always serve the first method to match the request.