Shing Wai Chan's Weblog

Servlet 3.0 web-fragment.xml

Thursday May 07, 2009

In JSR 315: Java Servlet 3.0 Specification, web-fragment.xml is introduced for pluggability of library jars which are packaged under WEB-INF/lib. The content of web.xml and web-fragment.xml are almost the same. One can define servlets, filters and listeners there. One can also specify metadata-complete=true in a given web-fragment.xml. In the latter case, the annotation processing of classes in that jar would be skipped. With web-fragment.xml, library jars can be self-contained and provide web related metadata information.

The basic differences of web.xml and web-fragment.xml are summarized in the following table:
 web.xmlweb-fragment.xml
LocationWEB-INF of the war fileMETA-INF directory of JAR file inside WAR file's WEB-INF/lib
Ordering related element<absolute-ordering><ordering>

Ordering of web fragments

If there are more than one web-fragment jars, then one may like to specify the order of processing web-fragment.xml and annotations. This is important. For instance, filters will be executed in the order specified in web.xml. Similary for listeners. In Servlet 3.0, <absolute-ordering> is introduced in web.xml and <ordering> is introduced in web-fragment.xml. The ordering of web-fragments is specified in the following priority:
  • from <absolute-ordering> in web.xml if it exists
  • from <ordering> for each web-fragment.xml if it exists
  • otherwise unspecified

absolute-ordering in web.xml

The <absolute-ordering> in web.xml provides a way to specify the ordering of loading web-fragment.xml and annotation processing of web fragment. For instance,
<web-app>
    ...
    <absolute-ordering>
        <name>A</name>
         <others/>
        <name>B</name>
    <absolute-ordering>
</web-app>
In the above example, the web fragment A would be processed first and web fragment B would be processed last. Note the name A and B are specified in name element of web-fragment.xml (see examples below).

ordering in web-fragment.xml

If there is no <absolute-ordering> in web.xml, then one would look at <ordering> in web-fragment.xml. The details are described in section 8.2.3 of Servlet 3.0 spec. Let us look at some examples.
  • There is only one jar having <ordering> in web-fragment.xml.
    <web-fragment>
        <name>A</name>
        ...
        <ordering>
            <before>
                <others/>
            </before>
        </ordering>
    </web-fragment>
    In this case, web-fragment A would be processed first.

  • There are two jars having <ordering> in web-fragment.xml, namely
    web-fragment A:
    <web-fragment>
        <name>A</name>
        ...
        <ordering>
            <before>
                <others/>
            </before>
        </ordering>
    </web-fragment>

    web-fragment B:

    <web-fragment>
        <name>B</name>
        ...
        <ordering>
            <before>
                <others/>
            </before>
        </ordering>
    </web-fragment>
    Both web-fragment A and B would like to be processed first. In this case, one only guarantee that both A and B are processed before other web-fragments. But the ordering of A and B are not determined, that is arbitrary in this case.

  • There are two jars having <ordering> in web-fragment.xml, namely
    web-fragment A:
    <web-fragment>
        <name>A</name>
        ...
        <ordering>
            <before>
                <others/>
            </before>
        </ordering>
    </web-fragment>

    web-fragment B:

    <web-fragment>
        <name>B</name>
        ...
        <ordering>
            <after>
                <name>A</name>
            </after>
            <before>
                <others/>
            </before>
        </ordering>
    </web-fragment>
    In this case, A would be processed first, then followed by B, and then other web-fragments.
If one would like to have a deterministic ordering, then I would recommend to use absolute-ordering in web.xml.

[5] Comments
Like this post? del.icio.us | furl | slashdot | technorati | digg

Servlet 3.0 Security Annotations

Monday May 04, 2009

In Servlet 2.5, only @DeclareRoles and @RunAs are supported in servlets. And @DenyAll, @PermitAll, @RolesAllowed are only supported for EJBs. In JSR 315: Java Servlet 3.0 Specification, @DenyAll, @PermitAll, @RolesAllowed will be supported in servlets. Furthermore, it supports JSR 250: Common Annotations for the Java Platform MR1:

  • @TransportProtected, a new annotation indicates whether the transport is confidential or none.
  • @DenyAll will also be available at the TYPE level.

The mapping of the @DenyAll, @PermitAll, @RolesAllowed and @TransportProtected to security constraint are described in Chapter 13.4.1 of Servlet 3.0 specification. These annotations can be applied to:

  • the servlet class
  • one of the following methods in HttpServlet:
    • doDelete
    • doGet
    • doHead
    • doOptions
    • doPost
    • doPut
    • doTrace
Note that method level authorization annotations (@DenyAll, @PermitAll, @RolesAllowed) override those in class level for the associated http method. Similarly, method level @TransportProtected overrides the one in class level.

In this blog, we illustrate the usages of these annotations by examples.

Example 1: Type Level

@WebServlet("/myurl")
@RolesAllowed("javaee")
public class TestServlet extends HttpServlet {
    ...
}

In this case, all http methods are protected and accessible only by users with role javaee.

Example 2: Method Level

@WebServlet("/myurl")
public class TestServlet extends HttpServlet {
    @PermitAll
    protected void doGet(HttpServletRequest req, HttpServletResponse res)
            throws IOException, ServletException {
        ...
    }

    @RolesAllowed("javaee")
    protected void doPost(HttpServletRequest req, HttpServletResponse res)
            throws IOException, ServletException {
        ...
    }

    @DenyAll
    protected void doTrace(HttpServletRequest req, HttpServletResponse res)
            throws IOException, ServletException {
        ...
    }
}

The behaviors of the above servlet can be summarized as follows:
Http methodBehavior
GETall can access GET method
POSTonly authenticated users with role javaee can access POST method
TRACEno one can access TRACE method

Example 3: Type and Method Level

@WebServlet("/myurl")
@RolesAllowed("javaee")
public class TestServlet extends HttpServlet {
    ...

    @RolesAllowed("staff")
    protected void doPost(HttpServletRequest req, HttpServletResponse res)
            throws IOException, ServletException {
        ...
    }

    @PermitAll
    protected void doTrace(HttpServletRequest req, HttpServletResponse res)
            throws IOException, ServletException {
        ...
    }
}

The behaviors of the above servlet can be summarized as follows:
Http methodBehavior
POSTonly authenticated users with role staff can access POST method
TRACEall can access TRACE method
methods other than POST and TRACEonly authenticated users with role javaee can access

Example 4: @TransportProtected and @RolesAllowed

@WebServlet("/myurl")
@TransportProtected
public class TestServlet extends HttpServlet {
    ...

    @TransportProtected(false)
    @RolesAllowed("javaee")
    protected void doTrace(HttpServletRequest req, HttpServletResponse res)
        throws IOException, ServletException {

        ...
    }
}

The behaviors of the above servlet can be summarized as follows:
Http methodBehavior
TRACEno https, only authenticated users with role javaee can access TRACE method
methods other than TRACErequire https

[3] Comments

Using Jetty Bayeux Client in GlassFish v3

Tuesday Mar 10, 2009

In Cometd environment, one can access cometd services through simple Javascript, Java API for Bayeux Protoc, DOJO, etc.

In the Grizzly Issue 174, developer amplus has contributed a first porting of Jetty Bayeux Java Client to Grizzly. A modification of the contribution has been checkin to Grizzly. The above client code is based on Jetty 6.1.11. Subsequently, various cometd bugs has been fixed in Grizzly 1.9.8 or later.

In this blog, we will describe how to use the Jetty Bayeux client in Grizzly with GlassFish v3.

Environment Setting

In this moment, comet support is turned off by default. The comet/cometd can be turned on in GlassFish v3 by adding the following property to corresponding http-listener. In our case, it is the http-listener-1.

    <property name="cometSupport" value="true">

Note that it is recommended that one should set the above property by using asadmin rather than directly editing the domain.xml. For instance,

    asadmin set server.http-service.http-listener.http-listener-1.property.cometSupport=true

Download the following jars and put it under $GLASSFISH_HOME/domains/domain1/lib:

  • grizzly-cometd-client-1.9.8.jar
  • cometd-bayeux-6.1.11.jar
  • jetty-6.1.11.jar
  • jetty-client-6.1.11.jar
  • jetty-util-6.1.11.jar
The first jar is from Grizzly repository. Note that you may like to get the correct Grizzly version working with your GlassFish v3. (For instance, one can find out the Grizzly version in MANIFEST.MF of web-glue.jar.) The remaining jars are from Jetty repository 6.1.11.

Finally, we have to start the server.

A Cometd client application using Jetty Bayeux client

In our example, we will create a web Bayeux Client for Cometd Chat Sample. It can be downloaded here.

One need to import Jetty's classes as follows:

    import org.mortbay.cometd.AbstractBayeux;
    import org.mortbay.cometd.client.BayeuxClient;
    import org.mortbay.jetty.client.HttpClient;
    import org.mortbay.thread.QueuedThreadPool;
    import org.mortbay.util.ajax.JSON;
    import dojox.cometd.Bayeux;
    import dojox.cometd.Client;
    import dojox.cometd.Message;
    import dojox.cometd.MessageListener;

Then one need to create a BayeuxClient in as follows:

  • Create a HttpClient.

        httpClient = new HttpClient();
        httpClient.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);
        httpClient.setMaxConnectionsPerAddress(50);

  • Create a QueuedThreadPool for the HttpClient.

        QueuedThreadPool pool = new QueuedThreadPool();
        pool.setMaxThreads(50);
        pool.setDaemon(true);
        httpClient.setThreadPool(pool);

  • Start the HttpClient and create a BayeuxClient.

        httpClient.start();
        client = new BayeuxClient(httpClient, address, cometdUri) {
            public void deliver(Client from, Message message) {
                // do something
                super.deliver(from, message);
            }
        };

    where address is an InetSocketAddress and cometdUri is the cometd service uri.

  • Create a MessageListener for the BayeuxClient.

        MessageListener listener = new MessageListener() {
            public void deliver(Client fromClient, Client toClient, Message msg) {
                Object data = msg.get(AbstractBayeux.DATA_FIELD);
                if (data != null) {
                   // do something
               }
           }
        };
        client.addListener(listener);

    This listener is a no-op as we only use the client to send message in our case.

  • Start the BayeuxClient and subscribe the channel.

        client.start();
        client.subscribe(channel);

With the setup above, the JSON message can be published as follows:

    Object msg=new JSON.Literal("{\"user\":\"" + name + "\",\"chat\":\"" + chat + "\"}");
    client.publish(channel, msg, String.valueOf(mid.getAndIncrement()));

where msg is a JSON object associated to data.

Comparison with Java API for Bayeux Protocol

In this section, we will compare Jetty Bayeux client with the Java API for Bayeux Protocol. For convenience, I will summarize the Java API for Bayeux Protocol as follows:
  • Get the CometContext.

        CometEngine context = CometEngine.getEngine().getCometContext(channel);

  • Create the DeliverResponse.

        Map map = new HashMap();
        map.put(messageDataName, messageDataValue);
        Data data = new Data();
        data.setMapData(map);

        DeliverResponse deliverResponse = new DeliverResponse();
        deliverResponse.setChannel("/service/echo");
        deliverResponse.setClientId(clientId);
        deliverResponse.setData(data);
        deliverResponse.setLast(true);
        deliverResponse.setFollow(true);
        deliverResponse.setFinished(true);

    Note that in this moment, one has to setFinished(true) in addition to setLast(true).

  • Notify the deliverResponse

        context.notify(deliverResponse);

 Jetty Bayeux ClientJava API for Bayeux Protocol
Setupmore involvedsimple
Access Cometd Servicelocal and remotelocal
Clientin server and standlonein server
Construction of JSON messagessome explicitlyall use API

[0] Comments
Like this post? del.icio.us | furl | slashdot | technorati | digg

Servlet 3.0 Annotations

Tuesday Dec 02, 2008

The JSR 315: Java Servlet 3.0 Specification expert group is in the process of making Public Review available. You can look at Rajiv's blog for more details. The reference implementation is available in GlassFish v3 nightly build. In Servlet 3.0, for ease of development, several new annotations are defined. These annotations are resided in the package javax.servlet.annotation. They are intended to provide meta data only. In other words, one still need to extend the corresponding class or implement the corresponding interface.

Now, one can have Servlet, Filter and ServletContextListener in a war file without web.xml. In this blog, I will discuss the following annotations:

  • @WebServlet
  • @ServletFilter
  • @WebServletContextListener

Servlet Annotation ( @WebServlet )

In JSR 315, one can specify the servlet meta data by using @WebServlet. For instance,

    @WebServlet(name="mytest",
        urlPatterns={"/myurl"},
        initParams={ @InitParam(name="n1", value="v1"), @InitParam(name="n2", value="v2") })
    public class TestServlet extends javax.servlet.http.HttpServlet {
        ....
    }

In this example, the class TestServlet is a servlet as it extends HttpServlet. The @WebServlet provides the following meta data:

  • the name of the servlet, mytest, corresponds to <servlet-name> under <servlet> in web.xml
  • the url pattern of the servlet, /myurl, corresponds to <url-pattern> under <servlet-mapping> in web.xml
  • initialization parameters of the servlet, n1=v1, n2=v2, corresponds to <init-param> under <servlet> in web.xml
      <init-param>
        <param-name>n1</param-name>
        <param-value>v1</param-value>
      </init-param>
      <init-param>
        <param-name>n2</param-name>
        <param-value>v2</param-value>
      </init-param>
    Note that in this case, @InitParam is used to specify the name/value pairs.

Servlet Filter Annotation ( @ServletFilter )

One can specify the servlet filter meta data by using @ServletFilter. For instance,

    @ServletFilter(urlPatterns={"/myurl"}.
        initParams={ @InitParam(name="mesg", value="my filter") })
    public class TestFilter implements javax.servlet.Filter {
        ....
        public void init(FilterConfig filterConfig) throws ServletException {
            ....
        }

        public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
            ....
        }

        public void destroy() {
            ....
        }
    }

In this example, the class TestFilter is a servlet filter as it implements Filter. The @ServletFilter provides the following meta data:

  • the url pattern of the filter applied, /myurl
  • initialization parameter of the filter, mesg=my filter, corresponds to <init-param> under <filter> in web.xml
    Note that in this case, @InitParam is used to specify the name/value pairs.

Servlet Context Listener Annotation ( @WebServletContextListener )

One can specify the servlet content listener met data by using @WebServletContextListener. For instance,

    @WebServletContextListener
    public class TestServletContextListener implements javax.servlet.ServletContextListener {
        ....
        public void contextInitialized(ServletContextEvent sce) {
            ....
        }

        public void contextDestroyed(ServletContextEvent sce) {
            ....
        }
    }

In this example, the class TestServletContextListener is a servlet context listener as it implements ServletContextListener. The @WebServletContextListener provides the meta data that this is a servlet context listener in a given war file.

Like this post? del.icio.us | furl | slashdot | technorati | digg

compression and compressionMinSize in GlassFish v3

Friday Sep 26, 2008

In Enabling HTTP Compression in GlassFish, Jean-Francois discussed about compression in GlassFish. There are four properties to configure compression, namely:

  • compression
  • compressionMinSize (in OCTET)
  • compressableMimeType
  • noCompressionUserAgents
One can turn compression on and off by setting compression = force and compression = off respectively. And one can also turn on the compression if the content-length is unknown or known to be greater than a certain size. There are two properties related to this: compression and compressionMinSize. In this blog, we will discuss various ways to accomplish this scenario in GlassFish v3.

compression vs compressionMinSize

propertypossible value if setDefault
compressionon, force, off, integeroff
compressionMinSizeinteger2048

There are several possible combinations. We will summarize the behaviors of GlassFish v3 in the below table. In the following, α and β are integers.

compressioncompressionMinSizeResult
onβcompression with min size β
onnot setcompression with default min size (2048)
forceβ or not setcompression with no size constraint
αβ or not setcompression with min size α
off or not setβ or not setno compression

So, roughly speaking, whenever there is conflicting information between compression and compressionMinSize, the compression property will take precedence.

Since we use strict inequality to check for known content length, the following are equivalent:

  • compression = force
  • compression = on and compressionMinSize = any negative integer

How to test it?

If one has turned on compression in GlassFish, then one will get HTTP compression if the HTTP request is
  • using HTTP 1.1
  • with a HTTP header:
        Accept-Encoding: gzip
  • the content-length is unknown or greater than the compression minimum size
    (or compression = force)
One can confirm that there is a HTTP compression by using Firefox with Firebug. There will be a HTTP response header:
  • Content-Encoding: gzip
Note that one can also notice some changes in HTTP response for HTTP compression by using the http client posted in one of my previous blogs.

Like this post? del.icio.us | furl | slashdot | technorati | digg

WEBDAV in GlassFish

Thursday Aug 14, 2008

WEBDAV (RFC 4918) protocol is a predecessor to HTTP/1.1 for management resources, etc. The WEBDAV code in GlassFish workspace is based on Tomcat. Jean-Francois blogged about this in 2006. WEBDAV Level 2 will be a supported feature in GlassFish v3. In this blog, we would provide additonal information about WEBDAV in GlassFish v3.

Configuration of WebDAVServlet

WEBDAV can be enabled by specifying the org.apache.catalina.servlets.WebdavServlet in web.xml for a given web application. Also, it can be enabled and configured globally in default-web.xml. One can configure WebDAVServlet by specifying the init-param as follows:

init-paramTypeDescriptionDefault
debugintdebug level0 (no debug)
listingsbooleanwhether one can list resourcesfalse
readonlybooleanwhether resources are readonlytrue

It is important to note that when listings is set to true or readonly is set to false, one must set up security constraints and turn on security manager.

WEBDAV Clients

I have verified that the following WEBDAV clients work with GlassFish v3:
  • Microsoft Word 2002 and 2003
      File > Open > the url
  • Internet Explorer 6 and 7
      File > Open (check "Open as Web Folder") > the url
    Note that under "Internet Options > Programs > HTML editor", we may like to set it to Microsoft Word above or Mircrosoft FrontPage.

Hand-On Examples on WEBDAV protocol

WEBDAV includes the following HTTP methods:
  • PROPFIND
  • PROPMATCH
  • MKCOL
  • GET
  • HEAD
  • POST
  • DELETE
  • PUT
  • COPY
  • MOVE
  • LOCK
  • UNLOCK
I find that it is a good exercise to send HTTP requests directly and see what happens there. For instance,
  • the following HTTP request copy index.html to index2.html:
    COPY /webdavtest/index.html HTTP/1.1
    Host: localhost
    Destination: http://localhost:8080/webdavtest/index2.html
    Connection: close
    
    
  • the following HTTP request delete the index2.html created above:
    DELETE /webdavtest/index2.html HTTP/1.1
    Host: localhost
    Connection: close
    
    
There are many ways to send http client requests to server. I find that it is quite handy to have the following Ruby script.


  #!/usr/bin/ruby
  
  require "socket"

  if ARGV.length != 3
      puts "ruby httpclient.rb <host> <port> <http request file>\n"
      exit
  end

  host = ARGV[0];
  port = ARGV[1].to_i;
  filename = ARGV[2];

  socket = TCPSocket.open(host, port)

  file = File.new(filename)
  while line = file.gets
      command = line.chomp
      puts command + "\r\n"
      socket.write(command + "\r\n")
  end

  puts socket.readlines

  socket.close

Note that you may like to update the path of ruby or run the interpretator directly.

Like this post? del.icio.us | furl | slashdot | technorati | digg

Common Gateway Interface in GlassFish

Tuesday Jul 15, 2008

Common Gateway Interface (CGI) supports dynamic contents in web environment. CGI programs are executable programs in the server platform with specific output. It can be a Bourne shell script, Perl script or even a C binary executable. It was very popular before the the appearance of Servlet, JSP and PHP. The CGI code in GlassFish workspace is based on Tomcat. In GlassFish v3, CGI will be a supported feature. Let us look at a very simple example.

Create a CGI script

In our example, we have a simple Perl program, hello, to print a hello message and the timestamp of the server.

    #!/bin/perl
    print "Content-type: text/html\n\n";
    print "Hello World: ";
    print scalar localtime;
    print "\n";

Enabling CGI processing and packaging the war file

The CGI processing can be enabled in a war file by adding CGIServlet et al in web.xml as follows:

  <web-app>
    <servlet>
      <servlet-name>cgi</servlet-name>
      <servlet-class>org.apache.catalina.servlets.CGIServlet</servlet-class>
    </servlet>

    <servlet-mapping>
      <servlet-name>cgi</servlet-name>
      <url-pattern>/cgi-bin/*</url-pattern>
    </servlet-mapping>
  </web-app>

In this case, one need to package the hello under the default cgiPathPrefix which is WEB-INF/cgi. For security, it is highly recommended that the contents / binaries of CGI programs should be prohibited from direct viewing or download.

Alternatively, one can enable CGI by uncommenting the corresponding sections in default-web.xml.

In our case, the CGI program can be invoked through http://localhost:8080/YOUR_CONTEXT/cgi-bin/hello .

Configuration of CGIServlet

One can configure CGIServlet by specifying the init-param as follows:
init-paramTypeDescriptionDefault
cgiPathPrefixStringsubdirectory containing the cgi programsWEB-INF/cgi
debugintdebug level0 (no debug)
executableStringexecutable for running the CGI scriptperl
parameterEncodingStringencoding use for parameterSystem.getProperty("file.encoding", "UTF-8")
passShellEnvironmentbooleanwhether to pass environment properties to CGI programfalse

CGI with native executables

GlassFish v3 CGI can work with native executables as follows:

  • set the init-param with name executable to be the empty String in web.xml
  • has exploded directory structure for the war in a directory, say /export/cgitest
  • make sure those executables has the executable bits set correctly
  • deploy the "directory" (not the "war"), for instance
    asadmin deploy /export/cgitest
Note that one works with the exploded directory structure rather than war file as the executable bits information is lost during the process of jar and unjar.

Like this post? del.icio.us | furl | slashdot | technorati | digg

Server Side Include in GlassFish

Friday Jul 11, 2008

Server Side Include (SSI) allows including dynamic contents in html. SSI and CGI were very popular before the the appearance of JSP and PHP. The SSI code in GlassFish workspace is based on Tomcat. In GlassFish v3, SSI will be a supported feature. Let us look at a very simple example.

Create a SSI file

In our example, we create a index.shtml which
  • includes the content of header.html,
  • prints a Hello message with server side timestamp, and
  • executes a command say, uname (or any command in your operating system).
The page is as follows:

    <!--#include virtual="header.html"-->
    <br>Hello, it is <!--#echo var="DATE_LOCAL"-->.
    <br>Result: <!--#exec cmd="uname"-->

Note that the extension shtml is configurable in web.xml (see servlet-mapping below).

Enable SSI processing

The SSI processing can be enabled in a war file by adding SSIServlet et al in web.xml as follows:

  <web-app>
    <servlet>
      <servlet-name>ssi</servlet-name>
      <servlet-class>org.apache.catalina.ssi.SSIServlet</servlet-class>
    </servlet>

    <servlet-mapping>
      <servlet-name>ssi</servlet-name>
      <url-pattern>*.shtml</url-pattern>
    </servlet-mapping>

    <mime-mapping>
      <extension>shtml</extension>
      <mime-type>text/html</mime-type>
    </mime-mapping>
  </web-app>

One can find more details about the configuration of SSIServlet in the section below. Alternatively, one can enable SSI by uncommenting the corresponding sections in default-web.xml.

Note that the mime-mapping is to notify the browser that the result of shtml file is of content-type: text/html. If you don't specify this, then GlassFish will try to get the mime-type from default-web.xml or the default in the system.

Configuration of SSIServlet

One can configure SSIServlet by specifying the init-param as follows:

init-paramTypeDescriptionDefault
bufferedboolean (or String converted to boolean)whether the output should be bufferedfalse
debugintrepresents debug level0 (no debug)
expiresLongexpiration time in secondsdo not set "Expires" header in Http Response
inputEncodingStringencoding for SSI input if there is no URL content encoding specifiedserver platform encoding
isVirtualWebappRelativeboolean (or String converted to boolean)whether the "virtual" path of "#include" directive is relative to content-rootfalse (means relative to the given SSI file)
outputEncodingStringencoding for SSI outputUTF-8

Like this post? del.icio.us | furl | slashdot | technorati | digg

Java API for Bayeux Protocol

Friday May 02, 2008

In Cometd environment, one communicates through Bayeux Protocol. The protocol is currently in 1.0 draft 1. GlassFish v3 has incorporated implementation of Bayeux from Grizzly. Jean Francois already has several good blogs on Cometd on Grizzly. In this blog, we are going to illustrate how to send a Bayeux message to a Cometd client by using Java API without writing any JSON code.

Basic set up

Download GlassFish v3 from GlassFish website. And add the following property to your http-listener in domain.xml as follows:

    <property name="cometSupport" value="true"/>

Start the server by "one" of the following:

  1. asadmin start-domain domain1
  2. java -jar glassfish-10.0-SNAPSHOT.jar

One also need a Cometd web application. In this blog, we will use the grizzly-cometd-echo sample. Just download the war file and deploy as follows:

    asadmin deploy grizzly-cometd-echo-1.7.3.2.war

Generate a Bayeux Message

In our example, we will generate a cometd message in a servlet, TestServlet.java. The cometdmsgtest.war file and source codes are available here.

  • As usual, one can get a CometContext as follows:

        CometEngine engine = CometEngine.getEngine();
        CometContext context = engine.getCometContext(contextPath);

    In our case, the contextPath is "/cometd/cometd" where the first "/cometd" is context root of the grizzly-cometd-echo.

  • One construct a Bayeux response message by using classes in package com.sun.grizzly.cometd.bayeux. The classes that we need to use are DeliverResponse and Data. It is constructed as follows:

        Map map = new HashMap();
        map.put(messageDataName, messageDataValue);
        Data data = new Data();
        data.setMapData(map);

        DeliverResponse deliverResponse = new DeliverResponse();
        deliverResponse.setChannel("/service/echo");
        //deliverResponse.setClientId("");
        deliverResponse.setData(data);
        deliverResponse.setLast(true);
        deliverResponse.setFollow(true);

    Note that

    • "/service/echo" is the channel name of grizzly-cometd-echo sample.
    • If one is not using an updated version of GlassFish, then one may need to call deliverResponse.setClientId("") to workaround a bug.
    • deliverResponse.setLast(true) indicates that this is the last Bayeux message in this Http response.
    • deliverResponse.setFollow(true) indicates that this is not the first Bayeux message in this Http response. In our case, the previous message is /meta/connect.

    Then one can send the Bayeux message as follows:

        context.notify(deliverResponse);

How to run the test

  • Use browser A1 to access the grizzly sample application by http://localhost:8080/cometd. One can type a message on the text box and see that it is echoed through Bayeux protocol.
  • Repeat the above in browser A2.
  • Use browser B to access the cometd message application by http://localhost:8080/cometdmsgtest. One can type a message in the text box and see that it appears in browser A1 and browser A2.

Like this post? del.icio.us | furl | slashdot | technorati | digg

Follow up on A Simple Comet Example: Long Polling vs Http Streaming

Wednesday Apr 23, 2008

In my previous comet blog, A Simple Comet Example: Hidden Frame and Long Polling", I illustrate comet by using a simple example of two frames. While it is good for illustration, there is a limitation. If you try to use two different browsers to access the counter and click really fast, then you may notice that one of the counter may be updated and then immediately changes to blank. This is because the comet response may come before the response of the http post. This is more significant in the case of Http Streaming. In this blog, we will explain how to resolve "blank problem" and change the example to use Http Streaming instead of Long Polling.

One More Frame

The "counter blank" problem can be solved easily by extracting the post action and put it in a different frame (button.html). In other words, we only keep the display related stuff in count.html.

In this case, post request is sent from button.html, not from count.html. And hence, count.html will only be updated by JavaScript only. (In contrast with my previous blog, the count.html can also be updated by Http Response.) Now, in index.html, there will three frames as follows:

    <iframe name="hidden" src="hidden_comet" frameborder="0" height="0" width="100%"><iframe>
    <iframe name="counter" src="count.html" frameborder="0" height="70%" width="100%"><iframe>
    <iframe name="button" src="button.html" frameborder="0" height="30%" width="100%"><iframe>

The next thing we need to do is to update one line of Java code in the doPost of the servlet to redirect back to button.html rather than count.html.

    req.getRequestDispatcher("button.html").forward(req, res);

You can download the updated sample here.

Http Streaming

Http Streaming is different from Long Polling by keeping the connection (until expiration) between client and server even after it delivers the data. In general, this will perform better. With the fix in the previous section, we can modify our example easily to Http Streaming by commenting out the following

  • event.getCometContext().resumeCometHandler(this); in HiddenCometHandle.java
    In this case, the server will not resume the connection.
  • parent.hidden.location.href = "hidden_comet" in updateCount JavaScript
    In this case, the browser will not reload the hidden frame again.
I have make a comment in the source codes. One can locate the above easily.

Like this post? del.icio.us | furl | slashdot | technorati | digg

A Simple Comet Example: Hidden Frame and Long Polling

Thursday Apr 03, 2008

Recently, there is a great interest in Comet technology. One can find many interesting articles in Comet Daily. Comet allows server and client to keep a live connection for communication. This provides a mechanism for server to update clients, instead of having classical polling. In this blog, I am going to share my experience about using Comet with hidden frame and long polling in GlassFish v3 Technology Preview 2 builds. I try to make example as simple as possible to illustrate the basic interactions there. If you want to learn more about Comet, then I recommend Jean-Francois' blogs.

Set up GlassFish v3

Download GlassFish v3 Technology Preview 2, unzip the file and start the server with jvm option v3.grizzlySupport=true to enable comet.

    java -Dv3.grizzlySupport=true -jar glassfish-10.0-SNAPSHOT.jar

We need the above jvm-option in today's build. This will not be needed when comet is enabled by default.

Comet Servlet Code

The comet servlet code is adapted from grizzly sample comet-counter, which uses Ajax client. The details of our serlvet is as follows:

  1. In init(ServletConfig), one registers a context path to CometEngine,

        ServletContext context = config.getServletContext();
        contextPath = context.getContextPath() + "/hidden_comet";
        CometEngine engine = CometEngine.getEngine();
        CometContext cometContext = engine.register(contextPath);
        cometContext.setExpirationDelay(30 * 1000);

    where "/hidden_comet" is url-pattern of the comet servlet in web.xml. For testing purpose, one keeps the connection for 30 sec.

  2. In doGet(HttpServletRequest, HttpServletResponse), one looks up the CometContext and adds our CometHandler.

        CounterHandler handler = new CounterHandler();
        handler.attach(res);
        CometEngine engine = CometEngine.getEngine();
        CometContext context = engine.getCometContext(contextPath);
        context.addCometHandler(handler);

  3. In doPost(HttpServletRequest, HttpServletResponse), one increments the counter and then invokes the CometContext.notify, which will trigger the CometHandler.onEvent above.

        counter.incrementAndGet();
        CometEngine engine = CometEngine.getEngine();
        CometContext<?> context = engine.getCometContext(contextPath);
        notify(null);

    In addition, it forwards to count.html page for displaying the count.

        req.getRequestDispatcher("count.html").forward(req, res);

  4. Next, one need to have a class implementing CometHandler interface. Among methods in CometHandler, the most interesting one is onEvent(CometEvent).

        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);
            }
        }

    In our case, it writes a Javascript back to client side. This will invoke the Javascript function updateCount in count.html. The onEvent also invokes resumeCometHandler. This is necessary as the polling connection will be dropped once it is used.

  5. To compile the above Java code, one needs to include javax.javaee*.jar and grizzly-comet*.jar in classpath.

Client Code

On client side, I will illustrate the technique of hidden frame. Basically, the main page will have at least two frames. One of them does the long polling and is hidden from user. In our case, the index.html consists two frames as follows:

    <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>

The first frame, which is hidden, is pointed to our Comet Servlet above through GET method. The second frame is to display the counter and submit button for incrementing the counter. The Javascript in count.html is very simple as follows:

    <script type='text/javascript'>
        function updateCount(c) {
            document.getElementById('count').innerHTML = c;
            parent.hidden.location.href = "hidden_comet";
         };
    </script>

How does it work?

One can download the sources and war file from here, and deploy the war file. Using two browsers to access http://localhost:8080/grizzly-comet-hidden/index.html and click on "Click" button on each browser separately. Then one sees that counts in both browsers will be updated whenever one clicks on one of them. The mechanism is outlined as follows:

  1. When the user accesses index.html, a browser will load two frames:
    • The "hidden" frame accesses our Comet Servlet through GET method. This allows the client to start long polling with server.
    • The "counter" frame loads a static count.html.
  2. When the user clicks on the button in count.html, it submits a POST request to our Comet Servlet. This triggers the CometHandler onEvent method and redirects back to count.html to display the count. The onEvent triggers the updateCount() JavaScript in "counter" frame, which will
    • update the count and
    • invoke the Comet Servlet doGet for long polling in "hidden" frame,

Like this post? del.icio.us | furl | slashdot | technorati | digg

Comparison of Security features in GlassFish and SJSAS 8.x EE

Friday May 25, 2007

Security is very essential, especially in the enterprise environment. In this blog, we will compare security of Profiles in GlassFish (GF) v2 and also note those feature availability in Sun Java System Application Server (SJSAS) 8.x Enterprise Edition. Note that Enterprise Profile is not available in public yet and will be in beta around July 2007. More information on Profiles in GlassFish v2 can be found here.

Comparison of Security with GF and SJSAS 8.x EE
FeatureGlassFish SJSAS 8.x EE
v1v2 Development Profilev2 Cluster Profile v2 Enterprise Profile
Support JSR 196 noyesno
KeyStore for SSL JKS NSS
Key/Certificate management tools keytool certutil, pk12util, modutil
Java Security Manageroff (default)on (default)on
Support JDBCRealm yesno
SingleSignOn (SSO)disable (default)enable (default)
Virtual Server Realms noyesno

With JDK 1.5 and NSS 3.11.4, Enterprise Profile in GlassFish v2 and SJSAS 8.x EE (but not available in GF v2 Cluster Profile) support the following:

  • management of the PKCS#11 modules using modutil
  • explicit reference of keys in PKCS#11 providers for https or iiop/SSL listeners. (Note that with JDK 1.5 or later, one can add PKCS#11 providers to a given JDK. But those keys cannot be references by current server.)
  • Elliptic Curve algorithm for SSL and other crypto operations (need Enterprise Profile GlassFish v2 and JDK 1.6)

In SJSAS 8.2 EE and the coming GlassFish v2 Enterprise Profile, there is support for the use of private key in Solaris 10 Softtoken. As an example, let us take a look at how to set up Solaris 10 Softtoken.

  1. Initialize Solaris 10 Softtoken password if you have not.

    /bin/pktool setpin

  2. Register the Solaris 10 Softtoken to NSS.

    modutil -dbdir $SJSAS_HOME/domains/domain1/config -force -add "Solaris 10 Softtoken" -libfile /usr/lib/libpkcs11.so -mechanisms RSA:DSA

  3. Verify that the token is added properly and find out the corresponding token name.

    modutil -dbdir $SJSAS_HOME/domains/domain1/config -list

    A sample output is as follows:

    Using database directory ....
    
    Listing of PKCS #11 Modules
    -----------------------------------------------------------
      1. NSS Internal PKCS #11 Module
             slots: 2 slots attached
            status: loaded
    
             slot: NSS Internal Cryptographic Services                            
            token: NSS Generic Crypto Services
    
             slot: NSS User Private Key and Certificate Services                  
            token: NSS Certificate DB
    
      2. Solaris 10 Softtoken
            library name: /usr/lib/libpkcs11.so
             slots: 1 slot attached
            status: loaded
    
             slot: Sun Crypto Softtoken
            token: Sun Software PKCS#11 softtoken
    -----------------------------------------------------------
      

    In this case, the token name is "Sun Software PKCS#11 softtoken". And this will be used in subsequent commands.

  4. Create a private key and certificate in Solaris 10 Softtoken.

    certutil -S -x -n mytestcert -t "u,u,u" -v 120 -s "cn=j2ee,ou=J2EE,o=Sun,L=Santa Clara,ST=California,C=US" -d $SJSAS_HOME/domains/domain1/config -h "Sun Software PKCS#11 softtoken"

    A sample output is as follows:

    Enter Password or Pin for "Sun Software PKCS#11 softtoken":
    
    A random seed must be generated that will be used in the
    creation of your key.  One of the easiest ways to create a
    random seed is to use the timing of keystrokes on a keyboard.
    
    To begin, type keys on the keyboard until this progress meter
    is full.  DO NOT USE THE AUTOREPEAT FUNCTION ON YOUR KEYBOARD!
    
    
    Continue typing until the progress meter is full:
    
    |************************************************************|
    
    Finished.  Press enter to continue: 
    
    
    Generating key.  This may take a few moments...
      
  5. Change the cert-nickname to "Sun Software PKCS#11 softtoken:mytestcert" in your listeners.

  6. Restart the server, then it will prompt the password for Solaris 10 Softtoken as follows:

    Please enter password for NSS slot Sun Software PKCS#11 softtoken>

Like this post? del.icio.us | furl | slashdot | technorati | digg

JDBCRealm in GlassFish with MySQL

Monday Apr 23, 2007

In these few months, there were several discussions of using GlassFish JDBCRealm with MySQL. In this blog, I will share my experience about using GlassFish JDBCRealm with MySQL.

  1. Download the MySQL Community Server. I have downloaded the Solaris 10 (x86, 32 bit TAR package), version 5.0.37, of the "MySQL Community Server".

  2. Expand the download file.

    gunzip mysql-5.0.37-solaris10-i386.tar.gz
    tar xf mysql-5.0.37-solaris10-i386.tar

  3. cd mysql-5.0.37-solaris10-i386

    and read INSTALL-BINARY.

  4. Set up the grant table.

    scripts/mysql_install_db

  5. Start the MySQL server.

    bin/mysqld_safe

  6. Set a password for the MySQL "root" user

    bin/mysqladmin -u root password YOUR_PASSWORD

  7. Create database and table. The following is a sample command.
    bin/mysql -u root --password=YOUR_PASSWORD
    
    create database database01;
    use database01;
    create table usertable(userid varchar(10) primary key, password varchar(32) not null);
    create table grouptable(userid varchar(10), groupid varchar(20) not null, primary key (userid, groupid));
    alter table grouptable add constraint FK_USERID foreign key(userid) references usertable(userid);
    commit;
    grant all privileges on *.* to 'root'@'YOUR_HOST' identified by 'YOUR_PASSWORD' with grant option;
    
    Note that you may like to replace YOUR_PASSWORD and YOUR_HOST in above.

  8. Populate user, group and passwor data. For the purpose of testing the database, you may try to use clear text password first as follows:
    insert into usertable values ('webuser', 'webuser');
    insert into grouptable values ('webuser', 'employee');
    
    For MD5, please take a look at another blog on JDBCRealm.

  9. Download the JDBC driver from Connectors > Connector/J . I have downloaded mysql-connector-java-5.0.5-bin.zip

  10. Unpack the package and copy the JDBC driver to $GLASS_HOME/lib.

    unzip mysql-connector-java-5.0.5-bin.jar
    cd mysql-connector-java-5.0.5
    cp mysql-connector-java-5.0.5-bin.jar $GLASSFISH_HOME/lib

  11. Restart the GlassFish server in order to pick up the JBDC driver.

  12. Create a Connector pool in Admin Console as follows:

    NameMySQLPool
    Resource Typejavax.sql.DataSource
    Database VendorMySQL

    then click "Next" and add the following properties:

    serverNameYOUR_HOST
    port3306
    databaseNamedatabase01
    userroot
    passwordYOUR_PASSWORD

    Note that different versions of the JDBC driver may have different properties. You may need to check the readme file there. Furthermore, you may need to remove extra default properties from Admin Console.

  13. Create a DataSource jdbc/mysql associated with the above pool.

  14. Create a JDBCRealm, named jdbcrealm with the following properties:

    datasource-jndijdbc/mysql
    user-tableusertable
    user-name-columnuserid
    password-name-columnpassword
    group-tablegrouptable
    group-name-columngroupid
    jaas-contextjdbcRealm
    digest-algorithmnone

    Note that if you are using MD5 for password data, then you need to set value of digest-algorithm to MD5.

  15. Now a JDBCRealm is ready and it can be used by specifying it in deployment descriptors. If there is anything wrong and cannot authenticate, then one can turn on security log to FINE level and check if there is any exception in server.log.

Like this post? del.icio.us | furl | slashdot | technorati | digg

Multiple Private Keys in a GlassFish domain

Friday Apr 06, 2007

GlassFish uses Java JKS for storing keys and certificates. Out of the box, the keyStore (keystore.jks) and the trustStore (cacerts.jks) reside in $GLASSFISH_HOME/domains/domain1. Even though there are several CA root certificates in cacerts.jks, there is only one private key in keystore.jks.

GlassFish supports the use of multiple private keys in a given domains. For instance, you may have two https listeners having different server private keys. This is a very useful scenario especially when one have EC key. So, in a given domain, we can have one https listener using RSA key for normal browser and one https listener using EC key for PDA.

In this blog, we will discuss the configuration when there are multiple private keys in a given domain of GlassFish. In this case, one needs to specify the private key / certificate to be used for SSL communication. If the information is not specified, then the server will pick up one which may not be desirable. Since one wants to be more precise in security environment, one would like to specify the corresponding certificate nickname in order to pick up the correct key.

There are two kinds of certificate nicknames: inbound, https outbound.

Inbound Certificate Nickname

One needs to specify the inbound cert-nickname for a given listener in domain.xml. For instance, in http listener, it is as follows:

    <http-listener ... security-enabled="true" ... />
      <ssl cert-nickname="s1as" ... />
      ...

Instead of hand-crafting the domain.xml, it would be a good idea to use Admin Console as follows: Configuration > HTTP Services > Http listeners > http-listener-2, and choose SSL tab and enter the valid alias value you want in "Certificate Nickname" textbox. Then one needs to restart the given domain (if there is a change of certificate nickname) in order to activate the change.

Similarly for iiop listeners.

Https Outbound Certificate Nickname

GlassFish also supports the https outbound from server. A private key / certificate is used for https outbound mutual SSL authentication. In this case, we can specify the https outbound certificate nickname as jvm-options in domain.xml:

    -Dcom.sun.enterprise.security.httpsOutboundKeyAlias=YOUR_ALIAS

One can achieve this through Admin Console as follows: Application Server > JVM Settings > JVM Options > Add JVM option, and enter the above jvm option in the new textbox. Then one needs to restart the server in order to activate this change.

Like this post? del.icio.us | furl | slashdot | technorati | digg

Browsing 196 AuthConfigProviders with AJAX

Friday Mar 23, 2007

JSR 196, Java Authentication SPI for Containers, defines SPI for providers plugging into containers for message authentication. It is currently under Proposed Final Draft (PFD) and GlassFish v2 (b40 rc or above) has an implementation of PFD of this JSR. On the other hand, AJAX is another new and exciting technology in the Web 2.0 area. In this blog, I will share my experience with you about constructing a web tool to browse 196 AuthConfigProviders registered in GlassFish by using AJAX tree.

I find that it is very helpful to use jMaki plugin in NetBeans 5.5.1. A jMaki widget can be created in a jsp application by simply drag-and-drop. A very good tutorial can be found in jMaki NetBeans 5.5 screencast.

In the following, I will outline steps to create my web application.

  1. Create a "Web Applicatons" project with name listacp using NetBeans as described in above screencast.
  2. Open index.jsp if it is not already open. Then drag and drop a "jMaki Yahoo Tree" from the left Palette to the jsp. And the following code will be generated in the jsp:

      <a:ajax name="yahoo.tree"/>

    This will create a AJAX Tree with the default data.

  3. We would like to construct the tree using data in JSON format from GlassFish server. In our case, the data is coming from acpdata.jsp. We need to modify the above as follows:

      <a:ajax name="yahoo.tree" service="/acpdata.jsp"/>

  4. In acpdata.jsp, we need to get a complete list of AuthConfigProvider. This can achieved by getting a complete list of registration ID first.

      AuthConfigFactory factory = AuthConfigFactory.getFactory();
      if (factory != null) {
        String[] regisIDs = factory.getRegistrationIDs(null);

    And for each registration ID, we can get RegistrationContext and AuthConfigProvider as follows:

      RegistrationContext regContext = factory.getRegistrationContext(regisID);
      if (regContext != null) {
        String layer = regContext.getMessageLayer();
        String appContext = regContext.getAppContext();
        AuthConfigProvider provider = factory.getConfigProvider(layer, appContext, null);

  5. Then we need to output the data in corresponding JSON format. An example of the format from corresponding source of the Yahoo Tree Widget can be found in jMaki Widget Gallery. For instance, in our case, acpdata.jsp outputs the data as

      { root: {
        title: '196 factory: com.sun.enterprise.security.jmac.config.GFAuthConfigFactory',
        expanded: 'true',
        children: [
        {
          title: 'registrationID = __2SOAP',
          expanded: 'false',
          children: [
            { title: 'messageLayer = SOAP'},
            { title: 'appContext = null'},
            { title: 'description = WSIT AuthConfigProvider'},
            { title: 'persistent = false'},
            { title: 'provider = com.sun.xml.wss.provider.wsit.WSITAuthConfigProvider@14a8f44'}
          ]
        }
        ,
      ...

  6. Protect the web application
    Since the list of AuthConfigProviders will reveal what has been deployed in the given GlassFish installation, we would like to protect this web application so that only users of asadmin group of admin-realm can access it. We can achieve this by:

    Adding security setting in web.xml through NetBeans by navigating the Web application on panel of NetBeans: Web Pages > WEB-INF, open web.xml, and click Security, and modifying it as follows:
      Login Configuration: Basic
      Realm Name: admin-realm

      Add Security Roles: admin

      Add Security Constraint
        Resource Name: secure resource
        URL Pattern(s): /*

      Click on "Enable Security Constraint"
        Role Names: admin

    Then, adding the security-role-mapping to sun-web.xml through "Edit As XML" mode:

      <security-role-mapping>
        <role-name>admin</role-name>
        <principal-name>admin</principal-name>
        <group-name>asadmin</group-name>
      </security-role-mapping>

  7. If we package the war file and deploy, then we can access

      http://your_host:your_port/listacp

    Enter your admin username and password. Everything works! The only drawback is the war file size is too big right now. In order to make it smaller, we have to remove unused AJAX libraries manually at this moment. (Try your best effort!) I hope the tool will only put in the required scripts in the future.

One can download my sample AJAX 196 AuthConfigProvider browser here. Note that we need to use a browser supporting AJAX (Firefox, Internet Explorer et al, not Mozilla 1.x) in order to run this example.

Like this post? del.icio.us | furl | slashdot | technorati | digg