Showing posts with label json. Show all posts
Showing posts with label json. Show all posts

Friday, February 7, 2014

Transparent PATCH support in JAX-RS 2.0

The PATCH method is one the the less well loved HTTP methods simple because until recently there really wasn't a standard PATCH format. This has been standardized for JSON for a while now so there are quite a few libraries that will do the heavy lifting for you. For the purposes of this blog I am going to use json-patch although it would be easy to adapt this particular implementation to the patch library of your choice.

A per normal lets get the resource and bean classes out of the way. In this example code we have a simple resource that knows how to return the original object and one that allows you to perform the PATCH method. Note that the patch method just accepts the bean object, this is because of some magic we are going to do in a little bit to pre-process the patch.

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("service")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class Service {

  @GET
  public Bean get() {
    return new Bean(true);
  }

  @PATCH
  @Consumes("application/json-patch+json")
  public Bean patch(Bean input) {
    System.out.println(input.getMessage() + "  " + input.getTitle());
    return input;
  }

}


import java.util.ArrayList;
import java.util.List;

public class Bean {

  private String title = "title";
  private String message = "message";
  private List<String> list = new ArrayList<String>();

  public Bean() {
    this(false);
  }

  public Bean(boolean init) {
    if (init) {
      title = "title";
      message = "message";
      list.add("one");
      list.add("two");
    }
  }


  public void setList(List list) {
    this.list = list;
  }

  public List getList() {
    return list;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  public String getTitle() {
    return title;
  }

  public void setMessage(String message) {
    this.message = message;
  }

  public String getMessage() {
    return message;
  }

}

So the @PATCH annotation is something we have to create for this example, luckily JAX-RS contains a extension meta-annotation for this purpose. We are also going to use @NameBinding as this example is using JAX-RS 2.0 so we can connect up our filter in a moment.

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.ws.rs.HttpMethod;
import javax.ws.rs.NameBinding;


@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@HttpMethod("PATCH")
@Documented
@NameBinding
public @interface PATCH {
}

So here is the implementation of the ReaderInterceptor that will process the incoming stream and replace it with the patched version. Note that the class is annotated with @PATCH also in order to make the @NamedBinding magic work and also that there is a lot of error handling that is missing as this is a simple POC.

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import com.github.fge.jsonpatch.JsonPatch;
import com.github.fge.jsonpatch.JsonPatchException;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

import javax.ws.rs.GET;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.ReaderInterceptor;
import javax.ws.rs.ext.ReaderInterceptorContext;

import org.glassfish.jersey.message.MessageBodyWorkers;

@Provider
@PATCH
public class PatchReader implements ReaderInterceptor {
  private UriInfo info;
  private MessageBodyWorkers workers;

  @Context
  public void setInfo(UriInfo info) {
    this.info = info;
  }

  @Context
  public void setWorkers(MessageBodyWorkers workers) {
    this.workers = workers;
  }

  @Override
  public Object aroundReadFrom(
    ReaderInterceptorContext readerInterceptorContext) 
    throws IOException,
           WebApplicationException {

    // Get the resource we are being called on, 
    // and find the GET method
    Object resource = info.getMatchedResources().get(0);

    Method found = null;
    for (Method next : resource.getClass().getMethods()) {
      if (next.getAnnotation(GET.class) != null) {
        found = next;
        break;
      }
    }


    if (found != null) {

      // Invoke the get method to get the state we are trying to patch
      //
      Object bean;
      try {
        bean = found.invoke(resource);
      } catch (Exception e) {
        throw new WebApplicationException(e);
      }
      
      // Convert this object to a an aray of bytes 
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      MessageBodyWriter<? super Object> bodyWriter =
        workers.getMessageBodyWriter(Object.class, bean.getClass(), 
          new Annotation[0], MediaType.APPLICATION_JSON_TYPE);

      bodyWriter.writeTo(bean, bean.getClass(), bean.getClass(), 
          new Annotation[0], MediaType.APPLICATION_JSON_TYPE,
          new MultivaluedHashMap<String, Object>(), baos);


      // Use the Jackson 2.x classes to convert both the incoming patch  
      // and the current state of the object into a JsonNode / JsonPatch
      ObjectMapper mapper = new ObjectMapper();
      JsonNode serverState = mapper.readValue(baos.toByteArray(), 
        JsonNode.class);
      JsonNode patchAsNode = mapper.readValue(
         readerInterceptorContext.getInputStream(), 
        JsonNode.class);
      JsonPatch patch = JsonPatch.fromJson(patchAsNode);

      try {
        // Apply the patch
        JsonNode result = patch.apply(serverState);

        // Stream the result & modify the stream on the readerInterceptor
        ByteArrayOutputStream resultAsByteArray = 
          new ByteArrayOutputStream();
        mapper.writeValue(resultAsByteArray, result);
        readerInterceptorContext.setInputStream(
          new ByteArrayInputStream(
            resultAsByteArray.toByteArray()));

        // Pass control back to the Jersey code
        return readerInterceptorContext.proceed();


      } catch (JsonPatchException e) {
        throw new WebApplicationException(
          Response.status(500).type("text/plain").entity(e.getMessage()).build());
      }

    } else {
      throw new IllegalArgumentException("No matching GET method on resource");
    }


  }
}

So once you have this deployed you can start playing with the data, so the original message is:

{
  "list" : [
    "one",
    "two"
  ],
  "message" : "message",
  "title" : "title"
}

So if you apply the following patch, the result returned is:

[
  {
    "op" : "replace",
    "path" : "/message",
    "value" : "otherMessage"
  },
  {
    "op" : "add",
    "path" : "/list/-",
    "value" : "three"
  }
]


{
  "list" : [
    "one",
    "two",
    "three"
  ],
  "message" : "otherMessage",
  "title" : "title"
}

This example shows it is relatively trivial to add PATCH support to your classes by following a simple coding pattern and using a simple Annotation. In this way PATCH support becomes trivial as the implementation can just delegate to your existing PUT method.

Update: Mirsolav Fuksa from the Jersey team reminded me that in order for this implementation to comply with the PATCH RFC it should provide the Accept-Patch header when the client performs an OPTIONS request. You can do this with a simple CotnainerResponseFilter:

import java.io.IOException;

import java.util.Collections;

import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.ext.Provider;

@Provider
public class OptionsAcceptHeader implements ContainerResponseFilter {

  @Override
  public void filter(ContainerRequestContext requestContext,
                     ContainerResponseContext responseContext) throws IOException {

    if ("OPTIONS".equals(requestContext.getMethod())) {
      if (responseContext.getHeaderString("Accept-Patch")==null) {
        responseContext.getHeaders().put(
          "Accept-Patch", Collections.<Object>singletonList("application/json-patch+json"));  
      }
    }
  }
}

Wednesday, February 5, 2014

Using JSON-P, aka JSR 353 with Jersey 1.x, or indeed any JAX-RS 1.x

I have been playing with JSON-P a little bit recently and one of the thing I wanted to try as making it work with Jersey 1.x. (Jersey 2.x has this functionality built in).

I had hoped to use the org.glassfish:jsonp-jaxp component that comes from the json-p project; but it depends on the JAX-RS 2.0 API. With a bit of friendly badgering of the ever helpful Pavel Bucek, a version was published that would work with JAX-RS 1.x implementations.

If you are a maven user this would be as simple as adding the following dependencies:

<dependency>
      <groupId>org.glassfish</groupId>
      <artifactId>javax.json</artifactId>
      <version>1.0.4</version>
    </dependency>
    <dependency>
      <groupId>org.glassfish</groupId>
      <artifactId>jsonp-jaxrs-1x</artifactId>
      <version>1.0</version>
    </dependency>

Otherwise you can easily just download the two files from here and here and add them to your project.

Then you can consume and produce JsonStructure (JsonObject and JsonArray) elements to your hearts content, using the helper methods from the previously mentioned post:

@Path("/hello")
public class Hello {

  @GET
  @Produces(MediaType.APPLICATION_JSON)
  public JsonStructure hello() {

    return JsonObjectBuilder builder =
      ob()
       .add("hello", "world")
       .add("fark", ".com")
       .add("fish", ob()
         .add("child", "child"))
       .add("array", ab()
         .add("one")
         .add("two")
         .add(ob()
           .add("boolean",
           true))).build();
  }


}



Thursday, January 30, 2014

Making your JSON-P (JSR-353) code slightly prettier

The JSON-P API as described in JSR-353 is a limited API for working with JSON; but at the basic level it will do the job. It is possible to add a few utility methods that can make your code, in my eyes, prettier.

The first think that annoyed me was the use of Json.createObjectBuilder() and Json.createArrayBuilder() when trying to construct a JSONObject.

So lets create a nice helper class with some very short method names for both:

public static JsonObjectBuilder ob() {
  return Json.createObjectBuilder();
}

public static JsonArrayBuilder ab() {
  return Json.createArrayBuilder();
}

This makes the creation of object just that little bit less wordy:

JsonObject object = ob()
  .add("hello", "world")
  .add("fark", ".com")
  .add("fish", ob()
    .add("child", "child"))
  .add("array", ab()
    .add("one")
    .add("two")
    .add(ob()
      .add("boolean",true)))
  .build();

The second problem is accessing element in a JsonObject can be wordy; but it is relatively easy to knock up a method that would allow some simply XPath like accessor:

public static <T extends JsonValue> T get(JsonStructure structure, 
  String path, Class<T> type) {

  String segments[] = path.split("/");
  JsonValue currentValue = structure;
  for (String segment : segments) {

    if (segment.length() == 0) {
      continue;
    }

    if (currentValue instanceof JsonObject) {
      JsonObject currentObject = (JsonObject) currentValue;
      currentValue = currentObject.get(segment);
    } else if (currentValue instanceof JsonArray) {
      if (segment.startsWith("[") && segment.endsWith("]")) {
        int index = Integer.parseInt(segment.substring(1, segment.length() - 1));
        currentValue = ((JsonArray) currentValue).get(index);
      } else {
        throw new IllegalArgumentException("Array type requires key of the form [n]");
      }
    } else {
      throw new IllegalStateException("Value types are not decomposible" 
        + currentValue.getValueType());
    }

  }

  return type.cast(currentValue);

}


// Example to get hold of a string value

System.out.println(get(object, "/fish/child", JsonString.class));


Of course even this is a little bit untidy as you have to care about the internal JsonValue types which is a pain; and deal with possibly null pointers without the aid of Optional. It doesn't take much to wrap these up though.

public static String getString(JsonStructure structure, String path) {
  JsonString value = get(structure, path, JsonString.class);
  return value != null ? value.getString() : null;
}

public static boolean is(JsonStructure structure, String path) {
  JsonValue value = get(structure, path, JsonValue.class);
  return value != null ? value == JsonValue.TRUE : false;
}

public static boolean isNull(JsonStructure structure, String path) {
  JsonValue value = get(structure, path, JsonValue.class);
  return value != null ? value == JsonValue.NULL : false;
}

public static BigInteger getBigDecimal(JsonStructure structure, String path) {
  JsonNumber value = get(structure, path, JsonNumber.class);
  return value != null ? value.bigIntegerValue() : null;
}

public static int getInt(JsonStructure structure, String path) {
  JsonNumber value = get(structure, path, JsonNumber.class);
  return value != null ? value.intValue() : null;
}

public static JsonArray getArray(JsonStructure structure, String path) {
  return get(structure, path, JsonArray.class);
}

public static JsonObject getObject(JsonStructure structure, String path) {
  return get(structure, path, JsonObject.class);
}

This means you can write more direct code such as:

if (is(object, "/array/[2]/boolean")) {
  System.out.println(getString(object, "/fish/child"));
}

Sometimes it only take a few statically imported methods to make a API more useful / easier to read.

Wednesday, January 29, 2014

Selecting level of detail returned by varying the content type, part II

In my previous entry, we looked at using the feature of MOXy to control the level of data output for a particular entity. This post looks at an abstraction provided by Jersey 2.x that allows you to define a custom set of annotations to have the same effect.

As before we have an almost trivial resource that returns an object that Jersey will covert to JSON for us, note that for the moment there is nothing in this code to do the filtering - I am not going to pass in annotations to the Response object as in the Jersey examples:

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

@Path("hello")
public class SelectableHello {


  @GET
  @Produces({ "application/json; level=detailed", "application/json; level=summary", "application/json; level=normal" })
  public Message hello() {

    return new Message();

  }
}

In my design I am going to define four annotations: NoView, SummaryView, NormalView and DetailedView. All root objects have to implement the NoView annotation to prevent un-annotated fields from being exposed - you might not feel this is necessary in your design. All of these classes look the same so I am going to only show one. Note that the factory method creating a AnnotationLiteral has to be used in preference to a factory that would create a dynamic proxy to have the same effect. There is code in 2.5 that will ignore any annotation implemented by a java.lang.reflect.Proxy object, this includes any annotations you may have retrieved from a class. I am working on submitting a fix for this.

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.enterprise.util.AnnotationLiteral;

import org.glassfish.jersey.message.filtering.EntityFiltering;

@Target({ ElementType.TYPE, 
  ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@EntityFiltering
public @interface NoView {

  /**
   * Factory class for creating instances of the annotation.
   */
  public static class Factory extends AnnotationLiteral<NoView> 
    implements NoView {

    private Factory() {
    }

    public static NoView get() {
      return new Factory();
    }
  }

}

Now we can take a quick look at our Message bean, this is slightly more complicated than my previous example to showing filtering of subgraphs in a very simple form. As I said before the class is annotated with a NoView annotation at the root - this should mean that the privateData is never returned to the client as it is not specifically annotated.

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
@NoView
public class Message {


  private String privateData;
  @SummaryView
  private String summary;
  @NormalView
  private String message;
  @DetailedView
  private String subtext;
  @DetailedView
  private SubMessage submessage;

  public Message() {
    summary = "Some simple summary";
    message = "This is indeed the message";
    subtext = "This is the deep and meaningful subtext";
    submessage = new SubMessage();
    privateData = "The fox is flying tonight";
  }


  // Getters and setters not shown
}


public class SubMessage {

  private String message;

  public SubMessage() {
    message = "Some sub messages";
  }

  // Getters and setters not shown
}


As noted before there is no code in the resource class to deal with filtering - I considered this to be a cross cutting concern so I have abstracted this into a WriterInterceptor. Note the exception thrown if a entity is used that doesn't have the NoView annotation on it.

import java.io.IOException;

import java.lang.annotation.Annotation;

import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;

import javax.ws.rs.ServerErrorException;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.WriterInterceptor;
import javax.ws.rs.ext.WriterInterceptorContext;

@Provider
public class ViewWriteInterceptor implements WriterInterceptor {


  private HttpHeaders httpHeaders;

  public ViewWriteInterceptor(@Context HttpHeaders httpHeaders) {
    this.httpHeaders = httpHeaders;
  }


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

    // I assume this case will never happen, just to be sure
    if (writerInterceptorContext.getEntity() == null) {

      writerInterceptorContext.proceed();
      return;
    }
    else
    {
      Class<?> entityType = writerInterceptorContext.getEntity()
          .getClass();
      String entityTypeString = entityType.getName();
      
      // Ignore any Jersey system classes, for example wadl
      //
      if (entityType == String.class  || entityType.isArray() 
        || entityTypeString.startsWith("com.sun") 
        || entityTypeString.startsWith("org.glassfish")) {
        writerInterceptorContext.proceed();
        return;
      }
      
      // Fail if the class doesn't have the default NoView annotation 
      // this prevents any unannotated fields from showing up
      //
      else if (!entityType.isAnnotationPresent(NoView.class)) {
        throw new ServerErrorException("Entity type should be tagged with @NoView annotation " + entityType, Response.Status.INTERNAL_SERVER_ERROR);
        
      }
    }

    // Get hold of the return media type:
    //

    MediaType mt = writerInterceptorContext.getMediaType();
    String level = mt.getParameters().get("level");

    // Get the annotations and modify as required
    //

    Set<Annotation> current = new LinkedHashSet<>();
    current.addAll(Arrays.asList(
      writerInterceptorContext.getAnnotations()));

    switch (level != null ? level : "") {
      default:
      case "detailed":
        current.add(com.example.annotation.DetailedView.Factory.get());
      case "normal":
        current.add(com.example.annotation.NormalView.Factory.get());
      case "summary":
        current.add(com.example.annotation.SummaryView.Factory.get());

    }

    writerInterceptorContext.setAnnotations(
      current.toArray(new Annotation[current.size()]));

    //

    writerInterceptorContext.proceed();
  }
}

Finally you have to enable the EntityFilterFeature manually, to do this you can simple register it in your Application class

import java.lang.annotation.Annotation;

import javax.ws.rs.ApplicationPath;

import org.glassfish.jersey.message.filtering.EntityFilteringFeature;
import org.glassfish.jersey.server.ResourceConfig;

@ApplicationPath("/resources/")
public class SelectableApplication extends ResourceConfig {
  public SelectableApplication() {

    packages("...");

    // Set entity-filtering scope via configuration.
    property(EntityFilteringFeature.ENTITY_FILTERING_SCOPE, 
    new Annotation[] {
      NormalView.Factory.get(), DetailedView.Factory.get(), 
      NoView.Factory.get(), SummaryView.Factory.get()
    });
    register(EntityFilteringFeature.class);
  }


}

Once you have this all up and running the application will respond as before:

GET .../hello Accept application/json; level=detailed or application/json
{
  "message" : "This is indeed the message",
  "submessage" : {
    "message" : "Some sub messages"
  },
  "subtext" : "This is the deep and meaningful subtext",
  "summary" : "Some simple summary"
}

GET .../hello Accept application/json; level=normal
{
  "message" : "This is indeed the message",
  "summary" : "Some simple summary"
}


GET .../hello Accept application/json; level=summary
{
  "summary" : "Some simple summary"
}

This is feel is a better alternative to using the MOXy annotations directly - using custom annotations should have to much easier to port your application to over implementation even if you have to provide you own filter. Finally it is worth also exploring the Jersey extension to this that allows Role based filtering which I can see as being useful in a security aspect.

Tuesday, January 28, 2014

Selecting level of detail returned by varying the content type

So I have been playing with the various ways of generating JSON in Jersey this week and I have been looking at solutions to the problem of returning different levels of detail depending on the client requirements. Here is one solution that uses the MoXY JAX-B provider in Jersey 2.x.

Consider this very simple hello world class:

@Path("hello")
public class SelectableHello {
    
    @GET
    @Produces({"application/json; level=detailed", "application/json; level=summary", "application/json; level=normal"})
    public Message hello() {
        return new Message();
    }
}

The Message class is annotated with some special annotations from MOXy that allows you to specify different partial object graphs. So my very simple message class looks like the following with three different levels defined:

import javax.xml.bind.annotation.XmlRootElement;

import org.eclipse.persistence.oxm.annotations.XmlNamedAttributeNode;
import org.eclipse.persistence.oxm.annotations.XmlNamedObjectGraph;
import org.eclipse.persistence.oxm.annotations.XmlNamedObjectGraphs;


@XmlNamedObjectGraphs({
  @XmlNamedObjectGraph(name = "summary", 
    attributeNodes = { 
      @XmlNamedAttributeNode("summary") }),
  @XmlNamedObjectGraph(name = "normal",
    attributeNodes =
    { @XmlNamedAttributeNode("summary"), 
      @XmlNamedAttributeNode("message") }),
  @XmlNamedObjectGraph(name = "detailed",
    attributeNodes =
    { @XmlNamedAttributeNode("summary"), 
      @XmlNamedAttributeNode("message"),
      @XmlNamedAttributeNode("subtext") })
  })
@XmlRootElement
public class Message {

  private String summary, message, subtext;

  public Message() {
    summary = "Some simple summary";
    message = "This is indeed the message";
    subtext = "This is the deep and meaningful subtext";
  }

  public void setSummary(String summary) {
    this.summary = summary;
  }

  public String getSummary() {
    return summary;
  }

  public void setMessage(String message) {
    this.message = message;
  }

  public String getMessage() {
    return message;
  }

  public void setSubtext(String subtext) {
    this.subtext = subtext;
  }

  public String getSubtext() {
    return subtext;
  }


}

Then it is a relatively easy step to define a JAX-RS ContextResolver that returns a suitably configured MoxyJsonConfig depending on the mime type. The code in here is a little bit ropey as it assumes that the first item in the accept list is the correct one.

import javax.ws.rs.core.Context;

import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;

import org.eclipse.persistence.jaxb.MarshallerProperties;

import org.glassfish.jersey.moxy.json.MoxyJsonConfig;

@Provider
public class MoxyJsonConfigResolver implements ContextResolver<MoxyJsonConfig> {

    HttpHeaders httpHeaders;


    @Context
    public void setHeaders(HttpHeaders httpHeaders) {
        this.httpHeaders = httpHeaders;
    }

    @Override
    public MoxyJsonConfig getContext(Class<?> c) {

        // Assume we are going to match the first accept
        //
        MediaType responseType = httpHeaders.getAcceptableMediaTypes().get(0);

        MoxyJsonConfig config = new MoxyJsonConfig();
        String level = responseType.getParameters().get("level");
        config.getMarshallerProperties().put(MarshallerProperties.OBJECT_GRAPH, level != null ? level : "detailed");
        return config;
    }
}

So once this little application is running, you can see the output for different mime types. In our case the default for "application/json" is "detailed" but I can see the argument in many cases for "normal" to be the default.

GET .../hello Accept application/json; level=detailed or application/json

{
    "message" : "This is indeed the message",
    "subtext" : "This is the deep and meaningful subtext",
    "summary" : "Some simple summary"
}

GET .../hello Accept application/json; level=normal

{
    "message" : "This is indeed the message",
    "summary" : "Some simple summary"
}

GET .../hello Accept application/json; level=summary

{
    "summary" : "Some simple summary"
}


This does work, but as you can see the annotations could be prettier, the next blog post will be an example using EntityFilteringFeature of Jersey. This feature builds on top of the MOXy functionality above while allowing a custom annotation scheme. This to my mind is a lot easier to work with.

Tuesday, April 30, 2013

Using Java WebSockets, JSR 356, and JSON mapped to POJO's

So I have been playing around with Tyrus, the reference implementation of the JSR 356 WebSocket for Java spec. Because I was looking at test tooling I was interested in running both the client and the server side in Java. So no HTML5 in this blog post I am afraid.

In this example we want to sent JSON back and forth and because I am old fashioned like that I want to be able to bind to a POJO object. I am going to use Jackson for this so my maven file looks like this:

<dependencies>
    <dependency>
        <groupId>javax.websocket</groupId>
        <artifactId>javax.websocket-api</artifactId>
        <version>1.0-rc3</version>
    </dependency>

    <dependency>
        <groupId>org.glassfish.tyrus</groupId>
        <artifactId>tyrus-client</artifactId>
        <version>1.0-rc3</version>
    </dependency>

    <dependency>
        <groupId>org.glassfish.tyrus</groupId>
        <artifactId>tyrus-server</artifactId>
        <version>1.0-rc3</version>
    </dependency>

    <dependency>
        <groupId>org.glassfish.tyrus</groupId>
        <artifactId>tyrus-container-grizzly</artifactId>
        <version>1.0-rc3</version>
    </dependency>
    

    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.2.0</version>
    </dependency>

    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
      <version>2.2.0</version>
    </dependency>

    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.2.0</version>
    </dependency>

  </dependencies>

So the first things we need to do is to define an implementations of the Encode/Decoder interfaces to do this work for us. This is going to do some simple reflection to workout what the bean class is. Like with JAX-WS it is easier to put them on the same class. Note that we use the streaming version of the interface and are only handling text content. (Ignoring the ability to send binary data for the moment)

package websocket;

import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;
import java.io.Reader;
import java.io.Writer;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import javax.websocket.DecodeException;
import javax.websocket.Decoder;
import javax.websocket.EncodeException;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;

public abstract class JSONCoder<T>
  implements Encoder.TextStream<T>, Decoder.TextStream<T>{


    private Class<T> _type;
    
    // When configured my read in that ObjectMapper is not thread safe
    //
    private ThreadLocal<ObjectMapper> _mapper = new ThreadLocal<ObjectMapper>() {

        @Override
        protected ObjectMapper initialValue() {
            return new ObjectMapper();
        }
    };
    

    @Override
    public void init(EndpointConfig endpointConfig) {
    
        ParameterizedType $thisClass = (ParameterizedType) this.getClass().getGenericSuperclass();
        Type $T = $thisClass.getActualTypeArguments()[0];
        if ($T instanceof Class) {
            _type = (Class<T>)$T;
        }
        else if ($T instanceof ParameterizedType) {
            _type = (Class<T>)((ParameterizedType)$T).getRawType();
        }
    }

    @Override
    public void encode(T object, Writer writer) throws EncodeException, IOException {
        _mapper.get().writeValue(writer, object);
    }

    @Override
    public T decode(Reader reader) throws DecodeException, IOException {
        return _mapper.get().readValue(reader, _type);
    }

    @Override
    public void destroy() {
        
    }

}

The bean class is really quite simple with a static subclass of the Coder that we can use later.

package websocket;

public class EchoBean {
    
    
    public static class EchoBeanCode extends
       JSONCoder<EchoBean> {
        
    }
    
    
    private String _message;
    private String _reply;


    public EchoBean() {
        
    }

    public EchoBean(String _message) {
        super();
        this._message = _message;
    }


    public void setMessage(String _message) {
        this._message = _message;
    }

    public String getMessage() {
        return _message;
    }


    public void setReply(String _reply) {
        this._reply = _reply;
    }

    public String getReply() {
        return _reply;
    }

}

So new we need to implement our server endpoint, you can go one of two way either annotating a POJO or extending Endpoint. I am going with the first for the server and the second for the client. Really all this service does is to post the message back to the client. Note the registration of the encode and decoder. The same class in this case.

package websocket;

import java.io.IOException;

import javax.websocket.EncodeException;
import javax.websocket.EndpointConfig;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import static java.lang.System.out;

@ServerEndpoint(value="/echo",
                encoders = {EchoBean.EchoBeanCode.class},
                decoders = {EchoBean.EchoBeanCode.class})
public class EchoBeanService
{
    
    @OnMessage
    public void echo (EchoBean bean, Session peer) throws IOException, EncodeException {
        //
        bean.setReply("Server says " + bean.getMessage());
        out.println("Sending message to client");
        peer.getBasicRemote().sendObject(bean);
    }

    @OnOpen
    public void onOpen(final Session session, EndpointConfig endpointConfig) {
        out.println("Server connected "  + session + " " + endpointConfig);
    }
}

Lets look at a client bean, this time extending the standard Endpoint class and adding a specific listener for a message. In this case when the message is received the connection is simply closed to make our test case simple. In the real world managing this connection would obviously be more complicated.

package websocket;

import java.io.IOException;

import javax.websocket.ClientEndpoint;
import javax.websocket.CloseReason;
import javax.websocket.EncodeException;
import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
import javax.websocket.MessageHandler;
import javax.websocket.Session;

import static java.lang.System.out;

@ClientEndpoint(encoders = {EchoBean.EchoBeanCode.class},
                decoders = {EchoBean.EchoBeanCode.class})
public class EchoBeanClient 
  extends Endpoint
{
    public void onOpen(final Session session, EndpointConfig endpointConfig) {

        out.println("Client Connection open "  + session + " " + endpointConfig);
        
        // Add a listener to capture the returning event
        //
        
        session.addMessageHandler(new MessageHandler.Whole() {

            @Override
            public void onMessage(EchoBean bean) {
                out.println("Message from server : " + bean.getReply());
                
                out.println("Closing connection");
                try {
                    session.close(new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, "All fine"));
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        
        // Once we are connected we can now safely send out initial message to the server
        //
        
        out.println("Sending message to server");
        try {
            EchoBean bean = new EchoBean("Hello");
            session.getBasicRemote().sendObject(bean);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (EncodeException e) {
            e.printStackTrace();
        }

    }
}

Now running the WebSocket standalone is really quite straightforward with Tyrus, you simple instantiate a Server and start it. Be aware this starts daemon threads so you need to make sure if this is in a main method that you do something to keep the JVM alive.

import org.glassfish.tyrus.server.Server;

Server server = new Server("localhost", 8025, "/", EchoBeanService.class);
server.start();


So the client is relatively simple; but as we are doing the declarative method we need to explicitly register the encoders and decoders when registering the client class.

import javax.websocket.ClientEndpointConfig;
import javax.websocket.Decoder;
import javax.websocket.Encoder;
import javax.websocket.Session;

import org.glassfish.tyrus.client.ClientManager;


// Right now we have to create a client, which will send a message then close
// when it has received a reply
//

ClientManager client = ClientManager.createClient();
EchoBeanClient beanClient = new EchoBeanClient();

Session session = client.connectToServer(
        beanClient, 
        ClientEndpointConfig.Builder.create()
         .encoders(Arrays.<Class<? extends Encoder>>asList(EchoBean.EchoBeanCode.class))
         .decoders(Arrays.<Class<? extends Decoder>>asList(EchoBean.EchoBeanCode.class))
         .build(),
        URI.create("ws://localhost:8025/echo"));
        
        
// Wait until things are closed down
        
while (session.isOpen()) {
    out.println("Waiting");
    TimeUnit.MILLISECONDS.sleep(10);
}

Now the output of this looks like the following:

Server connected SessionImpl{uri=/echo, id='e7739cc8-1ce5-4c26-ad5f-88a24c688799', endpoint=EndpointWrapper{endpointClass=null, endpoint=org.glassfish.tyrus.core.AnnotatedEndpoint@1ce5bc9, uri='/echo', contextPath='/'}} javax.websocket.server.DefaultServerEndpointConfig@ec120d
Waiting
Client Connection open SessionImpl{uri=ws://localhost:8025/echo, id='7428be2b-6f8a-4c40-a0c4-b1c8b22e1338', endpoint=EndpointWrapper{endpointClass=null, endpoint=websocket.EchoBeanClient@404c85, uri='ws://localhost:8025/echo', contextPath='ws://localhost:8025/echo'}} javax.websocket.DefaultClientEndpointConfig@15fdf14
Sending message to server
Waiting
Waiting
Waiting
Waiting
Waiting
Waiting
Waiting
Waiting
Waiting
Waiting
Sending message to client
Message from server : Server says Hello
Closing connection
Waiting

Interestingly the first time this is run the there is a pause, I suspect this is due to Jackson setting itself up but I haven't had time to profile. I did find that this long delay on occurred on the first post - although obviously this is going to be slower than just passing plain text messages in general. Whether the different is significant to you depends on your application.

It would be interesting to compare the performance of the plain text with a JSON stream API such as that provided by the new JSR and of course the version that binds those values to a JSON POJO. Something for another day perhaps.

Monday, November 19, 2012

JSON-Schema generation in Jersey

So in my previous post I talked about a proposal to allow the use of JSON-Schema in a WADL, this post looks at how to get this working with a recently build of Jersey. You are going to have to download / reference 1.16SNAPSHOT until 1.16 is released.

If you are using Maven this should be quite straight forward to update your dependencies assuming you already have jersey and jersey-json. You are just going to need to add a dependency on the "jersey-wadl-json-schema" artefact from the "com.sun.jersey.contribs" group to get the new feature.

If you are outside of Maven the easiest thing to do would be to download the latest jersey-archive and then the jersey-wadl-json-schema jar. How you deploy these is tool specific, but if you are using WLS then here are some specific notes on how to upgrade the version of Jersey.

Once you have this working, you need to create a WadlGeneratorConfig class in order to enable this new grammar generation:

package jersey;

import com.sun.jersey.api.wadl.config.WadlGeneratorConfig;
import com.sun.jersey.api.wadl.config.WadlGeneratorDescription;
import com.sun.jersey.wadl.generators.json.WadlGeneratorJSONGrammarGenerator;

import java.util.List;

public class JsonGeneratorConfig extends WadlGeneratorConfig {
 
    @Override
    public List configure() {
        return generator(WadlGeneratorJSONGrammarGenerator.class).descriptions();
    }
}

This can then be registered in a variety of ways, here is an example using a servlet init param. Note also that to make this example simple that we are using the Jersey POJO mapping; but whilst writing this blog I noticed that this setting affected the format of the JSON version of the WADL in case you try this.

<?xml version = '1.0' encoding = 'ISO-8859-1'?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">
  <servlet>
    <servlet-name>jersey</servlet-name>
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
    <init-param>
      <param-name>com.sun.jersey.config.property.WadlGeneratorConfig</param-name>
      <param-value>jersey.JsonGeneratorConfig</param-value>
    </init-param>
    <init-param>
      <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
      <param-value>true</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>jersey</servlet-name>
    <url-pattern>/resources/*</url-pattern>
  </servlet-mapping>
</web-app>

So I put together a really simple echo service, just to check this is all working:

package jersey;

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

@Path("/echo")
public class EchoResource {
     

    @GET
    @Produces("application/json")
    public EchoBean echo() {
        EchoBean bean = new EchoBean();
        bean.setMessage("Hello");
        return bean;
    }

    @POST
    @Consumes("application/json")
    @Produces("application/json")
    public EchoBean echo(EchoBean echo) {
        return echo;
    }
    
}

and

package jersey;

public class EchoBean {
    public EchoBean() {
        super();
    }
    
    private String message;

    public void setMessage(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }


}

This very simple example results in the following WADL with the JSON-Schema elements referenced:

<?xml version = '1.0' encoding = 'UTF-8'?>
<ns0:application xmlns:ns0="http://wadl.dev.java.net/2009/02">
   <ns0:doc xmlns:ns1="http://jersey.java.net/" ns1:generatedBy="Jersey: 1.16-SNAPSHOT 11/19/2012 12:59 AM"/>
   <ns0:grammars/>
   <ns0:resources base="http://localhost:7103/Jersey/resources/">
      <ns0:resource path="/echo">
         <ns0:method id="echo" name="GET">
            <ns0:response>
               <ns0:representation mediaType="application/json" xmlns:ns2="http://wadl.dev.java.net/2009/02/json-schema" ns2:describedby="application.wadl/echoBean"/>
            </ns0:response>
         </ns0:method>
         <ns0:method id="echo" name="POST">
            <ns0:request>
               <ns0:representation mediaType="application/json" xmlns:ns3="http://wadl.dev.java.net/2009/02/json-schema" ns3:describedby="application.wadl/echoBean"/>
            </ns0:request>
            <ns0:response>
               <ns0:representation mediaType="application/json" xmlns:ns4="http://wadl.dev.java.net/2009/02/json-schema" ns4:describedby="application.wadl/echoBean"/>
            </ns0:response>
         </ns0:method>
      </ns0:resource>
   </ns0:resources>
</ns0:application>

The URI application.wadl/echoBean contains this simple JSON-Schema definition:

{
    "type" : "object",
    "properties" : {
        "message" : {
            "type" : "string"
        }
    },
    "name" : "echoBean"
}

Now there are a number of limitations in the current design, not least that the generated schema doesn't take into account any notation settings. But I thought this would be enough to provoke feedback on whether this feature would be useful generally. There appears to be a growing interest in JSON-Schema both around the net and internally to Oracle, so it would be interesting to see whether this description becomes more common.

Friday, November 16, 2012

JSON-Schema in WADL

In between other jobs I have been recently been reviewing the WADL specification with a view to fixing some documentation problems with a view to producing an updated version. One of the things that because apparent was the lack of any grammar support for languages other than XML - yes you can use a mapping from JSON<->XML Schema but this would be less than pleasant for a JSON purist.

So I began to look at how one would go about attaching a JSON-Schema grammar of a JSON document in a WADL description of a service. This isn't a specification yet; but a proposal of how it might work consistently.

Now I work with Jersey mostly, so lets consider what Jersey will current generate for a service that returns both XML and JSON. So the service here is implemented using the JAX-B binding so they both use a similar structure as defined by the XML-Schema reference by the include.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<application xmlns="http://wadl.dev.java.net/2009/02">
    <doc xmlns:jersey="http://jersey.java.net/" jersey:generatedBy="Jersey: 1.16-SNAPSHOT 10/26/2012 09:28 AM"/>
    <grammars>
        <include href="xsd0.xsd">
            <doc title="Generated" xml:lang="en"/>
        </include>
    </grammars>
    <resources base="http://localhost/">
        <resource path="/root">
            <method id="hello" name="PUT">
                <request>
                    <representation xmlns:m="urn:message" element="m:requestMessage"  mediaType="application/json" />
                    <representation xmlns:m="urn:message" element="m:requestMessage" mediaType="application/xml" />
                </request>
                <response>
                    <representation xmlns:m="urn:message" element="m:responseMessage" mediaType="application/json"/>
                    <representation xmlns:m="urn:message" element="m:responseMessage" mediaType="application/xml" />
                </response>
            </method>
        </resource>
    </resources>
</application> 

So the first thing we considered was re-using the existing element property, which is defined as a QName, on the representation element to reference an imported JSON-Schema. It is shown here both with and another an arbitrary namespace to it can be told apart from XML elements without a namespace.

<grammars>
        <include href="xsd0.xsd" />
        <include href="application.wadl/responseMessage" />
    </grammars>


    <representation element="responseMessage" mediaType="application/json"/>

Or
    xmlns:json="http://wadl.dev.java.net/2009/02/json" 

    <representation 
        element="json:responseMessage" mediaType="application/json" />


The problem is that the JSON-Schema specification as it stands doesn't have a concept of a "name" property, so each JSON-Schema is uniquely identified by it's URI. Also from my read of the specification each JSON-Schema contains the definition for at most one document - not the multiple types / documents that can be contained in XML-Schema.

So the next best suggestion would be to just use the "filename" part of the URI as a proxy for the URI; but of course that won't necessarily be unique. I could see for example the US government and Yahoo both publishing there own "address" micro format.

The better solution to this problem is to introduce a new attribute, luckily the WADL spec was designed with this in mind, that is of type URI that can be used to directly reference the JSON-Schema definitions. So rather than the direct import in the previous example we have a URI property on the element itself. The "describedby" attribute name comes from the JSON-Schema proposal and is consistent with the rel used on atom links in the spec.

xmlns:json="http://wadl.dev.java.net/2009/02/json-schema" 
    xmlns:m="urn:message" 


    <grammars>
        <include href="xsd0.xsd" />
    </grammars>

    <representation 
        mediaType="application/json"
        element="m:responseMessage" 
        json:describedby="application.wadl/responseMessage" />


The has the secondary advantage in that this format is backwardly compatible with tooling that was relying on the XML-Schema grammar. Although this is probably only of interesting to people who work in tooling / testing tools like myself.

Once you have the JSON-Schema definition then some users are going to want to do away with the XML all together, so finally here is a simple mapping of the WADL to a JSON document that contains just the JSON-Schema information. It has been suggested by Sergey Breyozkin the JSON mapping would only show the json grammars and I am coming around to that way of thinking. I would be interested to hear of a usecase for the JSON mapping that would want access to the XML Schema.

{
   "doc":{
      "@generatedBy":"Jersey: 1.16-SNAPSHOT 10/26/2012 09:28 AM"
   },
   "resources":{
      "@base":"http://localhost/",
      "resource":{
         "@path":"/root",
         "method":{
            "@id":"hello",
            "@name":"PUT",
            "request":{
               "representation":[
                  {
                     "@mediaType":"application/json",
                     "@describedby":"application.wadl/requestMessage"
                  }
               ]
            },
            "response":{
               "representation":[
                  {
                     "@mediaType":"application/json",
                     "@describedby":"application.wadl/responseMessage"
                  }
               ]
            }
         }
      }
   }
}

I am currently using the mime type of "application/vnd.sun.wadl+json" for this mapping to be consistent with the default WADL mime type. I suspect we would want to change this in the future; but it will do for starters.

So this is all very interesting but you can't play with it unless you have an example implementation. I have something working for both the server side and for a Java client generator in Jersey and wadl2java respectively and that will be the topic of my next post. I have been working with Pavel Bucek on the Jersey team on these implementations and the WADL proposal, thanks very much to him for putting up with me.