Part I Development Tasks and Tools
1. Setting Up a Development Environment
Part II Developing Applications and Application Components
6. Using the Java Persistence API
7. Developing Web Applications
The CacheKeyGenerator Interface
Instantiating and Removing Servlets
JSP Tag Libraries and Standard Portable Tags
Options for Compiling JSP Files
Creating and Managing Sessions
HTTP Sessions, Cookies, and URL Rewriting
Saving Sessions During Redeployment
Distributed Sessions and Persistence
The replicated Persistence Type
The coherence-web Persistence Type
The Grizzly Implementation of Comet
Client Technologies to Use With Comet
Creating a Comet-Enabled Application
Creating the Deployment Descriptor
Advanced Web Application Features
Using the default-web.xml File
To Use the default-web.xml File
Configuring Logging and Monitoring in the Web Container
Configuring Idempotent URL Requests
Characteristics of an Idempotent URL
Configuring Valves and Catalina Listeners
8. Using Enterprise JavaBeans Technology
9. Using Container-Managed Persistence
12. Developing Lifecycle Listeners
13. Developing OSGi-enabled Java EE Applications
Part III Using Services and APIs
14. Using the JDBC API for Database Access
15. Using the Transaction Service
16. Using the Java Naming and Directory Interface
This section explains the Comet programming technique and how to create and deploy a Comet-enabled application with the Oracle GlassFish Server.
The following topics are addressed here:
Comet is a programming technique that allows a web server to send updates to clients without requiring the clients to explicitly request them.
This kind of programming technique is called server push, which means that the server pushes data to the client. The opposite style is client pull, which means that the client must pull the data from the server, usually through a user-initiated event, such as a button click.
Web applications that use the Comet technique can deliver updates to clients in a more timely manner than those that use the client-pull style while avoiding the latency that results from clients frequently polling the server.
One of the many use cases for Comet is a chat room application. When the server receives a message from one of the chat clients, it needs to send the message to the other clients without requiring them to ask for it. With Comet, the server can deliver messages to the clients as they are posted rather than expecting the clients to poll the server for new messages.
To accomplish this scenario, a Comet application establishes a long-lived HTTP connection. This connection is suspended on the server side, waiting for an event to happen before resuming. This kind of connection remains open, allowing an application that uses the Comet technique to send updates to clients when they are available rather than expecting clients to reopen the connection to poll the server for updates.
A limitation of the Comet technique is that you must use it with a web server that supports non-blocking connections to avoid poor performance. Non-blocking connections are those that do not need to allocate one thread for each request. If the web server were to use blocking connections then it might end up holding many thousands of threads, thereby hindering its scalability.
The GlassFish server includes the Grizzly HTTP Engine, which enables asynchronous request processing (ARP) by avoiding blocking connections. Grizzly's ARP implementation accomplishes this by using the Java NIO API.
With Java NIO, Grizzly enables greater performance and scalability by avoiding the limitations experienced by traditional web servers that must run a thread for each request. Instead, Grizzly's ARP mechanism makes efficient use of a thread pool system and also keeps the state of requests so that it can keep requests alive without holding a single thread for each of them.
Grizzly supports two different implementations of Comet:
Grizzly Comet — Based on ARP, this includes a set of APIs that you use from a web component to enable Comet functionality in your web application. Grizzly Comet is specific to the Oracle GlassFish Server.
Bayeux Protocol — Often referred to as Cometd, it consists of the JSON-based Bayeux message protocol, a set of Dojo or Ajax libraries, and an event handler. The Bayeux protocol uses a publish/subscribe model for server/client communication. The Bayeux protocol is portable, but it is container dependent if you want to invoke it from an Enterprise Java Beans (EJB) component. The Grizzly implementation of Cometd consists of a servlet that you reference from your web application.
In addition to creating a web component that uses the Comet APIs, you need to enable your client to accept asynchronous updates from the web component. To accomplish this, you can use JavaScript, IFrames, or a framework, such as Dojo.
An IFrame is an HTML element that allows you to include other content in an HTML page. As a result, the client can embed updated content in the IFrame without having to reload the page.
The example in this tutorial employs a combination of JavaScript and IFrames to allow the client to accept asynchronous updates. A servlet included in the example writes out JavaScript code to one of the IFrames. The JavaScript code contains the updated content and invokes a function in the page that updates the appropriate elements in the page with the new content.
The next section explains the two kinds of connections that you can make to the server. While you can use any of the client technologies listed in this section with either kind of connection, it is more difficult to use JavaScript with an HTTP-streaming connection.
When working with Comet, as implemented in Grizzly, you have two different ways to handle client connections to the server:
HTTP Streaming
Long Polling
The HTTP Streaming technique keeps a connection open indefinitely. It never closes, even after the server pushes data to the client.
In the case of HTTP streaming, the application sends a single request and receives responses as they come, reusing the same connection forever. This technique significantly reduces the network latency because the client and the server don't need to open and close the connection.
The basic life cycle of an application using HTTP-streaming is:
request --> suspend --> data available --> write response --> data available --> write response
The client makes an initial request and then suspends the request, meaning that it waits for a response. Whenever data is available, the server writes it to the response.
The long-polling technique is a combination of server-push and client-pull because the client needs to resume the connection after a certain amount of time or after the server pushes an update to the client.
The basic life cycle of an application using long-polling is:
request -> suspend --> data available --> write response --> resume
The client makes an initial request and then suspends the request. When an update is available, the server writes it to the response. The connection closes, and the client optionally resumes the connection.
If you anticipate that your web application will need to send frequent updates to the client, you should use the HTTP-streaming connection so that the client does not have to frequently reestablish a connection. If you anticipate less frequent updates, you should use the long-polling connection so that the web server does not need to keep a connection open when no updates are occurring. One caveat to using the HTTP-streaming connection is that if you are streaming through a proxy, the proxy can buffer the response from the server. So, be sure to test your application if you plan to use HTTP-streaming behind a proxy.
The following topics are addressed here:
Grizzly's support for Comet includes a small set of APIs that make it easy to add Comet functionality to your web applications. The Grizzly Comet APIs that developers use most often are the following:
CometContext: A Comet context, which is a shareable space to which applications subscribe to receive updates.
CometEngine: The entry point to any component using Comet. Components can be servlets, JavaServer Pages (JSP), JavaServer Faces components, or pure Java classes.
CometEvent: Contains the state of the CometContext object
CometHandler: The interface an application implements to be part of one or more Comet contexts.
The way a developer would use this API in a web component is to perform the following tasks:
Register the context path of the application with the CometContext object:
CometEngine cometEngine = CometEngine.getEngine(); CometContext cometContext = cometEngine.register(contextPath)
Register the CometHandler implementation with the CometContext object:
cometContext.addCometHandler(handler)
Notify one or more CometHandler implementations when an event happens:
cometContext.notify((Object)(handler))
This rest of this tutorial uses the Hidden Frame example to explain how to develop Comet-enabled web applications. You can download the example from grizzly.dev.java.net at Hidden example download. From there, you can download a prebuilt WAR file as well as a JAR file containing the servlet code.
The Hidden Frame example is so called because it uses hidden IFrames. The example allows multiple clients to increment a counter on the server. When a client increments the counter, the server broadcasts the new count to the clients using the Comet technique.
The Hidden Frame example uses the long-polling technique, but you can easily modify it to use HTTP-streaming by removing two lines. See To Notify the Comet Handler of an Event and To Create a HTML Page That Updates and Displays the Content for more information on converting the example to use the HTTP-streaming technique.
The client side of the example uses hidden IFrames with embedded JavaScript tags to connect to the server and to asynchronously post content to and accept updates from the server.
The server side of the example consists of a single servlet that listens for updates from clients, updates the counter, and writes JavaScript code to the client that allows it to update the counter on its page.
See Deploying and Running a Comet-Enabled Application for instructions on how to deploy and run the example.
When you run the example, the following happens:
The index.html page opens.
The browser loads three frames: The first one accesses the servlet using an HTTP GET; the second one loads the count.html page, which displays the current count; and the third one loads the button.html page, which is used to send the POST request.
After clicking the button on the button.html page, the page submits a POST request to the servlet.
The doPost method calls the onEvent method of the Comet handler and redirects the incremented count along with some JavaScript to the count.html page on the client.
The updateCount() JavaScript function on the count.html page updates the counter on the page.
Because this example uses long-polling, the JavaScript code on count.html calls doGet again to resume the connection after the servlet pushes the update.
This section uses the Hidden Frame example application to demonstrate how to develop a Comet application. The main tasks for creating a simple Comet-enabled application are the following:
This section shows you how to create a Comet-enabled web component by giving you instructions for creating the servlet in the Hidden Frame example.
Developing the web component involves performing the following steps:
Create a web component to support Comet requests.
Register the component with the Comet engine.
Define a Comet handler that sends updates to the client.
Add the Comet handler to the Comet context.
Notify the Comet handler of an event using the Comet context.
import javax.servlet.*; public class HiddenCometServlet extends HttpServlet { private static final long serialVersionUID = 1L; private String contextPath = null; @Override public void init(ServletConfig config) throws ServletException {} @Override protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {} @Override protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {); }
import com.sun.grizzly.comet.CometContext; import com.sun.grizzly.comet.CometEngine; import com.sun.grizzly.comet.CometEvent; import com.sun.grizzly.comet.CometHandler;
import java.io.IOException; import java.io.PrintWriter; import java.util.concurrent.atomic.AtomicInteger;
private final AtomicInteger counter = new AtomicInteger();
ServletContext context = config.getServletContext(); contextPath = context.getContextPath() + "/hidden_comet";
CometEngine engine = CometEngine.getEngine();
CometContext cometContext = engine.register(contextPath); cometContext.setExpirationDelay(30 * 1000);
private class CounterHandler implements CometHandler<HttpServletResponse> { private HttpServletResponse response; }
public void onInitialize(CometEvent event) throws IOException {} public void onInterrupt(CometEvent event) throws IOException { removeThisFromContext(); } public void onTerminate(CometEvent event) throws IOException { removeThisFromContext(); } public void attach(HttpServletResponse attachment) { this.response = attachment; } private void removeThisFromContext() throws IOException { response.getWriter().close(); CometContext context = CometEngine.getEngine().getCometContext(contextPath); context.removeCometHandler(this); }
You need to provide implementations of these methods when implementing CometHandler. The onInterrupt and onTerminate methods execute when certain changes occur in the status of the underlying TCP communication. The onInterrupt method executes when communication is resumed. The onTerminate method executes when communication is closed. Both methods call removeThisFromContext, which removes the CometHandler object from the CometContext object.
CounterHandler handler = new CounterHandler(); handler.attach(res);
CometEngine engine = CometEngine.getEngine(); CometContext context = engine.getCometContext(contextPath);
context.addCometHandler(handler);
public void onEvent(CometEvent event) throws IOException { if (CometEvent.NOTIFY == event.getType()) { int count = counter.get(); PrintWriter writer = response.getWriter(); writer.write("<script type='text/javascript'>" + "parent.counter.updateCount('" + count + "')" + "</script>\n"); writer.flush(); event.getCometContext().resumeCometHandler(this); } }
This method first checks if the event type is NOTIFY, which means that the web component is notifying the CometHandler object that a client has incremented the count. If the event type is NOTIFY, the onEvent method gets the updated count, and writes out JavaScript to the client. The JavaScript includes a call to the updateCount() function, which will update the count on the clients' pages.
The last line resumes the Comet request and removes it from the list of active CometHandler objects. By this line, you can tell that this application uses the long-polling technique. If you were to delete this line, the application would use the HTTP-Streaming technique.
Add the same code as for long-polling, except do not include the following line:
event.getCometContext().resumeCometHandler(this);
You don't include this line because you do not want to resume the request. Instead, you want the connection to remain open.
counter.incrementAndGet(); CometEngine engine = CometEngine.getEngine(); CometContext<?> context = engine.getCometContext(contextPath); context.notify(null); req.getRequestDispatcher("count.html").forward(req, res);
When a user clicks the button, the doPost method is called. The doPost method increments the counter. It then obtains the current CometContext object and calls its notify method. By calling context.notify, the doPost method triggers the onEvent method you created in the previous step. After onEvent executes, doPost forwards the response to the clients.
Developing the HTML pages for the client involves performing these steps:
Create a welcome HTML page, called index.html, that contains: one hidden frame for connecting to the servlet through an HTTP GET; one IFrame that embeds the count.html page, which contains the updated content; and one IFrame that embeds the button.html page, which is used for posting updates using HTTP POST.
Create the count.html page that contains an HTML element that displays the current count and the JavaScript for updating the HTML element with the new count.
Create the button.html page that contains a button for the users to submit updates.
<html> <head> <title>Comet Example: Counter with Hidden Frame</title> </head> <body> </body> </html>
<frameset> <iframe name="hidden" src="hidden_comet" frameborder="0" height="0" width="100%"></iframe> <iframe name="counter" src="count.html" frameborder="0" height="100%" width="100%"></iframe> <iframe name="button" src="button.html" frameborder="0" height="30%" widget="100%"></iframe> </frameset>
The first frame, which is hidden, points to the servlet by referencing its context path. The second frame displays the content from count.html, which displays the current count. The second frame displays the content from button.html, which contains the submit button for incrementing the counter.
<html> <head> </head> <body> <center> <h3>Comet Example: Counter with Hidden Frame</h3> <p> <b id="count"> </b> <p> </center> </body> </html>
This page displays the current count.
<script type='text/javascript'> function updateCount(c) { document.getElementById('count').innerHTML = c; parent.hidden.location.href = "hidden_comet"; }; </script>
The JavaScript takes the updated count it receives from the servlet and updates the count element in the page. The last line in the updateCount() function invokes the servlet's doGet method again to reestablish the connection.
Add the same code as for long-polling, except for the following line:
parent.hidden.location.href = “hidden_comet”
This line invokes the doGet method of CometServlet again, which would reestablish the connection. In the case of HTTP-Streaming, you want the connection to remain open. Therefore, you don't include this line of code.
<html> <head> </head> <body> <center> <form method="post" action="hidden_comet"> <input type="submit" value="Click"> </form> </center> </body> </html>
This page displays a form with a button that allows a user to update the count on the server. The servlet will then broadcast the updated count to all clients.
This section describes how to create a deployment descriptor to specify how your Comet-enabled web application should be deployed.
<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation= "http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd "> <servlet> <servlet-name>HiddenCometServlet</servlet-name> <servlet-class> com.sun.grizzly.samples.comet.HiddenCometServlet </servlet-class> <load-on-startup>0</load-on-startup> </servlet> <servlet-mapping> <servlet-name>HiddenCometServlet</servlet-name> <url-pattern>/hidden_comet</url-pattern> </servlet-mapping> </web-app>
This deployment descriptor contains a servlet declaration and mapping for HiddenCometServlet. The load-on-startup attribute must be set to 0 so that the Comet-enabled servlet will not load until the client makes a request to it.
Before running a Comet-enabled application in the GlassFish Server, you need to enable Comet in the server. Then you can deploy the application just as you would any other web application.
When running the application, you need to connect to it from at least two different browsers to experience the effect of the servlet updating all clients in response to one client posting an update to the server.
Before running a Comet-enabled application, you need to enable Comet in the HTTP listener for your application by setting a special attribute in the associated protocol configuration. The following example shows the asadmin set command that adds this attribute:
asadmin set server-config.network-config.protocols.protocol.http-1.http.comet-support-enabled="true"
Substitute the name of the protocol for http-1.
These instructions tell you how to deploy the Hidden Frame example.
as-install/bin/asadmin deploy grizzly-comet-hidden-1.7.3.1.war
These instructions tell you how to run the Hidden Frame example.
http://localhost:8080/grizzly-comet-hidden/index.html
The Bayeux protocol, often referred to as Cometd, greatly simplifies the use of Comet. No server-side coding is needed for servers such as GlassFish Server that support the Bayeux protocol. Just enable Comet and the Bayeux protocol, then write and deploy the client.
The following topics are addressed here:
Before running a Comet-enabled application, you need to enable Comet in the HTTP listener for your application by setting a special attribute in the associated protocol configuration. The following example shows the asadmin set command that adds this attribute:
asadmin set server-config.network-config.protocols.protocol.http-1.http.comet-support-enabled="true"
Substitute the name of the protocol for http-1.
To enable the Bayeux protocol on the GlassFish Server, you must reference the CometdServlet in your web application's web.xml file. In addition, if your web application includes a servlet, set the load-on-startup value for your servlet to 0 (zero) so that it will not load until the client makes a request to it.
<servlet> <servlet-name>Grizzly Cometd Servlet</servlet-name> <servlet-class> com.sun.grizzly.cometd.servlet.CometdServlet </servlet-class> <init-param> <description> expirationDelay is the long delay before a request is resumed. -1 means never. </description> <param-name>expirationDelay</param-name> <param-value>-1</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Grizzly Cometd Servlet</servlet-name> <url-pattern>/cometd/*</url-pattern> </servlet-mapping>
Note that the load-on-startup value for the CometdServlet is 1.
<servlet> ... <load-on-startup>0</load-on-startup> </servlet>
<script type="text/javascript" src="chat.js"></script>
dojo.require("dojo.io.cometd");
cometd.subscribe("/chat/demo", false, room, "_chat"); cometd.publish("/chat/demo", { user: room._username, chat: text});
asadmin deploy cometd-example.war
The context root for the example chat application is /cometd and the HTML page is index.html. So the URL might look like this:
http://localhost:8080/cometd/index.html
See Also
For more information about deployment in the GlassFish Server, see the Oracle GlassFish Server 3.1 Application Deployment Guide.
For more information about the Bayeux protocol, see Bayeux Protocol.
For more information about the Dojo toolkit, see http://dojotoolkit.org/.
For information about REpresentational State Transfer (RESTful) web services and Comet, see RESTful Web Services and Comet.