Sunday May 19, 2013

Java EE 7 Launch Webcast: Jun 12th, Register Now


The Java EE 7 Platform JSR was approved a few days ago and is ready to be released ...ta da!

Want to learn the latest and the greatest improvements in the platform from the best ?

Mark the date for webinars in two different time zones:

Jun 12, 2013, 9am PT
Jun 12, 2013, 9pm PT

Register now!

What will you see in this online event ?
  • Business Keynote (Hasan Rizvi and Cameron Purdy)
  • Technical Keynote (Linda DeMichiel)
  • Breakout Sessions on different JSRs by specification leads
  • Live Chat
  • Lots of Demos
  • Community, Partner, and Customer video testimonials

Feel free to use any of the following images to promote the launch webcast on your blog, website, social media, or elsewhere. You can also copy/paste the code fragment shown next to them:

<a
href="https://event.on24.com/eventRegistration/EventLobbyServlet?target=registration.jsp&eventid=615713&sessionid=1&key=453EBA948F6408FE613E61903CBAEBFA&partnerref=Java_EE7_Launch_glassfishblogs_06122013&sourcepage=register"><img
alt=""
src="https://blogs.oracle.com/arungupta/resource/javaee7-launch-jun12-185x225.png"
border="0" height="225" width="185"></a>



<a
href="https://event.on24.com/eventRegistration/EventLobbyServlet?target=registration.jsp&eventid=615713&sessionid=1&key=453EBA948F6408FE613E61903CBAEBFA&partnerref=Java_EE7_Launch_glassfishblogs_06122013&sourcepage=register"><img
alt=""
src="https://blogs.oracle.com/arungupta/resource/javaee7-launch-jun12-403x403.png"
border="0" height="403" width="403"></a>

Don't forget to register!

Get ready to drink from the fire hose, no throttle!

Thursday Apr 04, 2013

WebSocket Client and Server Endpoint: Annotated and Programmatic (TOTD #212)


JSR 356 defines Java API for WebSocket 1.0. It defines a standard-based programming model for creating WebSocket client and server endpoint. Both kind of endpoints can be created programmatically or using annotations. This Tip Of The Day (TOTD) provide short snippets of how to write a WebSocket client and server endpoint programmatically or using annotations.

The complete source code in this sample can be downloaded from here.

Lets start with annotation-based server endpoint.

@ServerEndpoint("/websocket")
public class MyEndpoint {
   
  @OnMessage
  public String echoText(String name) {
    return name;
  }
}

@ServerEndpoint marks the POJO as a WebSocket server endpoint. URI of the deployed endpoint is as value attribute of the annotation.  echoText method is invoked whenever a message with text payload is received by this endpoint. Payload of the message is mapped to the parameter name. A synchronous response is returned to the client using the return value.

Programmatic server endpoint can be defined as:

public class MyEndpoint extends Endpoint {

  @Override
  public void onOpen(final Session session, EndpointConfig ec) {
    session.addMessageHandler(new MessageHandler.Whole<String>() {

      @Override
      public void onMessage(String text) {
        try {
          session.getBasicRemote().sendText(text);
        } catch (IOException ex) {
          Logger.getLogger(MyEndpoint.class.getName()).log(Level.SEVERE, null, ex);
        }
      }
  });
}

A programmatic server endpoint is defined by extending Endpoint abstract class. onOpen method is overridden to be notified of when a new conversation has started. Session captures the other end of the conversation. EndpointConfig identifies the configuration object used to configure this endpoint. Multiple MessageHandlers are registered to handle text, binary, and pong messages. The first parameter of onOpen captures the other end of the conversation. A synchronous response to the client is sent by calling getBasicRemote().sendText(...) method.

Programmatic server endpoint needs to be configured using ServerApplicationConfig.

public class MyApplicationConfig implements ServerApplicationConfig {

  @Override
  public Set<ServerEndpointConfig> getEndpointConfigs(Set<Class<? extends Endpoint>> set) {
    return new HashSet<ServerEndpointConfig>() {
      {
        add(ServerEndpointConfig.Builder
            .create(MyEndpoint.class, "/websocket")
            .build());
      }
    };
  }

  @Override
  public Set<Class<?>> getAnnotatedEndpointClasses(Set<Class<?>> set) {
    return Collections.emptySet();
  }
}

WebSocket runtime scans the WAR file with all implementations of ServerApplicationConfig and registers the endpoint returned from getEndpointConfigs and getAnnotatedEndpointClasses. The URI of the server endpoint is published using ServerEndpointConfig.Builder.

Now lets take a look at annotated client endpoint.

@ClientEndpoint
public class MyClient {
  @OnOpen
  public void onOpen(Session session) {
    try {
      String name = "Duke";
      System.out.println("Sending message to endpoint: " + name);
      session.getBasicRemote().sendText(name);
    } catch (IOException ex) {
      Logger.getLogger(MyClient.class.getName()).log(Level.SEVERE, null, ex);
    }
  }
}

@ClientEndpoint marks the POJO as a WebSocket client endpoint. onOpen method is invoked whenever a new WebSocket connection is opened and is identified by @OnOpen annotation. Session captures the other end of the conversation. A synchronous message is sent to the server using session.getBasicRemote.sendText() method.

This client can connect to the endpoint as:

WebSocketContainer container = ContainerProvider.getWebSocketContainer();
String uri = "ws://localhost:8080" + request.getContextPath() + "/websocket";
container.connectToServer(MyClient.class, URI.create(uri));

And finally programmatic client endpoint.

public class MyClient extends Endpoint {
  @Override
  public void onOpen(final Session session, EndpointConfig ec) {
    session.addMessageHandler(new MessageHandler.Whole<String>() {

      @Override
      public void onMessage(String text) {
        System.out.println("Received response in client from endpoint: " + text);
      }
  });
  try {
    String name = "Duke";
      System.out.println("Sending message from client -> endpoint: " + name);
      session.getBasicRemote().sendText(name);
    } catch (IOException ex) {
      Logger.getLogger(MyClient.class.getName()).log(Level.SEVERE, null, ex);
    }
  }
}


The first parameter of onOpen captures the other end of the conversation.  EndpointConfig identifies the configuration object used to configure this endpoint. Multiple MessageHandlers are registered to handle text, binary, and pong messages. onMessage method is called whenever a message is received from the endpoint. A synchronous request to the server is sent by calling getBasicRemote().sendText(...) method.

This client can connect to the endpoint as:

WebSocketContainer container = ContainerProvider.getWebSocketContainer();
String uri = "ws://localhost:8080" + request.getContextPath() + "/websocket";
container.connectToServer(MyClient.class,
                null,
                URI.create(uri));

Now go download your GlassFish b82, samples source code, and run them.


Wednesday Mar 06, 2013

Chunked Step using Batch Applications in Java EE 7: Getting Started with GlassFish 4 (TOTD #211)


TOTD #192 explained the key concepts of JSR 352. This Tip Of The Day provides a working example of a how to write a simple chunk step using JSR 352 Reference Implementation integrated in GlassFish 4.

The source code for this sample application can be downloaded from here and works on GlassFish 4 b78.

As explained in TOTD #192, JSR 352 defines item-oriented processing using chunk step and task-oriented processing using batchlet step. A chunk consists of a reader that reads one item at a time, a processor that processes one item at a time, and a writer that aggregates 'chunk' number of items and then writes them out.

Here is an implementation of reader:

@Named
public class MyItemReader extends AbstractItemReader<MyInputRecord> {
   
    private final StringTokenizer tokens;
   
    public MyItemReader() {
        tokens = new StringTokenizer("1,2,3,4,5,6,7,8,9,10", ",");
    }
   
    @Override
    public MyInputRecord readItem() {
        if (tokens.hasMoreTokens()) {
            return new MyInputRecord(Integer.valueOf(tokens.nextToken()));
        }
        return null;
    }
}

Reader uses type information to specify the type of record that it is working on, MyInputRecord in this case. The readItem method returns null to indicate the end of items that can be read. In this case, a StringTokenizer is used to read the items but you can use an InputStream here if you like.

Here is an implementation of processor:

@Named
public class MyItemProcessor implements ItemProcessor<MyInputRecord,MyOutputRecord> {

    @Override
    public MyOutputRecord processItem(MyInputRecord t) {
        System.out.println("processItem: " + t);
       
        return (t.getId() % 2 == 0) ? null : new MyOutputRecord(t.getId() * 2);
    }
}

Processor uses type information to specify the type of input and output records its working on, MyInputRecord is the input record and MyOutputRecord is the output record in this case. The processItem method reads the input record and accepts only odd-numbered records. This is where your business logic would be implemented.

And here is an implementation of writer:

@Named
public class MyItemWriter extends AbstractItemWriter<MyOutputRecord> {

    @Override
    public void writeItems(List<MyOutputRecord> list) {
        System.out.println("writeItems: " + list);
    }
}

Writer uses type information as well to specify the type of output record, MyOutputRecord in this case. All these elements are tied together using "Job XML" as shown:

<job id="myJob" xmlns="http://batch.jsr352/jsl">
    <step id="myStep" >
        <chunk item-count="3">
            <reader ref="myItemReader"></reader>
            <processor ref="myItemProcessor"></processor>
            <writer ref="myItemWriter"></writer>
        </chunk>   
    </step>
</job>


Eventually, the references myItemReader, myItemProcessor, and myItemWriter will be resolved using CDI. But for now, the references can be explicitly resolved using "batch.xml" as shown:

<batch-artifacts xmlns="http://jcp.org.batch/jsl">
    <ref id="myItemReader" class="org.glassfish.chunk.simple.MyItemReader"/>
    <ref id="myItemProcessor" class="org.glassfish.chunk.simple.MyItemProcessor"/>
    <ref id="myItemWriter" class="org.glassfish.chunk.simple.MyItemWriter"/>
</batch-artifacts>


Once again, downloaded the source code from here and get it running on GlassFish 4 b78.

Post feedback on public@jbatch.java.net or users@glassfish.java.net.


Monday Mar 04, 2013

Consuming and Producing JSON using JAX-RS Entity Providers and JSR 353 Streaming API (TOTD# 210)


TOTD #193 explained how JAX-RS Entity Providers can be used to provide mapping between on-the-wire representations and their associated Java types. This blog shows how you can use Java API for JSON Processing (JSR 353), already integrated in GlassFish 4 promoted builds, to produce and consume JSON payload using Entity Providers.

The source code in this blog can be downloaded here and runs on GlassFish b76.

Lets say your domain object is defined as:

public class MyObject {

  private String name;
  private int age;

  //. . .
}

And your resource endpoint is defined as:

@Path("endpoint")
public class MyResource {
  @POST
  @Consumes(MediaType.APPLICATION_JSON)
  public MyObject echoObject(MyObject mo) {
    return mo;
  }
}

This is just echoing the domain object but I suspect your domain logic would be more complex than that ;-)

Using JAX-RS Client API, this endpoint can be invoked as:

WebTarget target = client.target(".../endpoint");
MyObject mo = target
               .request()
               .post(
                 Entity.entity(new MyObject("Duke", 18), MediaType.APPLICATION_JSON),
                 MyObject.class);
System.out.println("Received response: " + mo.getName() + ", " + mo.getAge() + "<br><br>");

The MessageBodyWriter.writeTo method, that writes MyObject to the underlying OutputStream, uses Streaming API from JSR 353 and looks like:

@Override
public void writeTo(MyObject t,
                    Class<?> type,
                    Type type1,
                    Annotation[] antns,
                    MediaType mt,
                    MultivaluedMap<String, Object> mm,
                    OutputStream out)
            throws IOException, WebApplicationException {
  JsonGeneratorFactory factory = Json.createGeneratorFactory();
  JsonGenerator gen = factory.createGenerator(out);
  gen.writeStartObject()
     .write("name", t.getName())
     .write("age", t.getAge())
     .writeEnd();
  gen.flush();
}

Similarly MessageBodyReader.readFrom method, that reads MyObject from the underlying InputStream, uses Streaming API from JSR 353 and looks like:

@Override
public MyObject readFrom(Class<MyObject> type,
                         Type type1,
                         Annotation[] antns,
                         MediaType mt,
                         MultivaluedMap<String, String> mm,
                         InputStream in)
                throws IOException, WebApplicationException {
  MyObject mo = new MyObject();
  JsonParser parser = Json.createParser(in);
  while (parser.hasNext()) {
    switch (parser.next()) {
      case KEY_NAME:
        String key = parser.getString();
        parser.next();
        switch (key) {
          case "name":
            mo.setName(parser.getString());
            break;
          case "age":
            mo.setAge(parser.getIntValue());
            break;
          default:
            break;
        }
        break;
      default:
        break;
    }
  }
  return mo;
}

The code is pretty straight forward and refer to Java API for JSON Processing javadocs if you need help in understanding the code.

Download the source code and enjoy!

Thursday Feb 28, 2013

Updating to latest WebSocket API - Starting with GlassFish b78 (TOTD #209)


Java API for WebSocket (JSR 356) has recently gone through a major refactoring. The changes are integrated in Tyrus b12 and now available in GlassFish b78. This Tip Of The Day (TOTD) shows the change log for WebSocket endpoint in Collaborative Whiteboard Sample (explained in TOTD #189) between GlassFish b76 and b78.

First of all, everything stays in javax.websocket.* and javax.websocket.server.* package.

There are name changes in annotations, APIs, class renames, and some behavior change as well. Pavel's blog provides a comprehensive list of changes and the annotation changes are listed here as well:

Name before b78
Name after b78
@WebSocketEndpoint
@ServerEndpoint
@WebSocketClient
@ClientEndpoint
@WebSocketMessage
@OnMessage
@WebSocketPathParam @PathParam
@WebSocketOpen
@OnOpen
@WebSocketClose
@OnClose
@WebSocketError
@OnError


Here is the change log for updating Whiteboard.java:

--- <html>Whiteboard.java (<b>849f919</b>)</html>
+++ <html><b>Current File</b></html>
@@ -45,50 +45,48 @@
import java.util.HashSet;
import java.util.Set;
import javax.websocket.EncodeException;
+import javax.websocket.OnClose;
+import javax.websocket.OnMessage;
+import javax.websocket.OnOpen;
import javax.websocket.Session;
-import javax.websocket.WebSocketClose;
-import javax.websocket.WebSocketMessage;
-import javax.websocket.WebSocketOpen;
-import javax.websocket.server.WebSocketEndpoint;
+import javax.websocket.server.ServerEndpoint;

/**
* @author Arun Gupta
*/
-@WebSocketEndpoint(value = "/websocket",
+@ServerEndpoint(value = "/websocket",
encoders = {FigureEncoder.class},
decoders = {FigureDecoder.class})
public class Whiteboard {

private static Set<Session> peers = Collections.synchronizedSet(new HashSet<Session>());

- @WebSocketOpen
+ @OnOpen
public void onOpen(Session peer) {
peers.add(peer);
}

- @WebSocketClose
+ @OnClose
public void onClose(Session peer) {
peers.remove(peer);
}

- @WebSocketMessage
+ @OnMessage
public void boradcastFigure(Figure figure, Session session) throws IOException, EncodeException {
System.out.println("boradcastFigure: " + figure);
for (Session peer : peers) {
if (!peer.equals(session)) {
- peer.getRemote().sendObject(figure);
+ peer.getBasicRemote().sendObject(figure);
}
}
}

- @WebSocketMessage
+ @OnMessage
public void broadcastSnapshot(ByteBuffer data, Session session) throws IOException {
System.out.println("broadcastBinary: " + data);
for (Session peer : peers) {
if (!peer.equals(session)) {
- peer.getRemote().sendBytes(data);
+ peer.getBasicRemote().sendBinary(data);
}
}
}

The updated source code for the sample is available here and runs on GlassFish b78.

Hopefully this gives you a good idea on how to migrate your WebSocket applications from the older to the newer API. The changes are still evolving and the specification (PDF) and javadocs (ZIP) are the definitive source for the latest information. The complete set of latest spec downloads are available here.

As always, send the spec feedback to users@websocket-spec.java.net and implementation feedback to users@tyrus.java.net.

Tuesday Feb 26, 2013

WebSocket Java Client API in GlassFish 4 (TOTD #208)


The WebSocket API by W3C defines an API that enables Web pages to use the WebSocket protocol for two-way communication with a remote host. Java API for WebSocket (JSR 356) has added an ability to invoke a WebSocket endpoint from a Java application. This Tip Of The Day (TOTD) will explain how you can use that API for invoking a WebSocket endpoint.

JSR 356 introduces a class-level annotation @WebSocketClient that converts a POJO into a WebSocket client. The usual lifecycle annotations such as @WebSocketOpen, @WebSocketClose, @WebSocketError, and @WebSocketMessage can be specified on methods.

@WebSocketClient
public class MyClientEndpoint {
@WebSocketOpen
public void onOpen(Session session) {
System.out.println("Connected to endpoint: " + session.getRemote());
try {
String name = "Duke";
System.out.println("Sending message to endpoint: " + name);
session.getRemote().sendString(name);
} catch (IOException ex) {
Logger.getLogger(MyClientEndpoint.class.getName()).log(Level.SEVERE, null, ex);
}
}

@WebSocketMessage
public void processMessage(String message) {
System.out.println("Received message in client: " + message);
Client.messageLatch.countDown();
}
}
This code sends a message to the connected WebSocket endpoint from the onOpen method and prints the message when it comes back. This endpoint is bootstrapped using a separate class:

public class Client {

final static CountDownLatch messageLatch = new CountDownLatch(1);

public static void main(String[] args) {
try {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
String uri = "ws://echo.websocket.org:80/";
System.out.println("Connecting to " + uri);
container.connectToServer(MyClientEndpoint.class, URI.create(uri));
messageLatch.await(100, TimeUnit.SECONDS);
} catch (DeploymentException | InterruptedException ex) {
Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
Note that a CountDownLatch is used to ensure that there is enough time for the client to connect to the endpoint. The latch counts down in MyClientEndpoint.processMessage once the echo message is received from the endpoint.

The source code can be downloaded here, connects to a remote WebSocket endpoint, and displays the output on the console.

The Maven dependency for now are specified as:
<dependencies>
<dependency>
<groupId>org.glassfish.tyrus</groupId>
<artifactId>tyrus-client</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.glassfish.tyrus</groupId>
<artifactId>tyrus-container-grizzly</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>

Hopefully these could be specified as javax dependencies only!

The latest specification draft and javadocs can be downloaded here. Share your feedback at users@websocket-spec.java.net (for the specification) or users@tyrus.java.net (for the implenentation).

Monday Feb 25, 2013

Chat Sever using WebSocket in GlassFish 4 (TOTD #207)


A canonical example of using WebSocket is a chat server. This Tip Of The Day (TOTD) will share the code for building exactly that!

The complete code in this TOTD is available here.

Here is the code for the Chat Server built as a WebSocket endpoint:

@WebSocketEndpoint("/websocket")
public class ChatEndpoint {
private static final Set<Session> peers = Collections.synchronizedSet(new HashSet<Session>());

@WebSocketOpen
public void onOpen(Session peer) {
peers.add(peer);
}

@WebSocketClose
public void onClose(Session peer) {
peers.remove(peer);
}

@WebSocketMessage
public void message(String message, Session client) throws IOException, EncodeException {
for (Session peer : peers) {
peer.getRemote().sendObject(message);
}
}
}

In this code:
  • Lifecycle callbacks manage the coordinates of the connecting clients
  • message method receives all the messages sent by the client and then transmits to all the connected clients.
Simple, isn't it ?

You can use a trivial JavaScript to exchange messages between two browsers. A chat session between two Chrome and Firefox is shown below:



Download the source code and run it in GlassFish 4 b77.

Tuesday Feb 19, 2013

JsonParser and JsonReader - Streaming and Object Readers in Java API for JSON Processing (TOTD #206)


JsonReader reads a JSON object or array from an input source. This provides DOM access to the JSON structure.

JsonParser provides forward, ready-only access to JSON data in a streaming way. The parser can be created from InputStream and Reader. A sample code looks like:

JsonParser jsonParser = Json.createParser(new StringReader(YOUR_STRING_HERE));

Here is a table with code fragments for parsing some common JSON using Object Model and events generated from Streaming API:

JSON Object Model API Events from Streaming API
{ }
JsonReader jsonReader =
    new JsonReader(new StringReader("{}"));
JsonObject json = jsonReader.readObject();
{START_OBJECT }END_OBJECT
{
  "apple":"red",
  "banana":"yellow"
}
jsonReader =
    new JsonReader(new StringReader(...));
json = jsonReader.readObject();

{START_OBJECT
  "apple"KEY_NAME:"red"VALUE_STRING,
  "banana"KEY_NAME:"yellow"VALUE_STRING
}
[
  { "apple":"red" },
  { "banana":"yellow" }
]
jsonReader =
    new JsonReader(new StringReader(...));
JsonArray jsonArr = jsonReader.readArray();

[START_ARRAY
  {START_OBJECT "apple"KEY_NAME:"red"VALUE_STRING }END_OBJECT,
  {START_OBJECT "banana"KEY_NAME:"yellow"VALUE_STRING }END_OBJECT
]END_ARRAY
{
  "title":"The Matrix",
  "year":1999,
  "cast":[
    "Keanu Reaves",
    "Laurence Fishburne",
    "Carrie-Anne Moss"
  ]
}
jsonReader =
    new JsonReader(new StringReader(...));
json = jsonReader.readObject();

{START_OBJECT
  "title"KEY_NAME:"The Matrix"VALUE_STRING,
  "year"KEY_NAME:1999VALUE_NUMBER,
  "cast"KEY_NAME:[START_ARRAY
    "Keanu Reaves"VALUE_STRING,
    "Laurence Fishburne"VALUE_STRING,
    "Carrie-Anne Moss"VALUE_STRING
  ]END_ARRAY
}END_OBJECT

The source code for this sample can be downloaded here, try it with GlassFish 4 b76 or later.

Note, GlassFish b76 has Public Review version of the APIs. But the APIs have evolved since then and an integration is planned in the next few days. So if you are using an older build, the sample code may not work. I'll update the sample code once the bits are integrated.

Here are some more links for you to read further:

Monday Feb 18, 2013

JsonGenerator and JsonObjectBuilder - Streaming and Object API in Java API for JSON Processing (TOTD #205)


Java API for JSON Processing (JSR 353) cleared Public Review unanimously and is on its way to to standardization. There is still Proposed Final Draft and the final vote to come. As per the Java EE 7 schedule, the specification will be final on 4/15/2013. The implementation is already integrated in GlassFish 4 builds.

The API provides an Object Model (like DOM for XML) and Streaming API (like StAX for XML) to parse and generate JSON structure. Here is a table that provide code fragments for generating some common JSON:

JSON Object Model API Streaming API
{ }
JsonObject jsonObject =
     new JsonObjectBuilder().build();

new JsonWriter(System.out)
     .writeObject(jsonObject);

JsonGeneratorFactory factory =
     Json.createGeneratorFactory();

JsonGenerator gen =
     factory.createGenerator(System.out);

gen.writeStartObject().writeEnd();
{
  "apple":"red",
  "banana":"yellow"
}
new JsonObjectBuilder()
  .add("apple", "red")
  .add("banana", "yellow")
.build();
gen.writeStartObject()
     .write("apple", "red")
     .write("banana", "yellow")
   .writeEnd();
[
  { "apple":"red" },
  { "banana":"yellow" }
]
JsonArray jsonArray = new JsonArrayBuilder()
  .add(new JsonObjectBuilder()
          .add("apple","red"))

  .add(new JsonObjectBuilder()
          .add("banana","yellow"))

  .build();
gen.writeStartArray()
     .writeStartObject()
       .write("apple", "red")
     .writeEnd()
     .writeStartObject()
       .write("banana", "yellow")
     .writeEnd()
.writeEnd();
{
  "title":"The Matrix",
  "year":1999,
  "cast":[
    "Keanu Reaves",
    "Laurence Fishburne",
    "Carrie-Anne Moss"
  ]
}
new new JsonObjectBuilder()
  .add("title", "The Matrix")
  .add("year", 1999)
  .add("cast", new JsonArrayBuilder()
  .add("Keanu Reaves")
  .add("Laurence Fishburne")
  .add("Carrie-Anne Moss"))
.build();
gen.writeStartObject()
     .write("title", "The Matrix")
     .write("year", 1999)
     .writeStartArray("cast")
       .write("Keanu Reaves")
       .write("Laurence Fishburne")
       .write("Carrie-Anne Moss")
     .writeEnd()
   .writeEnd();

The source code for this sample can be downloaded here, try it with GlassFish 4 b76 or later.

Here are some more links for you to read further:

Wednesday Feb 13, 2013

@WebSocketClient in GlassFish 4 (TOTD #204)


Tyrus is the Reference Implementation for Java API for WebSocket (JSR 356) and is already integrated in GlassFish 4. The implementation is rapidly evolving and and adding support for different features already defined in the latest version of the specification.

The latest integration in GlassFish added support for creating Java-based WebSocket clients.

Any POJO can be made a WebSocket client by adding @WebSocketClient. A inbound message is received in a method annotated with @WebSocketMessage. Any method can be designated as lifecycle method by specifying @WebSocketOpen, @WebSocketClose, @WebSocketError. Here is a simple sample:

@WebSocketClient
public class MyClient {
@WebSocketOpen
public void onOpen(Session session) {
System.out.println("Connected to endpoint: " + session.getRemote());
try {
session.getRemote().sendString("Duke");
} catch (IOException ex) {
Logger.getLogger(MyClient.class.getName()).log(Level.SEVERE, null, ex);
}
}

@WebSocketMessage
public void processMessage(String message, Session session) {
System.out.println(getClass().getName() + ": " + message);
}
}

In this code, a message is sent to the connected WebSocket endpoint whenever the connection is initiated. An inbound message (coming from the server) is processed using processMessage method.

The endpoint code is pretty trivial:

@WebSocketEndpoint(value="/websocket", configuration=DefaultServerConfiguration.class)
public class MyEndpoint {

@WebSocketMessage
public String sayHello(String name) {
System.out.println(getClass().getName() + ": " + name);
return "Hello " + name;
}

}
The endpoint has a single method that receives the payload, appends a string and returns back as the response. Even though server is returning the response right away, the client views this as two separate messages, one outbound and the other one inbound. So any message-exchange-pattern such as request/response need to be specifically built using the application code. The configuration attribute, in this case, is optional and will be fixed in a subsequent release of Tyrus.

The client can bootstrap using Java SE or Java EE components as:

WebSocketContainer container = ContainerProvider.getWebSocketContainer();
String uri = "ws://localhost:8080" + request.getContextPath() + "/websocket";
container.connectToServer(MyClient.class, URI.create(uri));
The complete source code used in this Tip Of The Day (TOTD) is available here. It will run on the latest checked out version of GlassFish workspace, not a promoted build yet (bleeding edge eh ;-)

Please provide feedback on users@tyrus.java.net.

How are you going to use Java-based WebSocket client in your applications ?

Here are other Java EE 7 entries published on this blog so far:


About

profile image
Arun Gupta is a technology enthusiast, a passionate runner, author, and a community guy who works for Oracle Corp.


Java EE 7 Samples

Stay Connected

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today