Hot questions for Using Neo4j in jax rs

Question:

I'm building a Neo4j (2.2.2) unmanaged extension and I can't figure out why the following method is throwing 415: unsupported media type when I post requests with properly-formatted JSON bodies and Content-Type:application/json headers.

@POST
@Path("/create")
@Consumes(MediaType.APPLICATION_JSON)
public Response createFoo(FooBar input) {
      //not relevant        
}

This is the FooBar class I want the JSON to be mapped to:

@XmlRootElement
public class FooBar implements Serializable {
    String title;    
    public FooBar(){}
}

This is the message in my log:

`SEVERE: A message body reader for Java class org.mycompany.myproject.FooBar, and Java type class     
org.mycompany.myproject.FooBar, and MIME media type application/json was not found.
The registered message body readers compatible with the MIME media type are:
** ->
com.sun.jersey.core.impl.provider.entity.FormProvider
com.sun.jersey.core.impl.provider.entity.StringProvider
com.sun.jersey.core.impl.provider.entity.ByteArrayProvider
com.sun.jersey.core.impl.provider.entity.FileProvider
com.sun.jersey.core.impl.provider.entity.InputStreamProvider
com.sun.jersey.core.impl.provider.entity.DataSourceProvider
com.sun.jersey.core.impl.provider.entity.XMLJAXBElementProvider$General
com.sun.jersey.core.impl.provider.entity.ReaderProvider
com.sun.jersey.core.impl.provider.entity.DocumentProvider      
com.sun.jersey.core.impl.provider.entity.SourceProvider$StreamSourceReader
....

After looking at other SO answers to similar questions, I have included the appropriate version of jersey-json and confirmed that it is being included in my jar.

This is my pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>org.mycompany</groupId>
<artifactId>myproject</artifactId>
<version>1.0</version>

<dependencies>
    <dependency>
        <groupId>javax.ws.rs</groupId>
        <artifactId>javax.ws.rs-api</artifactId>
        <version>2.0</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.neo4j</groupId>
        <artifactId>neo4j</artifactId>
        <version>2.2.2</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.codehaus.jackson</groupId>
        <artifactId>jackson-mapper-asl</artifactId>
        <version>1.9.13</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.neo4j.test</groupId>
        <artifactId>neo4j-harness</artifactId>
        <version>2.2.2</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.neo4j</groupId>
        <artifactId>neo4j-kernel</artifactId>
        <version>2.2.2</version>
        <type>test-jar</type>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.neo4j</groupId>
        <artifactId>neo4j-io</artifactId>
        <version>2.2.2</version>
        <type>test-jar</type>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.neo4j.app</groupId>
        <artifactId>neo4j-server</artifactId>
        <version>2.2.2</version>
    </dependency>
    <dependency>
        <groupId>org.neo4j.app</groupId>
        <artifactId>neo4j-server</artifactId>
        <version>2.2.2</version>
        <type>test-jar</type>
    </dependency>
    <dependency>
        <groupId>org.neo4j</groupId>
        <artifactId>server-api</artifactId>
        <version>2.2.2</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.hamcrest</groupId>
        <artifactId>hamcrest-all</artifactId>
        <version>1.3</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.media</groupId>
        <artifactId>jersey-media-json-jackson</artifactId>
        <version>2.13</version>
    </dependency>
    <dependency>
        <groupId>com.github.javafaker</groupId>
        <artifactId>javafaker</artifactId>
        <version>0.5</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.media</groupId>
        <artifactId>jersey-media-json-jackson</artifactId>
        <version>2.13</version>
    </dependency>
    <dependency>
        <groupId>com.sun.jersey</groupId>
        <artifactId>jersey-json</artifactId>
        <version>1.18.1</version>
    </dependency>
</dependencies>
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>2.4</version>
            <configuration>
                <source>1.7</source>
                <target>1.7</target>
            </configuration>
        </plugin>
        <plugin>
            <artifactId>maven-assembly-plugin</artifactId>
            <version>2.4</version>
            <configuration>
                <archive>
                    <manifest>
                        <mainClass>org.mycompany.myproject.mymainclassg</mainClass>
                    </manifest>
                </archive>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
            </configuration>
        </plugin>


    </plugins>
</build>

Note: Neo4j 2.2.2 runs Jersey 1.18.1

Update: At @StefanArmbruster's suggestion, I attempted adding jersey-media-json-jackson to the build, both with and without the jersey-json dependency, and am still having the same problem


Answer:

Instead of messing with Jersey's bundling, it is often simpler to simply directly include Jackson JAX-RS provider:

https://github.com/FasterXML/jackson-jaxrs-providers

(for JSON, but also for XML, Smile or CBOR)

Simply including the provider jar via Maven dependency should be enough (jar has META-INF/services auto-registration metadata); it does depend on Jackson 2.x (jackson-databind), which you may want to add an explicit dependency as well.

Question:

Hi,

I am trying to make a gzipped response in a Neo4j unmanaged extension. I found this example: http://www.codingpedia.org/ama/how-to-compress-responses-in-java-rest-api-with-gzip-and-jersey/

I tried to add this WriterInterceptor:

@Provider
@Compress
public class GZIPWriterInterceptor implements WriterInterceptor {

    @Override
    public void aroundWriteTo(WriterInterceptorContext context)
            throws IOException, WebApplicationException {

        MultivaluedMap<String,Object> headers = context.getHeaders();
        headers.add("Content-Encoding", "gzip");

        final OutputStream outputStream = context.getOutputStream();
        context.setOutputStream(new GZIPOutputStream(outputStream));
        context.proceed();
    }
}

And this annotation:

@NameBinding
@Retention(RetentionPolicy.RUNTIME)
public @interface Compress {
}

And in my resource I use it like this:

@GET
@Path("/users")
@Produces(MediaType.APPLICATION_JSON)
@Compress
public List<User> getUsers() {
    return factory.getUserService(graphDb).getUsers();
}

However the WriterInterceptor is never called, why? How can I make my response gzipped?

I want to solve this inside my unmanaged plugin and solutions where the response is gzipped in a proxy outside of Neo4j is not an alternative.


Answer:

You need to register a servlet filter that cares about gzipping. This can be done in a SPIPluginLifecycle, see this blog post: http://www.markhneedham.com/blog/2013/07/08/neo4j-unmanaged-extension-creating-gzipped-streamed-responses-with-jetty/

Question:

I am following Neo4j doc here: http://neo4j.com/docs/milestone/server-unmanaged-extensions.html to Unmanaged Extensions. I am not able to compile HelloWorld example given there. I am facing problem in creating jar file. In the doc, There is a tip for compiling: "You will need to include a dependency to JAX-RS API on your classpath when you compile", I am not sure how to include this dependency with jar command. I am not using Maven.


Answer:

You will have to download the jax-rs jars (and dependencies) from java.net: https://jersey.java.net/download.html

I strongly recommend to use a build tool like maven, gradle or ivy to manage your dependencies though. Then you just have to add the java.net maven repository as source and it will be pulled automatically.

Question:

I need to retrieve an attribute from a Response object which is returned from a post() invocation: in particular, I'm using Neo4J and after posting a node I would like to retrieve its Id, which is an attribute in the returned XML code. My current post looks like this:

Response res = target.path("resource/node").request(MediaType.APPLICATION_XML)
              .post(Entity.entity(node, MediaType.APPLICATION_XML));

Then I perform a check on the returned HTTP status and I would also need the node Id, which is returned in:

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<node xmlns="http://www.namespace.org/Neo4J" id="140">
 ... node properties ...
</node>

I tried to cast res.getEntity() to a Document but it causes the following:

java.lang.ClassCastException: org.glassfish.jersey.client.internal.HttpUrlConnector$2 cannot be cast to org.w3c.dom.Document

Thanks in advance.


Answer:

You can use JAXB to map POST body XML data to java object:

Payload entity = res.getEntity(Payload.class);
String id = payload.id;

where Payload you can define to reflect your XML structure:

import javax.xml.bind.annotation.*;


@XmlRootElement(name="node")
@XmlAccessorType(XmlAccessType.FIELD)
public class Payload {
    @XmlAttribute(name = "id")
    String id; // for example
}