Sunday, January 17, 2016

Java API for Web Sockets - Client Server Tutorial using Oracle Weblogic Server

Hi folks ,

This exercise will help you to understand Java Web Sockets API and write a sample client server code to create , connect to and send full duplex messages to a Web Socket .

Pre requisites for this java exercise :

1. Install Oracle Weblogic Server 12.1.3 only  since this exercise has been done with WLS 12.1.3. Do not try this exercise with Oracle WLS 12.1.2 since the API's are only supported for 12.1.3.
2. Create a domain on the WLS and a managed_server to deploy the Web Socket web application.
3. Your admin server , managed server should be up and running .

If the above are not done , go back and do the needful .

Let's briefly understand Web Sockets

Understanding the WebSocket Protocol


WebSocket is an application protocol that provides simultaneous two-way communication over a single TCP connection between a client and a server. The WebSocket protocol enables the client and the server to send data independently. The WebSocket Protocol is supported by most browsers. A browser that supports the WebSocket protocol provides a JavaScript API to connect to endpoints, send messages, and assign callback methods for WebSocket events (such as opened connections, received messages, and closed connections).
For general information about the WebSocket Protocol, see  this .

Why WebSocket ???

Obviously this question why go for WebSocket when the request-response model of HTTP is available . Let's see below .

In the traditional request-response model used in HTTP, the client requests resources and the server provides responses. The exchange is always initiated by the client; the server cannot send any data without the client requesting it first. This model worked well for the World Wide Web when clients made occasional requests for documents that changed infrequently, but the limitations of this approach are increasingly apparent as content changes quickly and users expect a more interactive experience on the web.

The WebSocket protocol addresses these limitations by providing a full-duplex communication channel between the client and the server. Combined with other client technologies, such as JavaScript and HTML5, WebSocket enables web applications to deliver a richer user experience.

Let's Begin Hands On :


Now I won't waste much of your time in theory and since this is a crash course let's quickly move on to some coding . I will be using WebLogic Server WebSocket implementation in this tutorial and will be explaining the parts of code in place .

There are three parts of this exercise (I) write a WebSocket server application , (ii) write a javascript WebSocket client to connect to (I) and (iii) write a Java WebSocket client to connect to (I) .

WebSocket Server code


Create a dynamic Web Project in Eclipse and give it a nice name e.g. wsServer , create a package WebSocket .server and add a class to the package Serverex1.java . I will be explaining this piece of code in comments section.

Required :  javax.websocket-api-1.1.jar , can be downloaded and be placed in lib directory in WEB_INF of your project or provided as a maven dependency in your pom.xml. Click  here .
 
package websocket.server;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import javax.websocket.CloseReason;
import javax.websocket.EndpointConfig;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

// Create an Annotated Endpoint , mark the annotation ServerEndPoint ; Creating an  
// annotated endpoint enables you to handle life cycle events for a WebSocket connection
// by annotating methods of the endpoint class.  An annotated endpoint is deployed
// automatically with the application.This annotation denotes that the class represents
// a WebSocket server endpoint. Set the value element of the ServerEndpoint annotation to
// the relative path to which the endpoint is to be deployed. The path must begin with a
//  forward slash (/) .
 
 
@ServerEndpoint("/echo")
public class Serverex1 {

// queue holds all the sessions connected to this WebSocket . A Session object represents  a
// conversation between a pair of WebSocket endpoints .

private static Queue<Session> queue = new ConcurrentLinkedQueue<Session>();

// this static block runs a new Thread and send  a message to all the clients connected to this
// WebSocket .
 
static {
    new Thread(new Runnable() {
       public void run() {
       DecimalFormat df = new DecimalFormat("#.####");
           while (true) {
               double d = 2 + Math.random();
               if (queue != null) {
                    sendAll("USD Rate : " + df.format(d));
               }
               try {
                  Thread.currentThread().sleep(5000);
               } catch (InterruptedException e) {
                    e.printStackTrace();
               }
            }
        }

        private void sendAll(String string) {
           ArrayList<Session> closedSessions = new ArrayList<>();
           for (Session session : queue) {
              if (!session.isOpen()) {
                 System.out.println("Session " + session.getId()
                   + " is closed");
                 closedSessions.add(session);
              } else {
                   try {
 
                 // The Session parameter represents a conversation between this 
                                         //  endpoint and the remote  endpoint and the getBasicRemote
                                         // method returns an object that represents the remote
                 // endpoint.

                        session.getBasicRemote().sendText(string);
                   } catch (IOException e) {
                       e.printStackTrace();
                   }
               }
               queue.removeAll(closedSessions);
               System.out.println("Sending Message " + string
                  + " to "  + queue.size() + " clients");
           }
       }
    }).start();
}

public Serverex1() {}

// Annotate the method with OnMessage to denote the lifecycle method
// in a WebSocket endpoint; the OnMessage method can take three
// types of message type text , binary and pong (pong is a response
// message frame for ping)

@OnMessage
public void message(Session session, String str) {
    try {
       // When a remote client , the ClientEndPoint will send a
       // message to this WebSocket , this method will be invoked
       // upon the receival of message the following logic will be
       // invoked , here the same text is sent back to the
       // clientEndPoint and also to each of the opent       
       // ClientEndPoints

       session.getBasicRemote().sendText(str);
       for (Session sess : session.getOpenSessions()) {
          sess.getBasicRemote().sendText(str);
       }
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    System.out.println("Received Message " + str + " from Client ");
}

// To handle a connection opened event, annotate the method for handling the event with the
// OnOpen annotation.

@OnOpen
public void open(Session session, EndpointConfig config) {
   queue.add(session);
   System.out.println("New Session Opened " + session.getId());
   System.out.println(config.getClass());
}

// You need handle a connection closed event only if you require
// some special processing before the connection is closed, for
// example, retrieving session attributes such as the ID, or any
// application data that the session holds before the data becomes

// unavailable after the connection is closed.To handle a connection

// closed event, annotate the method for handling the event with the

// OnClose annotation.

@OnClose
public void close(Session session, CloseReason reason) {
   queue.remove(session);
   System.out.println("Session closed " + session.getId());
   System.out.println(reason.getReasonPhrase());
}

// You need handle only error events that are not modeled in the
// WebSocket protocol, for example:

  • // Connection problems
  • // Runtime errors from message handlers
  • // Conversion errors in the decoding of messages
// To handle an error event, annotate the method for handling the
// event with the OnError annotation.


@OnError
public void error(Session session, Throwable t) {
   queue.remove(session);
   System.out.println("Error on session " + session.getId());  
}
}

// Create a war out of the above class file , do not include a deployment descriptor .xml file since the
// annotations will take care of everything . Deploy the war as a web application on weblogic
// managed server , I am doing it on my WLS server managed_server-1 at port 7002 . Once the war is
// deployed , you are ready to write clients to connect to this WebSocket.

JavaScript WebSocket Client


A browser-based WebSocket client application is typically a composite of HTML5 technologies, including HTML markup, CSS3, and JavaScript that makes use of the WebSocket JavaScript API. Most browsers support the W3C WebSocket API that can be used to create and work with the WebSocket protocol.

This API provides an implementation of the standard W3C WebSocket API. The API also provides a mechanism for using an alternative transport for WebSocket messaging when the WebSocket protocol is not supported

index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>WebSocket Client</title>
    <script type="text/javascript">
        var wsocket;
        function connect() {
           <!-- The client opens a WebSocket connection to the-->   

           <!-- server hosting the WebSocket endpoint, using the-->
           <!-- ws: // or ws:/// protocol -->
           wsocket =
               new WebSocket("ws://localhost:7002/wsserver/echo");
               wsocket.onmessage = onMessage;
        }


        <!--The client registers listeners with the WebSocket -->   

        <!--object to respond to events, such as opening, -->
        <!--closing and receiving messages. Based on the event -->
        <!-- and the information received, the client performs -->
        <!-- appropriate action. -->

        function onMessage(evt) {
           document.getElementById("rate").innerHTML = evt.data;
        }


        <!-- The client sends messages to the server over the-->

        <!--WebSocket object as needed by the application.-->

        function sendMsg() {
            <!--Check if connection is open before sending-->

            if (wsocket == null || wsocket.readyState != 1) {
               document.getElementById("reason").innerHTML =

                 "Not connected can't send msg"
            } else {
               wsocket.send(document.getElementById("name").value);
            }
        }
        window.addEventListener("load", connect, false);
    </script>
</head>
<body>
    <table>
        <tr>
            <td>
                <label id="rateLbl">Current Rate:</label>
            </td>
            <td>
                <label id="rate">0</label>
            </td>
        </tr>
    </table>

    Enter text to send to Server
    <input type="text" id="name"/>
    <input id="send_button" class="button" type="button" value="send" onclick="sendMsg()" />
</body>
</html>
 

Java WebSocket client


The javax.websocket package contains annotations, classes, interfaces, and exceptions that are common to client and server endpoints. Use the APIs in this package for writing a Java WebSocket client in the same way as for writing a server.

To Connecting a Java WebSocket Client to a Server Endpoint

Let's  see how to do this in Code

Create a Java Project in Eclipse and give it a name e.g. wsClient , create a package webSocket.client and add a class to the package Clientex1.java .

package websocket.client;
import java.io.IOException;
import java.net.URI;
import javax.websocket.ClientEndpoint;
import javax.websocket.ClientEndpointConfig;
import javax.websocket.ContainerProvider;
import javax.websocket.DeploymentException;
import javax.websocket.OnMessage;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;

// This annotation denotes that the class represents a WebSocket client endpoint.

@ClientEndpoint
public class Clientex1 {

   private static Object waitLock = new Object();

   @OnMessage
   public void onMessage(String string) {
     System.out.println("Received Message " + string);
   }

   public static void main(String[] args) {
       WebSocketContainer wsc = null;
       Session session = null;
       // 1.Invoke the
       // javax.websocket.ContainerProvider.getWebSocketContainer()
       // static method to obtain the client's            
       // javax.websocket.WebSocketContainer
       // instance.

       wsc = ContainerProvider.getWebSocketContainer();
       try {
           // 2.Invoke the overloaded connectToServer method on the
           // WebSocketContainer object that you obtained in the
           // previous step.In the invocation of the connectToServer
           // method, provide the following information as params
           // to the method:
                ◦// The client WebSocket endpoint
                ◦// The complete path to the server WebSocket     
                 // endpoint

           session = wsc.connectToServer(Clientex1.class,
           URI.create("ws://localhost:7002/wsserver/echo"));
           session.getBasicRemote().sendText("SENT THIS MESSAGE");
       } catch (DeploymentException | IOException e) {
          e.printStackTrace();
       } finally {
           if (session != null) {
              try {
                 session.close();
              } catch (IOException e) {
                  e.printStackTrace();
              }
           }
       }
    }
}

Sample Output :  

Server logs while calling from index.html
New Session Opened e81cb8ee-ced5-4d59-970a-d4d554a50541
class javax.websocket.server.DefaultServerEndpointConfig
Sending Message USD Rate : 2.0751 to 1 clients
Sending Message USD Rate : 2.9263 to 1 clients
Sending Message USD Rate : 2.303 to 1 clients

Output on .html 

Current Rate:
USD Rate : 2.3716

Output of java Client

Received Message SENT THIS MESSAGE
Received Message SENT THIS MESSAGE
Received Message USD Rate : 2.4786

Output on Server Logs :

New Session Opened fc560861-cebc-4499-839b-0f4d41a9bf6b
class javax.websocket.server.DefaultServerEndpointConfig
Received Message SENT THIS MESSAGE from Client
Sending Message USD Rate : 2.4786 to 2 clients
Sending Message USD Rate : 2.4786 to 2 clients
Sending Message USD Rate : 2.2093 to 2 clients

Hope You enjoyed it and learned something new today .

Happy Coding 

Friday, January 15, 2016

JMS tutorial - using Oracle WLS_Server as a JMS Provider

Hi folks , writing after a very long time , this is my first blog in 2016 .
Today I am going to share  how to run a sample program to send and receive messages to a JMS Queue .

So first lets brief you about JMS .

What Is Messaging?


Messaging is a method of communication between software components or applications.  A messaging client can send messages to, and receive messages from, any other client. Each client connects to a messaging agent that provides facilities for creating, sending, receiving, and reading messages.

What Is the JMS API?


The Java Message Service is a Java API that allows applications to create, send, receive, and read messages. Designed by Sun and several partner companies, the JMS API defines a common set of interfaces and associated semantics that allow programs written in the Java programming language to communicate with other messaging implementations.

For more details on JMS specification read here .

Pre requisites for this java exercise :


1. Install Oracle Weblogic Server 12.1.3 is preferred since this exercise has been done with WLS 12.1.3.
2. Create a domain on the WLS and a managed_server to deploy applications , target the JMS modules .
3. Your admin server , managed server should be up and running .

Configuration 


1. Create a JMS Server :

A JMS server acts as a management container for resources within JMS modules. Some of its responsibilities include the maintenance of persistence and state of messages and subscribers. A JMS server is required in order to create a JMS module.    




-> Services > Messaging > JMS Servers
Select New
Name  : testJMSServer
Persistent Store: (none)  -> Next
Target : managed_server-1 [The managed Server created and configured in your Oracle WLS_Server]
Finish





-------------------------------------------------------------------------------------------------------------------------


The JMS server should now be visible in the list with Health OK.    


2. Create a JMS Module

A JMS module is a definition which contains JMS resources such as queues and topics. A JMS module is required in order to create a JMS queue.    

-> Services > Messaging > JMS Modules -> New         
Name: testJMSModule  ->  Leave the other options empty  -> Next 
-> Targets: managed_server- 1  (or choose the same one as the JMS server)     
-> Leave “Would you like to add resources to this JMS system module” unchecked and  press Finish
 
3. Create a Connection Factory       
 
A connection factory is a resource that enables JMS clients to create connections to JMS destinations.    
 
-> Services > Messaging > JMS Modules
-> select testJMSModule and click New
-> Select Connection factory -> Next
-> Name: testConnectionFactory , JNDI Name : jms/testcf
-> Leave the other values at default
->  Advanced Targeting -> Subdeployments -> Click Create a new Sub deployment
A subdeployment is a mechanism by which JMS resources are grouped and targeted to a server instance or a cluster . 
Subdeployment Name : testJMSSubdeployment -> OK 
On Tagets -> check managed_server-1 and testJMSServer or the corresponding names you have used in this exercise.
 
 
4. Create a JMS Queue      
 
A JMS queue (as opposed to a JMS topic) is a point-to-point destination type. A message is written to a specific queue or received from a specific queue.
 
-> Services > Messaging > JMS Modules
-> select testJMSModule and click New
-> Select Queue -> Next
-> Name: testQueue , JNDI Name : jms/testq
-> Template : None 
->  Subdeployments -> testJMSSubdeployment -> OK 
On Tagets -> check testJMSServer.
Finish
 
You should see the following resources under testJMSModule under Services -> Messaging ->  JMS Modules
 

 
The JMS queue is now complete and can be accessed using the JNDI names
jms/testcf
jms/testq
 

If you hit  exception  :
 
java.lang.ClassCastException: weblogic.management.configuration.ServerMBeanImpl cannot be cast to weblogic.management.configuration.JMSServerMBean

Workaround :

- create jms queue without subdeployment specified and save
- edit queue and then set subdeployment then save
 
Now we have finished all the configuration and our JMSModule on the WLS_SERVER is ready to accept messages from a client and to send back to another client .

5. Let's come to coding part .

I am showing the simplest way possible to do this exercise hence I will be creating a Java Project TestJMS , create a package named   jms.test  in a src directory , create a folder in this project called lib  .

Now under your Oracle_Home which would look like this C:\OracleHome may be different for you ; this is the root directory from where you go and run your weblogic scripts etc.

-> Under OracleHome -> wlsserver -> modules -> copy all jars(*.jar) and paste in under the lib , also OracleHome -> wlsserver -> server -> lib -> copy all jars(*.jar) and paste in under the lib  .

Create three class files under this package MyListener.java , MyReceiver.java and MySender.java

The following is the source code and I am explaining the code in the comments section in the code itself , have a look .

/**
 * This example shows how to establish a connection and send messages to the JMS
 * queue. The classes in this package operate on the same JMS queue. Run the
 * classes together to see the messages being sent and received.
 * @author Anmol Deep
 */

MySender.java

package jms.test;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Hashtable;
import java.util.Properties;

import javax.jms.JMSException;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueSender;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

 
/**
 * The class is used to send messages to the queue.
 */
public class MySender {
 // Defines the JNDI context factory.
 public final static String JNDI_FACTORY = "weblogic.jndi.WLInitialContextFactory";

 // Defines the Connection Factory's JNDI name
 public final static String JMS_FACTORY = "jms/testcf";

 // Defines the queue's JNDI name
 public final static String QUEUE = "jms/testq";

 private QueueConnectionFactory qconFactory;
 private QueueConnection qcon;
 private QueueSession qsession;
 private QueueSender qsender;
 private Queue queue;
 private TextMessage msg;


 public void init(Context ctx, String queueName) throws NamingException,
   JMSException {

         // Create the QueueConnectioFactory by lookup JNDI name
         qconFactory = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY);

         // Create the Queue Connection
         qcon = qconFactory.createQueueConnection();

         // Create the QueueSession
         qsession = qcon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);

         // Create the Queue object by lookup JNDI name
         queue = (Queue) ctx.lookup(queueName);

         // Create the QueueSender object
         qsender = qsession.createSender(queue);

         // Create the TextMessage Object
         msg = qsession.createTextMessage();

         // Start the Queue Connection
         qcon.start();
 }

 
 /**
  * Sends a message to a JMS queue.
  *
  * @param message
  *            message to be sent
  * @exception JMSException
  *                if JMS fails to send message due to internal error
  */
 public void send(String message) throws JMSException {
      msg.setText(message);
      qsender.send(msg);
 }

 
 /**
  * Closes JMS objects.
  *
  * @exception JMSException
  *                if JMS fails to close objects due to internal error
  */
 public void close() throws JMSException {
      qsender.close();
      qsession.close();
      qcon.close();
 }

 
 /**
  * main() method.
  *
  * @exception Exception
  *                if operation fails
  */
 public static void main(String[] args) throws Exception {
      Properties parm = new Properties();
      parm.setProperty(Context.INITIAL_CONTEXT_FACTORY,
        "weblogic.jndi.WLInitialContextFactory");

      // weblogic managed server URL
      parm.setProperty(Context.PROVIDER_URL, "t3://localhost:7002");
      parm.setProperty("java.naming.security.principal", "weblogicUserName");
      parm.setProperty("java.naming.security.credentials", "weblogicPassword");

  
      Context ctx = new InitialContext(parm);
      ctx.lookup(QUEUE);
      MySender qs = new MySender();
      qs.init(ctx, QUEUE);
      readAndSend(qs);
      qs.close();
 }

 private static void readAndSend(MySender mySender) throws IOException,
   JMSException {
      BufferedReader msgStream = new BufferedReader(new InputStreamReader(
        System.in));
      String text= null;
      boolean quitting = false;
      do {
      System.out.print("Enter message (\"quit\" to quit): \n");
      text= msgStream.readLine();
      if (text!= null && text.trim().length() != 0) {
       mySender.send(text);
       System.out.println("JMS Message Sent: " + text+ "\n");
       quitting= text.equalsIgnoreCase("quit");
      }
     } while (!quitting);
    }
  }


A MessageListener object is used to receive asynchronously delivered messages. 

For more info.  read  this

MyListener.java

package jms.test;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
public class MyListener implements MessageListener {

public void onMessage(Message m) {
      try{
          TextMessage textMessage=(TextMessage)m;
          System.out.println("Following Message Receieved :"+textMessage.getText());
      }catch(JMSException e){

          System.out.println(e);
          e.printStackTrace();}
     }
}


 
MyReceiver.java

package jms.test;
import java.util.Properties;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueReceiver;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.naming.Context;
import javax.naming.InitialContext;

public class MyReceiver {
public static void main(String[] args) {
      try {

           Properties parm = new Properties();
           parm.setProperty(Context.INITIAL_CONTEXT_FACTORY,
             "weblogic.jndi.WLInitialContextFactory");
           parm.setProperty(Context.PROVIDER_URL, "t3://localhost:7002");
           parm.setProperty("java.naming.security.principal", "weblogicUserName");
           parm.setProperty("java.naming.security.credentials", "weblogicPassword");

           Context ctx = new InitialContext(parm);
 
           QueueConnectionFactory qconF= (QueueConnectionFactory) ctx
                 .lookup("jms/testcf");
           QueueConnection qCon = qconF.createQueueConnection();
           qCon.start();

           QueueSession qSess = qCon.createQueueSession(false,
     Session.AUTO_ACKNOWLEDGE);

     Queue queue = (Queue) ctx.lookup("jms/testq");
   

   QueueReceiver qReceiver = qSess.createReceiver(queue);
   // create listener object
   MyListener listener = new MyListener();

   // register the listener object with receiver
   qReceiver.setMessageListener(listener);

   System.out.println("Receiver is ready, waiting for messages.");
   System.out.println("press Ctrl+c to shutdown...");
   while (true) {
    Thread.sleep(1000);
   }
  } catch (Exception e) {
   e.printStackTrace();
   System.out.println(e);
  }
 }

}
Run the MySender class
Run the MyReciever class
On Weblogic  Under JMSModules - > testJMSModule - > testQueue ->  Monitoring tab ->  Show Messages

Sample Output :

MySender

Enter message ("quit" to quit):
This is a sample program to Send messages to a JMS Queue
JMS Message Sent: This is a sample program to Send messages to a JMS Queue

Enter message ("quit" to quit):

This is the second Message Sent 

JMS Message Sent: This is the second Message Sent

 

Enter message ("quit" to quit):

quit
JMS Message Sent: quit

MyReceiver


Receiver is ready, waiting for messages.
press Ctrl+c to shutdown...
Following Message Receieved :This is a sample program to Send messages to a JMS Queue
Following Message Receieved :This is the second Message Sent
Following Message Receieved :"quit"
Following Message Receieved :quit

End of Exercise
Happy Coding .