Статьи

Сервер HTML 5 отправил события на Glassfish 4

SSE (Server Sent Events) — это технология Web Pushing, разработанная по технологии HTML 5. Итак, что толкает?

Нажимать

Это передача наборов данных, которые отправляются через регулярные промежутки времени или в любое время через серверное приложение, в направлении сервера — — -> браузер, без необходимости каких-либо запросов веб-браузера. Обслуживание  Twitter  обновления в настоящее время на веб — странице через регулярные промежутки времени, появление  Facebook  акций на экране , когда новый ресурс доступен, обслуживая мгновенные  финансовые  данные (обменный курс доллара, четности и т.д.) непосредственно на экран пользователей могут быть приведены в качестве примеров для  толкая  scenerio.

События, отправленные сервером  , не являются уникальной техникой, используемой для добавления ресурса http, в предыдущей  статье мы упоминали, как обеспечить передачу данных в веб-браузер с помощью техники LongPolling. Я думаю, что эта последовательность о технологиях Push не будет неправильной, как правило, в этом порядке;

Опрос  ->  LongPolling  ->  События ServerSent  ->  WebSocket

ServerSent Events — это технология, развитие которой уже продолжается, например технология WebSocket. По этой причине я хотел бы отметить, что он не используется в каждом веб-браузере, и это наверняка поддерживается веб-браузерами нового поколения.

Технология SSE имеет несколько дополнительных функций, в отличие от других, таких как автоматическое восстановление соединения при потере соединения, маршрутизация сообщения определенной функции в передачах событий / ресурсов. Например, вы можете взглянуть на обсуждение сравнения SSE и WebSocket из записи в  StackOverFlow .

Как работает SSE?

Логика работы технологии SSE видна на рисунке ниже. Запрос рукопожатия  отправляется SSE поддерживается сервером с помощью веб — браузера и системы сервер возвращает  ответ рукопожатия ,  которое «текст / событийно-поток» MIME тип. После подтверждения связи между веб-браузером и службой SSE служба SSE может в любое время отправлять любые объемы данных. jaxrs-сс

JaxRS 2 standard under the technology umbrella of Java EE 7 provides support for SSE (Server Sent Events) technology with its second version. And now, let’s practice a Jax-RS 2 application together by using Twitter REST API which searches a word and then broadcasts the results to all clients in a certain interval. Dependencies Jax-RS 2 is under the Java EE 7 spesification and Jersey 2 library is the reference implementation of it. So, if the application is desired to be run in Glassfish 4 application server, in the following Maven dependency element must be added pom.xml that is Maven configuration file.

<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>

SSE technology can be added to applications as an external feature of Jersey 2 library. It means that there is not any SSE feature in javaee-api (but glassfish contains inside), so it should be added as a provided dependency in the form of a plug-in/feature. 

<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-sse</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>

We will use Json-P technology in the application which is a standard under the Java EE 7, for the purpose of managing the JSON objects and arrays easily. Because of that, “javax.json” dependency should be added inside the pom.xml. 

<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.json</artifactId>
<version>1.0.3</version>
<scope>provided</scope>
</dependency>

Because this application will make a Twitter search, it must use Twitter REST API version 1.1. for this reason (1.0 deprecated), you can benefit from Twitter4j library which elasticizes the usage of Twitter REST API. 

<dependency>
<groupId>org.twitter4j</groupId>
<artifactId>twitter4j-core</artifactId>
<version>3.0.5</version>
</dependency>

If the application is desired to be run in some lightweight Servlet Container platforms such as Jetty or Tomcat apart from application servers, “jersey-container-servlet” dependency can be added inside the pom.xml.

<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>2.0</version>
</dependency>

Configuration

A REST Endpoint class should be added inside the application for an entry point to Jax-RS server.

@ApplicationPath("/search")
public class SearchEndpoint extends Application {

@Override
public Set<Class<?>> getClasses() {

   Set<Class<?>> classes=new HashSet<>();
   classes.add(SearchResource.class);
   return classes;
}

@Override
   public Set<Object> getSingletons() {
   Set<Object> singletons=new HashSet<>();
   singletons.add(new SseFeature());
   return singletons;
}
}

If you pay attention to Application#getClasses() method, you can see that a Java class by the name of SearchResource is configured as a RESTful resource, and a new SseFeature is declared to RESTful application in the Application#getSingletons() method.

SearchResource as a RESTful resource

A Java class namely SearchResource is a REST resource which enables the handshake between the web browser and server. In addition, SearchResource broadcasts the Twitter searches made on the server to the users.

@Path("/")
public class SearchResource {

private static final SseBroadcaster BROADCASTER = new SseBroadcaster();
private static final ScheduledExecutorService sch = Executors.newSingleThreadScheduledExecutor();

static {

 sch.scheduleWithFixedDelay(
      new SearchTwitTask(BROADCASTER)
      ,0 // Start instantly
      ,30 //  Run SearchTwitTask's run() in every 30 seconds
      ,TimeUnit.SECONDS);

}

@GET
@Produces("text/event-stream")
@Path("/hang")
public EventOutput getMessages() {

	EventOutput eventOutput = new EventOutput();
	BROADCASTER.add(eventOutput); // Added to broadcast list
	return eventOutput;
}

}

If data broadcasting is desired through the SSE technology with Jax-RS, a SseBroadcaster object must be created. If this SseBroadcaster object is added, every client that makes request to the server can make data broadcasting to all users through the SseBroadcaster#broadcast(..) method.

SseBroadcaster class contains within itself each unique user along with EventOutput object. For example, an EventOutput object is being created when a user makes RESTful request to SearchResource#getMessages method. This EventOutput object remains as a broadcasting object which represents the current user’s session. If created EventOutput object attached to SseBroadcaster object then BROADCASTER can send any data to relevant user. When created EventOutput object sent to user, server sends a “text/event-stream” resource, so web browser starts to retrieve data from the server after it takes “text/event-stream” type Handshake data.

Make a search in every 30 seconds and then broadcasting to users

public class SearchTwitTask implements Runnable {
private SseBroadcaster broadcaster;

public SearchTwitTask(SseBroadcaster broadcaster) {
this.broadcaster = broadcaster;
}

@Override
public void run() {

JsonArrayBuilder jsonArray = Json.createArrayBuilder();
JsonObjectBuilder jsonObject = Json.createObjectBuilder(); 

ConfigurationBuilder cb = new ConfigurationBuilder();
cb.setDebugEnabled(true)
    .setOAuthConsumerKey("kkkkkkkkkkkkk")
    .setOAuthConsumerSecret("sssssssssssssss")
    .setOAuthAccessToken("ttttttttttttttttttttt")
    .setOAuthAccessTokenSecret("tttttttttttttttttttsssssssssssss");

Twitter twitter = new TwitterFactory(cb.build()).getInstance();

QueryResult result = null;
try {
// Searching Java EE twits
result = twitter.search(new Query("Java EE")); 

// Every records added to Json Array as a Json Object
for (Status status : result.getTweets()) { 
	jsonObject.add("created_at", 
	       status.getCreatedAt().toString());
	jsonObject.add("from_user_name",
	       status.getUser().getScreenName());
	jsonObject.add("profile_image_url",
	       status.getUser().getProfileImageURL());

	jsonArray.add(jsonObject.add("text",   
	       status.getText()));
}
} catch (TwitterException e) {
e.printStackTrace(); 
}
// Gets result as a Json String
String twitJsonArray= jsonArray.build().toString(); 

// Creates a SSE Event object,,,
OutboundEvent.Builder b = new OutboundEvent.Builder();
// in application/json format
b.mediaType(MediaType.APPLICATION_JSON_TYPE);

// Search results are broadcasting to all members
broadcaster.broadcast(b.data(String.class,twitJsonArray).build());

}
}

SearchTwitTask is a task class which is type of Runnable interface and is being run in every 30 seconds with SchedulerExecutorService object. Of course EJB’s Timer Feature or Concurrent Utility version of (Managed)SchedulerExecutorService can be used here, but for portability reason, Java SE SchedulerExecutorService is used here. Firstly, Twitter records are being searched in which “Java EE” expression is mentioned and then the results are got as JsonArray object with Json-P library in the @Override Runnable#run method, respectively.

Broadcast operations done with Jax-RS 2 can be forwarded to users as OutboundEvent object which will be created by the developer. In the SearchTwitTask class, String type twitJsonArray data which is identified as MIME type “application/json”is being broadcasted to all application consumers through the broadcast method of SseBroadcasterclass.

SSE HTML side

<!DOCTYPE html>
<html class=" js no-touch svg inlinesvg svgclippaths no-ie8compat js no-touch svg inlinesvg svgclippaths no-ie8compat">
<head>
<title></title>
<meta charset="UTF-8">
<link type="text/css" rel="stylesheet" href="http://cdn.jsdelivr.net/foundation/4.0.4/css/foundation.min.css">
<link type="text/css" rel="stylesheet" href="http://cdn.jsdelivr.net/foundation/4.0.4/css/normalize.css">
</head>
<body>

<!-- Header and Nav -->
<div class="row">
<div class="large-12 columns">
<div class="panel">
<div class="row">
<div class="large-6 columns">
	<h1>Twitter Search Broadcaster</h1>
</div>
<div class="large-6 columns" >
	Pushes new data searhed for Java EE in every 30 seconds.
</div>
</div>
</div>
</div>
</div>

<!-- End Header and Nav -->
<div class="row" id="alertbox" style="display: none;">
<div class="large-12 columns">
<span class="alert-box alert">Your browser doesn not support HTML 5 SSE <img src="http://kodcu.com/wp/wp-includes/images/smilies/icon_sad.gif" alt=":(" class="wp-smiley"> </span>
</div>
</div>

<div class="row">
<!-- Feed Placeholder -->
<div id="feeds" class="large-12 columns"></div>

</div>

<!-- Footer -->
<footer class="row">
<div class="large-12 columns">
<hr>
<div class="row">
<div class="large-12 columns">
<p>© Twitter Search Broadcaster | kodcu.com</p>
</div>
</div>
</div>
</footer>

<script type="text/x-mustache-template" id="feedTemplate">
{{#.}}
<!-- Feed Entry -->
<div class="row">
<div class="large-1 columns small-3"><img style="min-height: 60px;min-width: 60px;" src="{{profile_image_url}}">
</div>
<div class="large-11 columns">
<div class="row">
<div class="large-12 columns">
	<p><strong>{{from_user_name}} said:</strong> {{text}}</p>
</div>
<div class="large-12 columns">
	<p><strong>Created date:</strong> {{created_at}}</p>
</div>
</div>

</div>
</div>
<!-- End Feed Entry -->

<hr>
{{/.}}
</script>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/mustache.js/0.7.2/mustache.min.js"></script>
<script type="text/javascript" src="resources/jaxrs-sse.js"></script>
</body>
</html>

The following technologies are used in the HTML side of the application where Twitter search is being made:

SSE Javascript side

(function () {

if((typeof EventSource)==="undefined")
{
   // if SSE is not supported by current browser 
   $("#alertbox").css("display","");
   return;
}

// Connecting to SSE service
var eventSource = new EventSource("search/hang");

// After SSE handshake constructed
eventSource.onopen = function (e) {
 console.log("Waiting message..");
};

// Error handler
eventSource.onerror = function (e) {
 console.log("Error");
 console.log(e);
};

// Message handler
eventSource.onmessage=function (e) {
   console.log(e);
   var feedTemplate = $("#feedTemplate").html();
  // Renders the template with Json Array returned SSE service 
   var feeds = Mustache.render(feedTemplate, $.parseJSON(e.data));
   
   $("#feeds").html(feeds);
};
})();

Javascript sided main object of the HTML 5 SSE technology is EventSource. Just as EventSource object is created, it makes a request to SSE service with an URL and operates Callback functions dependent to onopen, onerror, onmessage areas which of them are available for the management of session, error and getting message cases.

Running the application

Twitter search application can be run in two ways:

1. Way (With embedded jetty plug-in)

> mvn clean package jetty:run-war

2. Way (With Glassfish 4 application server)

> mvn clean package

> asadmin start-domain

> asadmin deploy JaxRS-SSE-1.0.war

You can access to the application which are run one of these methods from this address http://localhost:8080/JaxRS-SSE

A Live Example: http://discoverjavaee.com/JaxRS-SSE/

You can Access to application’s codes from http://en.kodcu.com/2013/11/jaxrs-2-html-5-server-sent-events-on-glassfish-4/

Hope to see you again.