25 March 2014

JSON Parsing is Cake with WebLogic Server 12.1.3

Another feature of WebLogic Server 12.1.3 that developers will find really useful is the inclusion of an implementation of JSR-353 Java API for JSON Processing.

See Chapter 10 Java API for JSON Processing in the Developing Applications for Oracle WebLogic Server book @ http://docs.oracle.com/middleware/1213/wls/WLPRG/java-api-for-json-proc.htm#WLPRG1055

The original JSR submission for this API provides a good description of what it sets out to do.

JSR 353: JavaTM API for JSON Processing
 
This new API, working from the foundations provided by earlier implementations such as Jackson, Jettison and Google JSon, provides a standard API for working with JSON from Java. The goals and objectives of the API are described in the specification request as:
 JSON(JavaScript Object Notation) is a lightweight data-interchange format.

Many popular web services use JSON format for invoking and returning the data.

Currently Java applications use different implementation libraries to produce/consume JSON from the web services. Hence, there is a need to standardize a Java API for JSON so that applications that use JSON need not bundle the implementation libraries but use the API. Applications will be smaller in size and portable.

The goal of this specification is to develop such APIs to:
  • Produce and consume JSON text in a streaming fashion(similar to StAX API for XML)
  • Build a Java object model for JSON text using API classes(similar to DOM API for XML)
WebLogic Server 12.1.3 includes a module which contains the API/implementation of this relatively lightweight but important API, enabling developers and applications to more easily work with JSON in a portable, standard manner.

 Unlike JAX-RS 2.0 and JPA 2, both of which have pre-existing specification versions that need to be supported by default, there are no additional steps required for applications to use this API with WebLogic Server 12.1.3.  It's simply included as a default module of the server and available for any application to make use of.
The API and implementation is located in this jar file in a WebLogic Server 12.1.3 installation:

$ORACLE_HOME/wlserver/modules/javax.json_1.0.0.0_1-0.jar

In the my previous post, Using the JAX-RS 2.0 Client API with WebLogic Server 12.1.3
I have a short example of using the API to parse an JAX-RS supplied InputStream to marshall a JSON payload into a Java object.

        
        ...
        GeoIp g = new GeoIp();
        JsonParser parser = Json.createParser(entityStream);
        while (parser.hasNext()) {
            switch (parser.next()) {
                case KEY_NAME:
                    String key = parser.getString();
                    parser.next();
                    switch (key) {
                        case "ip":
                            g.setIpAddress(parser.getString());
                            break;
                        case "country_name":
                            g.setCountryName(parser.getString());
                            break;
                        case "latitude":
                            g.setLatitude(parser.getString());
                            break;
                        case "longitude":
                            g.setLongitude(parser.getString());
                            break;
                        case "region_name":
                            g.setRegionName(parser.getString());
                            break;
                        case "city":
                            g.setCity(parser.getString());
                            break;
                        case "zipcode":
                            g.setZipCode(parser.getString());
                            break;
                        default:
                            break;
                    }
                    break;
                default:
                    break;
            }
        }
        ...
 
The Java EE 7 tutorial has a section showing how to use the new javax.json API which is well worth having a look at if working with JSON is your thing.

http://docs.oracle.com/javaee/7/tutorial/doc/jsonp.htm

Arun Gupta also has a good hands-on lab under development for Java EE 7 that uses the JSON API to read and write JSON into Java objects that represent a movie database.   His examples collaborate with JAX-RS to issue both GET and POST calls to read and update data using JSON payload.

https://github.com/javaee-samples/javaee7-samples



24 March 2014

Using the JAX-RS 2.0 Client API with WebLogic Server 12.1.3



Please note: this blog discusses WebLogic Server 12.1.3
which has not yet been released.

As part of the JAX-RS 2.0 support we are providing with WebLogic Server 12.1.3, one really useful new feature is the new Client API it provides, enabling applications to easily interact with REST services to consume and publish information.

By way of a simple example, I'll build out an application that uses the freegeoip.net REST service to lookup the physical location of a specified IP address or domain name and deploy it to WebLogic Server 12.1.3.

The first step to perform is to make a call to the freegeoip.net REST API and examine the JSON payload that is returned.
$ curl http://freegeoip.net/json/buttso.blogspot.com

{"ip":"173.194.115.75","country_code":"US","country_name":"United States","region_code":"CA","region_name":"California","city":"Mountain View","zipcode":"94043","latitude":37.4192,"longitude":-122.0574,"metro_code":"807","area_code":"650"}

The next step is to build a Java class to represent the JSON payload that is returned. In this case, it's quite simple because the JSON payload that is returned doesn't contain any relationships or complex data structures.
/**
 *
 * @author sbutton
 * {"ip":"173.194.115.75","country_code":"US","country_name":"United States","region_code":"CA","region_name":"California","city":"Mountain View","zipcode":"94043","latitude":37.4192,"longitude":-122.0574,"metro_code":"807","area_code":"650"}            
 */
public class GeoIp implements Serializable {

    private String ipAddress;
    private String countryName;
    private String regionName;
    private String city;
    private String zipCode;
    private String latitude;
    private String longitude;

    public String getIpAddress() {
        return ipAddress;
    }

    public void setIpAddress(String ipAddress) {
        this.ipAddress = ipAddress;
    }    

    ...

}
With the GeoIP class defined, the next step is to consider how to convert the JSON payload into an instance of the GeoIP class. I'll show two ways this can be done.

The first way to do it is to create a class that reads the result of the REST request, parses the JSON payload and constructs a representative instance of the GeoIP class. Within the JAX-RS API, there is an interface MessageBodyReader that can be implemented to convert a Stream into a Java type.

http://docs.oracle.com/javaee/6/api/javax/ws/rs/ext/MessageBodyReader.html

Implementing this interface gives you the readFrom(Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, InputStream entityStream) method which supplies an InputStream containing the response to read. The method then parses out the JSON payload and constructs a responding GeoIP instance from it.

Parsing the JSON payload is straightforward with WebLogic Server 12.1.3 since we've included the (JSR-353) Java API for JSON Processing implementation which provides an API for reading and creating JSON objects.
package oracle.demo.wls.jaxrs.client.geoip;

import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import javax.json.Json;
import javax.json.stream.JsonParser;
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.Provider;

@Provider
@Produces(MediaType.APPLICATION_JSON)
public class GeoIpReader implements MessageBodyReader {

    @Override
    public boolean isReadable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        return GeoIp.class.isAssignableFrom(type) ;
    }

    @Override
    public GeoIp readFrom(Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, InputStream entityStream) throws IOException, WebApplicationException {
        GeoIp g = new GeoIp();
        JsonParser parser = Json.createParser(entityStream);
        while (parser.hasNext()) {
            switch (parser.next()) {
                case KEY_NAME:
                    String key = parser.getString();
                    parser.next();
                    switch (key) {
                        case "ip":
                            g.setIpAddress(parser.getString());
                            break;
                        case "country_name":
                            g.setCountryName(parser.getString());
                            break;
                        case "latitude":
                            g.setLatitude(parser.getString());
                            break;
                        case "longitude":
                            g.setLongitude(parser.getString());
                            break;
                        case "region_name":
                            g.setRegionName(parser.getString());
                            break;
                        case "city":
                            g.setCity(parser.getString());
                            break;
                        case "zipcode":
                            g.setZipCode(parser.getString());
                            break;
                        default:
                            break;
                    }
                    break;
                default:
                    break;
            }
        }
        return g;
    }
}

Once this class is built, it can be registered with the Client so that it can be called when necessary to convert a payload of MessageType.APPLICATION_JSON type into an instance of the GeoIP object, here done in an @PostConstruct method on a JSF Bean
    @PostConstruct
    public void init() {
        client = ClientBuilder.newClient();
        client.register(GeoIpReader.class);
    }


The alternative way to do thi is to use the EcliseLink MOXY JAXB implementation that is provided with WebLogic Server, which can automatically marhsall and unmarshall JSON payloads to and from Java objects. Helpfully, the JAX-RS 2.0 shared-library that WebLogic Server 12.1.3 contains the jersey-media-moxy extension that enables the EclipseLInk MOXY implementation to be simply registered and used by applications when conversion is needed.

To use the JAXB/MOXY approach, the GeoIPReader class can be thrown away. No manual parsing of the payload is required. Instead, the base GeoIP class is annotated with JAXB annotations to denote it as being JAXB enabled and to provide some assistance in the mapping of the class properties to the payload property names.
package oracle.demo.wls.jaxrs.client.geoip;

import java.io.Serializable;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;

/**
 *
 * @author sbutton
 * {"ip":"173.194.115.75","country_code":"US","country_name":"United States","region_code":"CA","region_name":"California","city":"Mountain View","zipcode":"94043","latitude":37.4192,"longitude":-122.0574,"metro_code":"807","area_code":"650"}            
 */

@XmlRootElement
public class GeoIp implements Serializable {
    
    @XmlAttribute(name = "ip")
    private String ipAddress;
    @XmlAttribute(name = "country_name")
    private String countryName;
    @XmlAttribute(name = "region_name")
    private String regionName;
    @XmlAttribute(name = "city")
    private String city;
    @XmlAttribute(name = "zipcode")
    private String zipCode;
    @XmlAttribute(name = "latitude")
    private String latitude;
    @XmlAttribute(name = "longitude")
    private String longitude;

    ...
   
}


With the JAXB annotations placed on the GeoIP class to enable it to be automatically marshalled/unmarshalled from JSON, the last step is to register the EclipseLink MOXY implementation with the Client. This is done with the assistance of a small utility method, as shown in the Jersey User Guide Media chapter.
    public static ContextResolver createMoxyJsonResolver() {
        final MoxyJsonConfig moxyJsonConfig = new MoxyJsonConfig();
        moxyJsonConfig.setFormattedOutput(true);

        Map namespacePrefixMapper = new HashMap(1);
        namespacePrefixMapper.put("http://www.w3.org/2001/XMLSchema-instance", "xsi");
        moxyJsonConfig.setNamespacePrefixMapper(namespacePrefixMapper).setNamespaceSeparator(':');

        return moxyJsonConfig.resolver();
    }
This method is then used to register the relevant ContextResolver with the Client to use to handle JSON_conversions, instead of the GeoIPReader class that was used before.<
    @PostConstruct
    public void init() {
        client = ClientBuilder.newClient();
        client.register(createMoxyJsonResolver());
        //client.register(GeoIpReader.class);
    }

With the JSON payload to GeoIP conversion now covered, the JAX-RS Client API can be used to make the call to the freegeoip REST service and process the response.

To make a client call, two classes are used: javax.ws.rs.client.Client and javax.ws.rs.client.WebTarget .

The Jersey User Guide provides a good description of theses two classes and their relationship:

The JAX-RS Client API is a designed to allow fluent programming model. This means, a construction of a Client instance, from which a WebTarget is created, from which a request Invocation is built and invoked can be chained in a single "flow" of invocations ... Once you have a Client instance you can create a WebTarget from it ... A resource in the JAX-RS client API is an instance of the Java class WebTarget and encapsulates an URI. The fixed set of HTTP methods can be invoked based on the WebTarget. The [base] representations are Java types, instances of which, may contain links that new instances of WebTarget may be created from.

In this example application, the Client is opened in an @PostConstruct method and closed in a @PreDestroy method, with the WebTarget being created and its GET method called when the lookup is executed by the user.
@Named
@RequestScoped
public class GeoIpBackingBean {

    private WebTarget target = null;
    private Client client = null;

    ...

    @PostConstruct
    public void init() {
        client = ClientBuilder.newClient();
        //client.register(createMoxyJsonResolver());
        client.register(GeoIpReader.class);
    }

    @PreDestroy
    public void byebye() {
        client.close();
    }

    public void lookupAddress() {
        try {
            target = client.target(String.format(rest_base_url, addressToLookup));
            geoIp = target.request().get(GeoIp.class);
        } catch (Exception e) {
            e.printStackTrace();
            FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Error executing REST call: " + e.getMessage()));
        }
    }
 
    ...
}  


Bringing it all together as a JSF based application results in a JSF Bean being created that allows the IP address to be entered and a method that invokes the JAX-RS Client API to call out to the freegeoip.net REST service to retrieve the JSON payload containing the location information. A simple JSF facelet page is used to support the entering of the IP address and the display of the relevant data from the GeoIP object.


    <h:form>
        <h:panelGrid columns="2" style="vertical-align: top;">
        <h:outputLabel value="Address"/>
        <h:inputText value="${geoIpBackingBean.addressToLookup}"/>
        <h:outputLabel value=""/>
        <h:commandButton action="${geoIpBackingBean.lookupAddress()}" value="Lookup" style="margin: 5px;"/>
        </h:panelGrid>
    </h:form>


    <h:panelGrid columns="2">
        <h:outputText value="IP:"/>
        <h:outputText value="${geoIpBackingBean.geoIp.ipAddress}"/>
        <h:outputText value="Country Code:"/>
        <h:outputText value="${geoIpBackingBean.geoIp.countryName}"/>
        <h:outputText value="State:"/>
        <h:outputText value="${geoIpBackingBean.geoIp.regionName}"/>
        <h:outputText value="City"/>
        <h:outputText value="${geoIpBackingBean.geoIp.city}"/>
        <h:outputText value="Zipcode:"/>
        <h:outputText value="${geoIpBackingBean.geoIp.zipCode}"/>
        <h:outputText value="Coords:"/>
        <c:if test="${geoIpBackingBean.geoIp.ipAddress != null}">
            <h:outputText value="${geoIpBackingBean.geoIp.latitude},${geoIpBackingBean.geoIp.longitude}"/>
        </c:if>
      </h:panelGrid>

The last step to perform is to add a weblogic.xml deployment descriptor with a library-ref to the [jsf,2.0] shared-library, which must be deployed as I described earlier in Using JAX-RS 2.0 with WebLogic Server 12.1.3.

The application is now ready to to deploy and run.

21 March 2014

Using JAX-RS 2.0 in WebLogic Server 12.1.3



Please note: this blog discusses WebLogic Server 12.1.3
which has not yet been released.

We've been working on adding some Java API updates to the coming WebLogic Server 12.1.3 release.

One that I think is going to be very popular is JAX-RS 2.0, which includes some useful new capabilities around filtering, interception and a really useful new client API.

 In the WebLogic Server 12.1.3 release we are providing this in the form of an optional shared-library that contains: the JAX-RS 2.0 API, a Jersey 2.x implementation, some common Jersey extensions such as media support and a utility that works to expose the JAX-RS 2.0 API to referencing applications.

To make use of it, developers first deploy the jax-rs-2.0.war shared-library from the $ORACLE_HOME/wlserver/common/deployable-libraries directory to the server (or cluster) then use it in an application by referencing it as a library using a weblogic deployment descriptor.

Using the library-name and specification-version attributes from the JSF library, an example of a weblogic.xml to use it would be (lines:8-11):

<?xml version="1.0" encoding="UTF-8"?>  
<weblogic-web-app>  
    <jsp-descriptor>  
        <keepgenerated>true</keepgenerated>  
        <debug>true</debug>  
    </jsp-descriptor>  
    <context-root>/service-centre</context-root>  
    <library-ref>  
        <library-name>jax-rs</library-name>  
        <specification-version>2.0</specification-version>  
    </library-ref>  
</weblogic-web-app>