X

Proactive insights, news and tips from Oracle WebLogic Server Support. Learn Oracle from Oracle.

WLS 12.2.1 launch - Servlet 3.1 new features

Introduction

WLS 12.2.1 release support new features of Servlet 3.1 specification.
The Servlet 3.1 specification is a major version of Servlet
specification. This version of specification mainly introduced
Non-blocking IO and Http Protocal upgrade features into
ServletContainer for adopting in modern web application
development.Non-blocking IO helps improve the ever increasing demand for
improved Web Container scalability, increase the number of connections
that can simultaneously be handled by the Web Container. Non-blocking
IO in the Servlet container allows developers to read data as it
becomes available or write data when possible to do so. Also this
version introduced several minor changes for security and functional
enhancement.

1 Upgrade Processing

1.1 Description

In HTTP/1.1, the Upgrade general-header allows the client to specify
the additional communication protocols that it supports and would like
to use. If the server finds it appropriate to switch protocols, then
new protocols will be used in subsequent communication.

The Servlet container provides an HTTP upgrade mechanism. However the
Servlet container itself does not have knowledge about the upgraded
protocol. The protocol processing is encapsulated in the
HttpUpgradeHandler. Data reading or writing between the Servlet
container and the HttpUpgradeHandler is in byte streams.

When an upgrade request is received, the Servlet can invoke the
HttpServletRequest.upgrade method, which starts the upgrade process.
This method instantiates the given HttpUpgradeHandler class. The
returned HttpUpgradeHandler instance may be further customized. The
application prepares and sends an appropriate response to the client.
After exiting the service method of the Servlet, the Servlet container
completes the processing of all filters and marks the connection to be
handled by the HttpUpgradeHandler. It then calls the
HttpUpgradeHandler's init method, passing a WebConnection to allow the
protocol handler access to the data streams.

The Servlet filters only process the initial HTTP request and response.
They are not involved in subsequent communications. In other words,
they are not invoked once the request has been upgraded. The
HttpUpgradeHandler may use non blocking IO to consume and produce
messages. The Developer has the responsibility for thread safe access to
the ServletInputStream and ServletOutputStream while processing HTTP
upgrade. When the upgrade processing is done, HttpUpgradeHandler.destroy
will be invoked.

1.2 Example

In this example, the client sends the request to the server. The
server accepts the request, sends back the response, and then invokes
the HttpUpgradeHandler.init() method and continues the communication
using a dummy protocol. The client shows the request and response
headers during the handshake process.

Client

the client initiates the HTTP upgrade request.

@WebServlet(name = "ClientTest", urlPatterns = {"/"})
public class ClientTest extends HttpServlet {protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {String reqStr = "POST " + contextRoot + "/ServerTest HTTP/1.1" + CRLF;
...
reqStr += "Upgrade: Dummy Protocol" + CRLF;// Create socket connection to ServerTest
s = new Socket(host, port);
input = s.getInputStream();
output = s.getOutputStream();// Send request header with data
output.write(reqStr.getBytes());
output.flush();
}
}

The header Upgrade: Dummy Protocol is an HTTP/1.1 header field set to
Dummy Protocol in this example. The server decides whether to accept
the protocol upgrade request.

Server

ServerTest.java checks the Upgrade field in the request header. When
it accepts the upgrade requests, the server instantiates
ProtocolUpgradeHandler, which is the implementation of
HttpUpgradeHandler. If the server does not support the Upgrade protocol
specified by the client, it sends a response with a 404 status.

 @WebServlet(name="ServerTest", urlPatterns={"/ServerTest"})
public class ServerTest extends HttpServlet {protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// Checking request header
if ("Dummy Protocol".equals(request.getHeader("Upgrade"))){
...
ProtocolUpgradeHandler handler = request.upgrade(ProtocolUpgradeHandler.class);
} else {
response.setStatus(400);
...
}
}
...
}

ProtocolUpgradeHandler is the implementation of HttpUpgradeHandler,
which processes the upgrade request and switches the communication
protocol. The server checks the value of the Upgrade header to determine
if it supports that protocol. Once the server accepts the request, it
must use the Upgrade header field within a 101 (Switching Protocols)
response to indicate which protocol(s) are being switched.

Implementation of HttpUpgradeHandler

 public class ProtocolUpgradeHandler implements HttpUpgradeHandler {
@Overridepublic void init(WebConnection wc) {this.wc = wc;try {
ServletOutputStream output = wc.getOutputStream();
ServletInputStream input = wc.getInputStream();
Calendar calendar = Calendar.getInstance();
DateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");// Reading the data into byte array
input.read(echoData);// Setting new protocol header
String resStr = "Dummy Protocol/1.0 " + CRLF;
resStr += "Server: Glassfish/ServerTest" + CRLF;
resStr += "Content-Type: text/html" + CRLF;
resStr += "Connection: Upgrade" + CRLF;
resStr += "Date: " + dateFormat.format(calendar.getTime()) +CRLF;
resStr += CRLF;// Appending data with new protocol
resStr += new String(echoData) + CRLF;// Sending back to client
...
output.write(resStr.getBytes());
output.flush();
} catch (IOException ex) {
Logger.getLogger(ProtocolUpgradeHandler.class.getName()).log(Level.SEVERE, null, ex);
}
...
}
@Overridepublic void destroy() {
...try {
wc.close();
} catch (Exception ex) {
Logger.getLogger(ProtocolUpgradeHandler.class.getName()).log(Level.SEVERE, "Failed to close connection", ex);
}
...
}
}
The init() method sets up the new protocol headers. The new
protocol is used for subsequent communications. This example uses a
dummy protocol. The destroy() method is invoked when the upgrade process
is done. This example shows the handshake process of the protocol
upgrade. After the handshake process, the subsequent communications use
the new protocol. This mechanism only applies to upgrading
application-layer protocols upon the existing transport-layer
connection. This feature is most useful for Java EE Platform providers.

2 Non-blocking IO

2.1 Description

Non-blocking request processing in the Web Container helps improve
the ever increasing demand for improved Web Container scalability,
increase the number of connections that can simultaneously be handled by
the Web Container. Non blocking IO in the ServletContainer allows
developers to read data as it becomes available or write data when
possible to do so. Non-blocking IO only works with async request
processing in Servlets and Filters, and upgrade processing. Otherwise,
an IllegalStateException must be thrown when ServletInputStream's
setReadListener is invoked.

2.2 Non-Blocking Read Example

Servlet

In ServerServlet, the server receives the request, starts the
asynchronous processing of the request, and registers a ReadListener

 @WebServlet(name = "ServerServlet", urlPatterns = {"/server"}, asyncSupported = true)public class ServerServlet extends HttpServlet {
.....protected void service(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");// async read
final AsyncContext context = request.startAsync();final ServletInputStream input = request.getInputStream();final ServletOutputStream output = response.getOutputStream();
input.setReadListener(new ReadListenerImpl(input, output, context));
}
Note: Non-blocking I/O only works with asynchronous
request processing in servlets and filters or upgrade handler. See
Servlet Spec 3.2 for more details.

Read Listener Implementation

 public class ReadListenerImpl implements ReadListener {private ServletInputStream input;private ServletOutputStream output;private AsyncContext context;private StringBuilder sb = new StringBuilder();public ReadListenerImpl(ServletInputStream input, ServletOutputStream output, AsyncContext context) {this.input = input;this.output = output;this.context = context;
}
/**
* do when data is available to be read.
*/
@Overridepublic void onDataAvailable() throws IOException {while (input.isReady()) {
sb.append((char) input.read());
}
}
/**
* do when all the data has been read.
*/
@Overridepublic void onAllDataRead() throws IOException {try {
output.println("ServerServlet has received '" + sb.toString() + "'.");
output.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
context.complete();
}
}
/**
* do when error occurs.
*/
@Overridepublic void onError(Throwable t) {
context.complete();
t.printStackTrace();
}

The onDataAvailable() method is invoked when data is available to be
read from the input request stream. The container subsequently invokes
the read() method if and only if isReady() returns true. The
onAllDataRead() method is invoked when all the data from the request has
been read. The onError(Throwable t) method is invoked if there is any
error or exceptions occurs while processing the request. The isReady()
method returns true if the underlying data stream is not blocked. At
this point, the container invokes the onDataAvailable() method.

Users can customize the constructor to handle different parameters.
Usually, the parameters are ServletInputStream, ServletOutputStream, or
AsyncContext. This sample uses all of them to implement the ReadListener
interface.

2.3 Non-Blocking Write Example

Servlet

In ServerServlet.java, after receiving a request, the servlet starts
the asynchronous request processing and registers a WriteListener.

 protected void processRequest(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");// async write
final AsyncContext context = request.startAsync();final ServletOutputStream output = response.getOutputStream();
output.setWriteListener(new WriteListenerImpl(output, context));
}

Write Listener Implementation

 public class WriteListenerImpl implements WriteListener {private ServletOutputStream output;private AsyncContext context;public WriteListenerImpl(ServletOutputStream output, AsyncContext context) {this.context = context;this.output = output;
}
/**
* do when the data is available to be written
*/
@Overridepublic void onWritePossible() throws IOException {if (output.isReady()) {
output.println("<p>Server is sending back 5 hello...</p>");
output.flush();
}for (int i = 1; i <= 5 && output.isReady(); i++) {
output.println("<p>Hello " + i + ".</p>");
output.println("<p>Sleep 3 seconds simulating data blocking.<p>");
output.flush();// sleep on purpose
try {Thread.sleep(3000);
} catch (InterruptedException e) {// ignore
}
}
output.println("<p>Sending completes.</p>");
output.flush();
context.complete();
}
/**
* do when error occurs.
*/
@Overridepublic void onError(Throwable t) {
context.complete();
t.printStackTrace();
}
}
The method onWritePossible() is invoked when data is
available to write to the response stream. The container subsequently
invokes the writeBytes() method if and only if isReady() returns true.
The onError(Throwable t) method is invoked if any error or exceptions
occur while writing to the response. The isReady() method returns true
if the underlying data stream is not blocked. At this point, the
container invokes the writeBytes() method.

4 SessionID change

4.1 Description

Servlet specification 3.1 a new interfaces and method for avoiding
Session fixation. Weblogic ServletContainer should implement the session
id change processing for security reason.

4.2 SessionID change Example

In this example application, the SessionIDChangeListener interface
overrides the sessionIdChanged method, which receives a notification
that the session ID has been changed in a session. The
SessionIDChangeTest changes the value of the session ID by invoking
javax.servlet.http.HttpServletRequest.changeSessionId().

Servlet

 @WebServlet(name = "SessionIDChangeServlet", urlPatterns = {"/SessionIDChangeServlet"})
public class SessionIDChangeServlet extends HttpServlet {protected void processRequest(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
HttpSession session = request.getSession(true);try {
StringBuilder sb = new StringBuilder();
sb.append("<h3>Servlet SessionIDChangeTest at " + request.getContextPath() + "</h3><br/>");
sb.append("<p>The current session id is: &nbsp;&nbsp;" + session.getId() + "</p>");
/* Call changeSessionID() method. */
request.changeSessionId();
sb.append("<p>The current session id has been changed, now it is: &nbsp;&nbsp;" + session.getId() + "</p>");
request.setAttribute("message", sb.toString());
request.getRequestDispatcher("response.jsp").forward(request, response);
} finally {
out.close();
}
}
....
}

The Servlet get a session object from request. A sessionID generated
at that time. After request.changeSessionId() was called a new sessionID
generated to replace the old one on the session object.

HttpSessionIdListener Implementation

 @WebListener
public class SessionIDChangeListener implements HttpSessionIdListener {
@Overridepublic void sessionIdChanged(HttpSessionEvent event, String oldSessionId) {System.out.println("[Servlet session-id-change example] Session ID " + oldSessionId + " has been changed");
}
}
The implementation's sessionIdChanged method will be triggered when the request.changeSessionId() was called.

Be the first to comment

Comments ( 0 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.Captcha
Oracle

Integrated Cloud Applications & Platform Services