Java EE 7 WebSocket Client Sample Application with JavaFX

2012年12月22日 at 11:56 午後 2件のコメント

At the previous entry, I wrote how to WebSocket application on Server Side. However JSR 356 Java API for WebSocket is providing Client API also. So in this entry, I will explain the WebSocket Client API with JavaFX.

The image of this application is like following video. There is one button on the bottom of application. If you click the button, the application connect to WebSocket Server and receive the messages and show the message on the TableView.




The example of running Application

Preparation :
In order to compile and run the application, you need following libraries.

* grizzly-framework-2.2.19.jar
* grizzly-http-2.2.19.jar
* grizzly-http-server-2.2.19.jar
* grizzly-websocket-2.2.19.jar
* tyrus-servlet-1.0-b08.jar

You can download the Grizzly related libraries from following site.

http://grizzly.java.net/nonav/docs/docbkx2.2/html/dependencies.html

* Grizzly is a popular NetWork Server framework which is wrote by Java NIO. And originally the grizzly was created for the server engine for GlassFish. It has high scalability and good performance. If you use the grizzly libraries, you don’t need to write the low level socket programing by Java.

In this client application, I will use the Grizzly to connect the WebSocket Server with less code.

Also, you need to get the jar file as tyrus-servlet (reference implementation of Java API for WebSocket) and it include the javax.net.websocket package. Please get the libraries from following site ?

http://repo1.maven.org/maven2/org/glassfish/tyrus/tyrus-servlet/1.0-b08/

Then I will start to create a NetBeans Project by selecting JavaFX FXML Application.


In this Project, I will specified the project name as “JavaFX-WebSocket”. After that in order to be able to use the downloaded libraries in the project,I configured and added the libraries to the project.

If the project had created successfully , following 3 classes will be created by NetBeans.
”JavaFXWebSocket.java”, ”Sample.fxml”, ”SampleController.java”.

I will try to update the above 3 classes as follows. At first , I will customize the view(FXML). If you already configured the SceneBuilder, you can show the SceneBuilder screen on your desktop after you click the Sample.fxml file on NetBeans Project.

This Sample Application is very simple. So I only change the size of the Window and added two component as Label and TableView. After drag and drop these component, I inserted the ID as “table” for TableView. And there is TableColumn inside of TableView. So I specified the ID as “column” for TableColumn.

If you change the screen, you may get the FXML code like follows.

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="453.0" xmlns:fx="http://javafx.com/fxml" fx:controller="javafx.websocket.SampleController">
  <children>
    <Button fx:id="button" layoutX="326.0" layoutY="365.0" onAction="#handleButtonAction" text="Start TimeLine" />
    <Label fx:id="label" layoutX="126.0" layoutY="120.0" minHeight="16.0" minWidth="69.0" />
    <Label layoutX="14.0" layoutY="14.0" prefWidth="292.0" text="WebScoket Twitter TimeLine Client Smaple" underline="true" />
    <TableView fx:id="table" layoutX="14.0" layoutY="45.0" prefHeight="311.0" prefWidth="425.0">
      <columns>
        <TableColumn id="" maxWidth="445.0" prefWidth="445.0" text="Message List from Twitter" fx:id="column" />
      </columns>
    </TableView>
  </children>
</AnchorPane>

After customize the View, I will start to implement the Controller of JavaFX. For JavaFXWebSocket.java , there is no need to modify in this application. So I will use the NetBeans created code.

package javafx.websocket;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class JavaFXWebSocket extends Application {
    
    @Override
    public void start(Stage stage) throws Exception {
        Parent root = FXMLLoader.load(getClass().getResource("Sample.fxml"));
        
        Scene scene = new Scene(root);
        
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

I implemented the SampleController.java as follows. At first , I inserted the “@FXML TableView table” and “@FXML TableColumn column”. The field name is the same id of FXML(fx:id=”table”, fx:id=”column” ). And I implements the action of push the button on handleButtonAction method. Twitter Streaming is long running process. So I need to implement the check program as multi thread. At JavaFX ,javafx.concurrent.Service, javafx.concurrent.Task classes is prepared to implement the Task. So I had used the Service class to do it.

package javafx.websocket;

import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ResourceBundle;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javax.websocket.ClientEndpointConfiguration;
import javax.websocket.DefaultClientConfiguration;
import javax.websocket.DeploymentException;
import org.glassfish.tyrus.client.ClientManager;

public class SampleController implements Initializable {

    @FXML
    private TableView table;

    @FXML
    private TableColumn<RowData,String> column;
    
    @FXML
    private void handleButtonAction(ActionEvent event) {
        TwitterCheckService thread = new TwitterCheckService(table);
        thread.start();
    }

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        table.setEditable(true);
        column.setResizable(true);
        column.setCellValueFactory(new PropertyValueFactory<RowData, String>("message"));
    }

    class TwitterCheckService extends Service {

        private TableView table;
        private CountDownLatch messageLatch = null;

        public TwitterCheckService(TableView table) {
            this.table = table;
        }

        @Override
        protected Task createTask() {
            Task<Void> task = new Task<Void>() {
                @Override
                protected Void call() throws Exception {
                    messageLatch = new CountDownLatch(1);
                    try {
                        URI clientURI = new URI("ws://localhost:8080/TwitterTimeLine/twitter");
//            ClientContainer cliContainer = ContainerProvider.getClientContainer();
                        ClientManager cliContainer = org.glassfish.tyrus.client.ClientManager.createClient();

                        ClientEndpointConfiguration clientConfig = new DefaultClientConfiguration();
                        cliContainer.connectToServer(new TwitterClient(table), clientURI);
                        messageLatch.await(1, TimeUnit.SECONDS);
                    } catch (DeploymentException | URISyntaxException | InterruptedException ex) {
                        Logger.getLogger(SampleController.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    return null;
                }
            };
            return task;
        }
    }
}

In the above class, there is initialize() method to initialize the class. In the method, I wrote column.setCellValueFactory(new PropertyValueFactory(“message”)) . This is the value configuration of the Table column. In fact, the field of “message” in RowData class will be showed on every column in the Table. I specified the javafx.scene.text.Text type instead of String type, because it was difficult to manage the size of String in the Column of TableView. So instead of String, I used the Text and Text#setWrappingWidth could manage the size of viewable String.

package javafx.websocket;

import javafx.scene.text.Text;

public class RowData {

    private Text message;

    public RowData(Text message) {
        this.message = message;
        message.setWrappingWidth(400);
    }

    public Text getMessage() {
        return message;
    }

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

I will pick up and explain the important point of WebSocket Client in SampleController class as follows. I wrote the comment in the code as “// ClientContainer cliContainer = ContainerProvider.getClientContainer();” In fact the above code is recommended as standard. And you should write the following property on the system property.

“webocket.clientcontainer.classname= actual class name ”

However in this programing , I faced the error. Thus I wrote the following in order to get the instance of ClientContainer. “ClientManager cliContainer = org.glassfish.tyrus.client.ClientManager.createClient();”

            URI clientURI = new URI("ws://localhost:8080/TwitterTimeLine/twitter");
//            ClientContainer cliContainer = ContainerProvider.getClientContainer();
            ClientManager cliContainer = org.glassfish.tyrus.client.ClientManager.createClient();

            ClientEndpointConfiguration clientConfig = new DefaultClientConfiguration();
            cliContainer.connectToServer(new TwitterClient(table), clientURI);
            messageLatch.await(20, TimeUnit.SECONDS);

I specified the “TwitterClient(table)” inside of cliContainer.connectToServer(new TwitterClient(table), clientURI) line. It is a WebSocket Client code which equal to the class of adding the @WebSocketEndpoint annotation at the server side. Instead of the “@WebSocketEndpoint” annotation, we must specify the “@WebSocketClient” annotation at the client side code. Following is the client side code of WebSocket client.

package javafx.websocket;

import javafx.collections.ObservableList;
import javafx.scene.control.TableView;
import javafx.scene.text.Text;
import javax.websocket.Session;
import javax.websocket.WebSocketClient;
import javax.websocket.WebSocketClose;
import javax.websocket.WebSocketMessage;
import javax.websocket.WebSocketOpen;

@WebSocketClient
public class TwitterClient {

    TableView table;
    ObservableList<RowData> list;

    public TwitterClient(TableView table) {
        this.table = table;
    }

    @WebSocketOpen
    public void onOpen(Session session) {
        System.out.println("Connection had opened.");
    }

    @WebSocketMessage
    public void onMessage(String message) {
        if (message.length() == 0) {
            return;
        }
        // In order to adjst the size of String in Table, I used Text.
        Text text = new Text(message);

        list = table.getItems();
        list.add(0,new RowData(text));
    }

    @WebSocketClose
    public void closeConnection(Session session) {
        System.out.println("Connection had closed.");
    }
}

Actually the code is very similar to the server side code. Instead of the @WebSocetEndpoint, I specified the @WebSocketClient annotation. And inside of the class, we can implements the method which is added @WebSocketOpen, @WebSocketMessage and @WebSocketClose annotation. In this program, client receive the message from the server. And there is no need to send the data to server. So I implemented the @WebSocketMessage public void onMessage(String message) method. In this method, the client receive the message from server and wrapping the String to Text object in order to adjust the length of the viewable String (Text text = new Text(message)). Finally the message is inserted into the first line of the Table.

This WebSocket client code is very easy to implement. So you can write the WebSocket program very easily not only server side but also Java Application like JavaFX.

Entry filed under: Java.

Java EE 7 WebSocket Server-Side Programing with Twitter4J WebLogic Server 12c Forum 2013 開催

2件のコメント


Java Champion & Evangelist

Translate

ご注意

このエントリは個人の見解であり、所属する会社の公式見解ではありません

カレンダー

2012年12月
 12
3456789
10111213141516
17181920212223
24252627282930
31  

カテゴリー

clustermap

ブログ統計情報

  • 1,289,158 hits

Feeds

アーカイブ