11 December 2014

Exploring DevOps with Chef and WebLogic Server

I'm about to embark on a journey that explores the use of WebLogic Server within a DevOps regime.  My first port of call for this journey will be using Chef.

A loose travel itinerary is:
  • Setting up an environment to explore the basic operations of Chef - using the Chef Development Kit (ChefDK)
  • Exploring the basics of how Chef works to install Java and WebLogic Server on a single node
  • Installing and examining some of the existing cookbooks that are available for Java and WebLogic Server
  • Extending the environment to provision multiple nodes to create a typical multiple machine clustered WebLogic Server environment
I've started working on the first task, where I've also explored using Docker to create an isolated, reusable and easily shareable environment that contains the ChefDK.

The Docker project is here on GitHub:
I also tried a quick experiment with using Oracle Linux as the base docker image:
The Dockerfile contains the set of instructions required to install the ChefDK and the necessary utilities into the docker image when it is built.

#
# Dockerfile for Chef 4 WLS Environment 
# 

FROM ubuntu

MAINTAINER Steve Button <>

ENV     DEBIAN_FRONTEND noninteractive

# Install Utilities
RUN apt-get update
RUN apt-get install -yq wget
RUN apt-get install -yq curl
RUN apt-get install -yq git

# Install Chef
RUN wget https://opscode-omnibus-packages.s3.amazonaws.com/ubuntu/12.04/x86_64/chefdk_0.3.5-1_amd64.deb
RUN dpkg -i chefdk*.deb

# Verify and Setup Chef
RUN chef verify 
RUN echo 'eval "$(chef shell-init bash)"' << ~/.bashrc

...

CMD ["/bin/bash"]

With this Dockerfile a build operation can be performed that produces a docker image, which can then be run to provide an environment in which start exploring the Chef.

$ docker build -t buttso/chef4wls .

$ docker run -ti buttso/chef4wls

oracle@5481a3330f27:~$ which chef-client
/opt/chefdk/embedded/bin/chef-client

This is just a brief outline - I will describe this first task in more detail once I get a bit further along and can verify everything has been installed and works correctly.

12 September 2014

Retro Day - Workload Management in WebLogic Server

Quite recently, I stumbled across this older but still very relevant whitepaper on the workload management capabilities of  WebLogic Server.  Written by one of the key engineers at the time, who later went on to become an architectect, it explains the workings of the new work load management feature introduced into WebLogic Server at the time and covers the concepts of work-manager configuration and effect, work scheduling and prioritization, overload protection and  more.

This is well worth a read if you are ever looking for good information on the work load management feature of WebLogic Server:

http://www.oracle.com/technetwork/articles/entarch/workload-management4-101578.html

31 July 2014

Developing with JAX-RS 2.0 for WebLogic Server 12.1.3

In an earlier post on the topic of Using JAX-RS 2.0 with WebLogic Server 12.1.3, I described that we've utilized the shared-library model to distribute and enable it.

This approach exposes the JAX-RS 2.0 API and enlists the Jersey 2.x implementation on the target server, allowing applications to make use of it as when they are deployed through a library reference in a weblogic deployment descriptor.

The one resulting consideration here from a development perspective is that since this API is not part of the javaee-api-6.jar nor is it a default API of the server, it's not available in the usual development API libraries that WebLogic provides.

For instance the $ORACLE_HOME/wlserver/server/lib/api.jar doesn't contain a reference to the JAX-RS 2.0 API, nor do the set of maven artifacts we produce and push to a repository via the oracle-maven-sync plugin contain the javax.ws.rs-api-2.0.jar library.

To develop an application using JAX-RS 2.0 to deploy to WebLogic Server 12.1.3, the javax.ws.rs-api-2.0.jar needs to be sourced and added to the development classpath.

Using maven, this is very simple to do by adding an additional dependency for the javax.ws.rs:javax.ws.rs-api:2.0 artifact that is hosted in public maven repositories:

    <dependency>
        <groupid>javax.ws.rs</groupid>
        <artifactid>javax.ws.rs-api</artifactid>
        <version>2.0</version>
        <scope>provided</scope>
    </dependency>

Note here that the scope is set to provided since the library will be realized at runtime through jax-rs-2.0.war shared-library that it deployed to the target server and referenced by the application. It doesn't need to be packaged with the application to deploy to WebLogic Server 12.1.3.

For other build systems using automated dependency management such as Gradle or Ant/Ivy, the same sort of approach can be used.

For Ant based build systems, the usual approach of obtaining the necessary API libraries and adding them to the development CLASSPATH will work. Be mindful that there is no need to bundle the jax.ws.rs-ap-2.0.jar in the application itself as it will be available from the server when correctly deployed and referenced in the weblogic deployment descriptor.

30 July 2014

Using Eclipse (OEPE) to Develop Applications using WebSocket and JSON Processing API with WebLogic Server 12.1.3

Following from my last posting, I thought I'd also show how Eclipse (OEPE) makes the new Java EE 7 APIs available from Oracle WebLogic Server 12.1.3.

The first step was downloading and installing the Oracle Enterprise Pack for Eclipse (OEPE) distribution from OTN.

http://www.oracle.com/technetwork/developer-tools/eclipse/downloads/index.html

Firing up Eclipse, the next step is to add a new Server type for Oracle WebLogic Server 12.1.3, pointing at a local installation.






With that done, I then created a new Dynamic Web Project that was directed to work against the new WebLogic Server type I'd created.  Looking at the various properties for the project, you can see that the WebSocket 1.0 and JSON Programming 1.0 libraries are automatically picked up and added to the Java Build Path of the application, by virtue of being referenced as part of the WebLogic System Library.



Into this project, I then copied over the Java source and HTML page from my existing Maven project, which compiled and built successfully.

For new applications using these APIs, Eclipse will detect the use of the javax.websocket API and annotations, the javax.json API calls and so forth and present you with a dialog asking you if you want to import the package to the class to resolve the project issues.



 With the application now ready, selecting the Run As > Run on Server menu option launches WebLogic Server, deploys the application and opens an embedded browser instance to access the welcome page of the application.


And there's the test application built in Eclipse using the WebSocket and JSON Processing APIs running against WebLogic Server 12.1.3.


Developing with the WebSocket and JSON Processing API with WebLogic Server 12.1.3 and Maven

Oracle WebLogic Server 12.1.3 provides full support for Java EE 6 and also adds support for a select set of APIs from Java EE 7.

The additional APIs are:
  • JSR 356 - Java API for WebSocket 1.0
  • JSR 353 - Java API for JSON Processing
  • JSR 339 - Java API for RESTful Web Services 2.0
  • JSR 338 - Java Persistence API 2.1
See the "What's New in 12.1.3 Guide" at http://docs.oracle.com/middleware/1213/wls/NOTES/index.html#A1011612131 for more general information.

At runtime, the WebSocket and JSON Processing APIs are available as defaults and don't require any form of post installation task to be performed to enable their use by deployed applications.

On the other hand, the JPA and JAX-RS APIs require a step to enable them to be used by deployed applications.

Developing with the WebSocket and JSON Processing APIs using Maven

 

To create applications with these APIs for use with Oracle WebLogic Server 12.1.3, the API needs to be made available to the development environment.  Typically when developing Java EE 6 applications, the javax:javaee-web-api artifact is used from the following dependency:
<dependency>
    <groupId>javax</groupId>
    <artifactId>javaee-web-api</artifactId>
    <version>6.0</version>
    <scope>provided</scope>
</dependency>

As the WebSocket and JSON Processing APIs are not part of the Java EE 6 API, they need to be added to the project as dependencies.

The obvious but incorrect way to do this is to change the javax:javaee-web-api dependency to be version 7 so that they are provided as part of that dependency.  This introduces the Java EE 7 API to the application, including APIs such as  Servlet 3.1, EJB 3.2 and so forth which aren't yet supported by WebLogic Server.  Thus it presents the application developer with APIs to use that may not be available on the target server.

The correct way to add the WebSocket and JSON Processing APIs to the project is to add individual dependencies for each API using their individual published artifacts.

<dependency>
    <groupId>javax.websocket</groupId>
    <artifactId>javax.websocket-api</artifactId>
    <version>1.0</version>
    <scope>provided</scope>
</dependency>

<dependency>
    <groupId>javax.json</groupId>
    <artifactId>javax.json-api</artifactId>
    <version>1.0</version>
    <scope>provided</scope>
</dependency>

Using NetBeans, these dependencies can be quickly and correctly added using the code-assist dialog, which presents developers with options for how to resolve any missing classes that have been used in the code.



 Using the JSON Processing API with WebSocket Applications

 

 The JSON Processing API is particularly useful for WebSocket application development since it provides a simple and efficient API for parsing JSON messages into Java objects and for generating JSON from Java objects.  These tasks are very typically performed in WebSocket applications using the Encoder and Decoder interfaces, which provides a mechanism for transforming custom Java objects into WebSocket messages for sending and converting WebSocket messages into Java objects.

An Encoder converts a Java object into a form able to send as a WebSocket message, typically using JSON as the format for use within Web browser based JavaScript clients.
package buttso.demo.cursor.websocket;

import java.util.logging.Level;
import java.util.logging.Logger;
import javax.json.Json;
import javax.json.JsonObject;
import javax.websocket.EncodeException;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;

/**
 * Convert a MouseMessage into a JSON payload.
 * 
 * @author sbutton
 */
public class MouseMessageEncoder implements Encoder.Text{

    private static final Logger logger = Logger.getLogger(MouseMessageEncoder.class.getName());

    @Override
    public String encode(MouseMessage mouseMessage) throws EncodeException {
        logger.log(Level.FINE, mouseMessage.toString());
        JsonObject jsonMouseMessage = Json.createObjectBuilder()
                .add("X", mouseMessage.getX())
                .add("Y", mouseMessage.getY())
                .add("Id", mouseMessage.getId())
                .build();
        logger.log(Level.FINE, jsonMouseMessage.toString());
        return jsonMouseMessage.toString();
    }

    @Override
    public void init(EndpointConfig ec) {
        // throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    public void destroy() {
        // throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

}

An Decoder takes a String from a WebSocket message and turns it into a custom Java object, typically receiving a JSON payload that has been constructed and sent from a Web browser based JavaScript client.
package buttso.demo.cursor.websocket;

import java.io.StringReader;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.json.Json;
import javax.json.JsonObject;
import javax.websocket.Decoder;
import javax.websocket.EndpointConfig;

/**
 * Converts a JSON payload into a MouseMessage
 * 
 * @author sbutton
 */
public class MouseMessageDecoder implements Decoder.Text {

    private static final Logger logger = Logger.getLogger(MouseMessageDecoder.class.getName());

    @Override
    public MouseMessage decode(String message) {
        logger.log(Level.FINE, message);
        JsonObject jsonMouseMessage = Json.createReader(new StringReader(message)).readObject();
        MouseMessage mouseMessage = new MouseMessage();
        mouseMessage.setX(jsonMouseMessage.getInt("X"));
        mouseMessage.setY(jsonMouseMessage.getInt("Y"));
        logger.log(Level.FINE, mouseMessage.toString());
        return mouseMessage;
    }

    @Override
    public boolean willDecode(String string) {
        return true;
    }

    @Override
    public void init(EndpointConfig ec) {
        // throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    public void destroy() {
        // throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

}

The Encoder and Decoder implementations are specified as configuration elements on a WebSocket Endpoint (server and/or client) and are automatically invoked to perform the required conversion task.
@ServerEndpoint(value = "/mouse", decoders = MouseMessageDecoder.class, encoders = MouseMessageEncoder.class)
public class MouseWebSocket {

    private final Logger logger = Logger.getLogger(MouseWebSocket.class.getName());
    
    ...
    
    @OnMessage
    public void onMessage(Session peer, MouseMessage mouseMessage) throws EncodeException {
        logger.log(Level.FINE, "MouseMessage {0} from {1}", new Object[]{mouseMessage, peer.getId()});
        messages.add(mouseMessage);

        for (Session others : peer.getOpenSessions()) {
            try {
                if (!others.getId().equals(peer.getId())) {
                    mouseMessage.setId((int) peer.getUserProperties().get("id"));
                }
                others.getBasicRemote().sendObject(mouseMessage);
            } catch (IOException ex) {
                Logger.getLogger(MouseWebSocket.class.getName()).log(Level.SEVERE, null, ex);
            }

        }
    }

    ...
}
This example enables MouseMessage objects to be used in the WebSocket ServerEndpoint class to implement the required functionality and allow them to be transmitted in JSON format to and from clients. On the JavaScript client, the JSON representation is used to receive MouseMessages sent from the WebSocket Endpoint and to send MouseMessages to the same WebSocket Endpoint.

The JavaScript JSON API can be used to produce JSON representation of JavaScript objects as well as parse JSON payloads into JavaScript objects for use by the application code. For example, JavaScript logic can be used to send messages to WebSocket endpoints in JSON form using the JSON.stringify function and to create JavaScript objects from JSON messages received from a WebSocket message using the JSON.parse function.

    ...

    document.onmousemove = function(e) {
        if (tracking) {
            // send current mouse position to websocket in JSON format
            ws.send(JSON.stringify({X: e.pageX, Y: e.pageY}));
        }
    }


    ws.onmessage = function(e) {
        // convert JSON payload into JavaScript object
        mouseMessage = JSON.parse(e.data);

        // create page element using details from received 
        // MouseMessage from the WebSocket
        point = document.createElement("div");
        point.style.position = "absolute";
        point.style.zIndex = mouseMessage.Id;
        point.style.left = mouseMessage.X + "px";
        point.style.top = mouseMessage.Y + "px";
        point.style.color = colors[mouseMessage.Id];
        point.innerHTML = "∗";
        document.getElementById("mouser").appendChild(point);
    };
When running the application, the mouse events are captured from the Web client, send to the WebSocket endpoint in JSON form, converted into MouseMessages, decorated with an ID representing the client the message came from and then broadcast out to any other connect WebSocket client to display.

A very crude shared-drawing board. 

Simulatenous drawing in browser windows using WebSocket and JSON Processing API

22 April 2014

Cycle4Sam 5 - Riding 1000km in 6 days for $100k for Women's and Children's Hospital



I recently participated in a week long charity bike ride called Cycle4Sam, where funds are raised to support the palliative care unit at the Women and Children's Hospital of South Australia.

The ride is conducted on a bi-annual basis in honour of young Sam Roberts, who sadly passed away from a rare genetic disease at the age of 4. His parents, Marty and Michelle and his siblings Lucy and Charlie, created the Sam Roberts Family Fund in order to raise funds and provide help for other families who find themselves in the same situation.

http://www.cycle4sam.com/


As the major fundraising effort, the Cycle4Sam ride sees a group of riders raise funds through sponsors to participate in a ride of 1000km. This years ride started on April 12th from the regional South Australia city of Renmark and finished back in Adelaide on April 18th, covering 1000km over 6 days of riding.




Day 1 was run as a loop of the Riverland region, taking in Loxton, Berry and a number of other lovely little towns on the way back to Renmark.  A small ceremony was held at the start to honour and remember Sam and other little children who are suffering in the same way.





Day 2 was a transit stage, riding from Renmark to a small town along the River Murray called Walker Flat.



Day 3 took in a loop of the iconic Barossa Valley region  through the towns of Sedan, Angaston, Tanunda, Mt Prospect and back to Walker Flat.  This day had some nice climbing with the Sedan Hill and Menglers Hill included as the main climbs of the day on the back of the generally rolling terrain of the region.




Day 4 was another long day, riding from Walker Flat to the southern beach town of Carrackalinga.  A planned stop at the Woodstock winery to meet up with another family who were afflicted in the same way as the Roberts saw the day come to a slightly unscheduled but much welcomed end, with a short'ish bus transit down to the final stay over.


Day 5 was a rest-day, or more accurately a non-riding day for those of us with kids, which had us taking the kids for a lovely bike ride along the beach front from Normanville to Carrackalinga, followed by a King of the Mountain event up the Latvian Climb.  Tough work for little legs but they all did superbly.



Day 6 was a loop around the Fleurieu Peninsula taking in the lovely back road around Parawa down to Victor Harbor, down to Goolwa and back to Port Elliot for lunch.  A summit of the Crows Nest followed, by a ride up the Myponga Reservoir climb, closing by a screaming descent down to Carrackaling and back to Normanville.





Day 7 was the final day and took us back to Adelaide through the McLaren Vale, Mylor, Aldgate Valley, Stirling and down the freeway, where we were escorted by the SA Police through to the grounds of the Women's and Children's Hospital.



As part of the closing celebrations, the Roberts Family presented the Women's and Children's Hospital with a cheque for $100,000 that was raised by the event.



http://wchfgivinghearts.org.au/event/cycle4sam


I was in the company of an outstanding group of people performing an outstanding service for the community and I thank them all for the privilege of letting me share the event with them.





03 April 2014

MaxPermSize Be Gone!

Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=256m; support was removed in 8.0
No further commentary required.

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>