March 11, 2015

JAX-RS Client API Groovy DSL, prototype

Groovy language is DSL friendly language.  See http://groovy-lang.org/dsls.html or http://groovy.codehaus.org/Writing+Domain-Specific+Languages.

Let me share with you my JAX-RS Client API Groovy DSL prototype, code is available in GitHub https://github.com/shamoh/jersey-groovy repo. It just supports GET and POST methods. This is really just concept prototype. But it shows power of Groovy and way how simplified JAX-RS Client API could look like.

The GitHub repository contains couple of unit tests to show the DSL. And also uses Spock Framework what is another Groovy DSL example, in this case as testing framework/API.

Examples

GET plain text, no parameters

JAX-RS Client API:
WebTarget target = ClientBuilder.newClient().target(RESOURCE_URL)
def response = target.request(TEXT_PLAIN_TYPE).get(String)
response == "Hello World!"
Jersey Groovy DSL:
def response = GET[RESOURCE_URL].text
response == "Hello World!"


GET plain text, path parameter

JAX-RS Client API:
WebTarget target = ClientBuilder.newClient().target(RESOURCE_URL)
def response = target.path("Jersey").request(TEXT_PLAIN_TYPE).get(String)
response == "Hello Jersey!"
Jersey Groovy DSL:
def response = GET[RESOURCE_URL/"Jersey"].text
response == "Hello Jersey!"


GET XML, path parameter

JAX-RS Client API:
WebTarget target = ClientBuilder.newClient().target(RESOURCE_URL)
def response = target.path("Jersey").request(APPLICATION_XML_TYPE).get(Message)
response instanceof Message
response.greeting == "Hello Jersey!"
Jersey Groovy DSL:
def response = (GET[RESOURCE_URL/"Jersey"] & APPLICATION_XML_TYPE) as Message
response instanceof Message
response.greeting == "Hello Jersey!"


POST plain text, response text

JAX-RS Client API:
WebTarget target = ClientBuilder.newClient().target(RESOURCE_URL)
def Entity entity = Entity.entity("Jersey", TEXT_PLAIN_TYPE);
def response = target.request().post(entity, String)
response == "Hello Jersey!"
Jersey Groovy DSL:
def response = POST[RESOURCE_URL] << "Jersey" as String
response == "Hello Jersey!"

POST object as JSON, response text

JAX-RS Client API:
WebTarget target = ClientBuilder.newClient().target(RESOURCE_URL)
def Entity entity = Entity.entity(new Message("Jersey", new Date()), APPLICATION_JSON_TYPE)
def response = target.request().post(entity, String)
response == "Hello Jersey!"
Jersey Groovy DSL:
def response = (POST[RESOURCE_URL] | APPLICATION_JSON_TYPE) \
                   << new Message("Jersey", new Date()) as String
response == "Hello Jersey!"

(And finally there is simple slide deck to introduce the DSL API, same examples.)

Enjoy.

JAX-RS Message Body Writer & Reader using Kryo serialization framework


In this post I'm gonna show you how simple it is to enhance JAX-RS application by a new body format. In this example I use Kryo serialization framework.

Kryo serialization framework


Kryo is a fast and efficient object graph serialization framework for Java. The goals of the project are speed, efficiency, and an easy to use API. The project is useful any time objects need to be persisted, whether to a file, database, or over the network.
Kryo can also perform automatic deep and shallow copying/cloning. This is direct copying from object to object, not object->bytes->object.

JAX-RS Message Body providers

JAX-RS allows you to enhance JAX-RS engine to support another body type. There is MessageBodyReader and MessageBodyWriter SPI to be implemented. I have chosen to use application/x-kryo media type for Kryo (de)serialized message bodies.


package cz.kramolis.jersey.kryo;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.MessageBodyWriter;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;

@Consumes("application/x-kryo")
@Produces("application/x-kryo")
public class KryoMessageBodyProvider implements
                        MessageBodyWriter<Object>,
                        MessageBodyReader<Object> {

    //
    // MessageBodyWriter
    //

    @Override
    public long getSize(Object object, Class<?> type, Type genericType,
                        Annotation[] annotations, MediaType mediaType) {
        return -1;
    }

    @Override
    public boolean isWriteable(Class<?> type, Type genericType,
                        Annotation[] annotations, MediaType mediaType) {
        return true;
    }

    @Override
    public void writeTo(Object object, Class<?> type, Type genericType,
                        Annotation[] annotations, MediaType mediaType,
                        MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream)
            throws IOException, WebApplicationException {
        final ByteArrayOutputStream baos = new ByteArrayOutputStream();

        final Kryo kryo = new Kryo();
        final Output output = new Output(baos);

        kryo.writeObject(output, object);
        output.close();

        entityStream.write(baos.toByteArray());
    }

    //
    // MessageBodyReader
    //

    @Override
    public boolean isReadable(Class<?> type, Type genericType,
                        Annotation[] annotations, MediaType mediaType) {
        return true;
    }

    @Override
    public Object readFrom(Class<Object> type, Type genericType,
                           Annotation[] annotations, MediaType mediaType,
                           MultivaluedMap<String, String> httpHeaders,
                           InputStream entityStream) throws IOException, WebApplicationException {
        try {
            Kryo kryo = new Kryo();
            Input input = new Input(entityStream);
            Object object = kryo.readObject(input, type);
            input.close();

            return object;
        } catch (Exception e) {
            throw new WebApplicationException(e);
        }
    }

}

You can see it is really simple Message Body provider. But I hope it shows you the way...

Than you need to register provider class into JAX-RS Application, e.g.


package cz.kramolis.jersey.kryo;

import java.util.HashSet;
import java.util.Set;

import javax.ws.rs.core.Application;

public class JaxRsApplication extends Application {

    static final Set<Class<?>> APP_CLASSES = new HashSet<Class<?>>() {{
        add(KryoMessageBodyProvider.class);
        add(PersonResource.class);
    }};

    @Override
    public Set<Class<?>> getClasses() {
        return APP_CLASSES;
    }

}


And finally usage of the new body type is very easy:


package cz.kramolis.jersey.kryo;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

@Path("/")
@Consumes("application/x-kryo")
@Produces("application/x-kryo")
public class PersonResource {

    @POST
    public Person echo(final Person person) {
        return person;
    }

    @PUT
    public void put(final Person person) {
    }

    @GET
    public Person get() {
        return new Person("Wolfgang", 21, "Salzburg");
    }

}


Conclusion

Whole sources are available in dedicated GitHub repository. Use it as you need. JAX-RS provides really simple way how to extend it's support. And Kryo is really powerful framework. Give it a try. Look at deeper JVM serializers comparision.

In one of the next blog posts I will provider some performance data comparing Kryo with JSON, XML and others message body providers.

Links