Wednesday Apr 23, 2008

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

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

Thursday Apr 03, 2008

A Simple Comet Example: Hidden Frame and Long Polling

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();
        CometEngine engine = CometEngine.getEngine();
        CometContext context = engine.getCometContext(contextPath);

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

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

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

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

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,

Note: There is a threading issue in the above sample code. I have posted the fix and other additional comments in another blog: Follow up on A Simple Comet Example: Long Polling vs Http Streaming.

Friday May 25, 2007

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

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

Monday Apr 23, 2007

JDBCRealm in GlassFish with MySQL

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.


  5. Start the MySQL server.


  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) not null, groupid varchar(20) not null, primary key (userid, groupid));
    alter table grouptable add constraint FK_USERID foreign key(userid) references usertable(userid);
    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

  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:

    Resource Typejavax.sql.DataSource
    Database VendorMySQL

    then click "Next" and add the following properties:


    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:


    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.

Friday Apr 06, 2007

Multiple Private Keys in a GlassFish domain

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:

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.

Friday Mar 23, 2007

Browsing 196 AuthConfigProviders with AJAX

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:',
        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:


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


    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.

Friday Mar 16, 2007

Troubleshooting JAXWS Message Level Security in GlassFish

In Java EE 5, one can implement JAXWS Web Services through servlets and Ejb endpoints (JSR 109). GlassFish supports message level security for Web Services. You don't need to write special client and server Java code in order to take advantages of the message level security. What you need to do is specific a corresponding message-level-security element in sun-ejb-jar.xml, sun-web.xml, and sun-application.xml. For instance,

    <message-security-binding auth-layer="SOAP" provider-id="XWS_ServerProvider">
        <request-protection auth-source="sender"/>
        <response-protection auth-source="content"/>

Or you can turn on the default in domain.xml for server side or sun-acc.xml for appclient.

This blog will highlight some troubleshootings for JAXWS message level security.

Read logs

  1. Look at client and server logs. The server log is located at $GLASSFISH_HOME/domains/your_domain/logs/server.log.
  2. If you want to see more SOAP level debug info, like corresponding SOAP messages, then you can turn on provider's debug.
    For server and embedded client, one can achieve this by navigating from admin console: Configuration > Security > Message Security > SOAP > Providers:
    • For server, choose the provider XWS_ServerProvider, change the debug property to true and save the configuration.
    • For embedded client, choose the provider XWS_ClientProvider, change the debug property to true and save the configuration.
    For appclient, one need to change the debug property to true for the provider XWS_ClientProvider, and log-service level to INFO.

Endpoint info mismatch

For message level security, one need to ensure that the same request-policy and response-policy are applied to both client and server. These info can be specified in sun-\*.xml for a given application or domain.xml and sun-acc.xml for default provider configuration (if default is on). If you see com.sun.xml.wss.impl.PolicyViolationException: Expected Signature Element as per receiver requirements or there is no security processing in SOAP message in debug log, then most probably some of the info about endpoint in sun-\*.xml is not correct. You may like to double check each port-component-name (defined in JSR 109) and endpoint-address-uri inside message-security-binding in sun-\*.xml are correct.


  • According to JSR 181, we have the following:
    @WebService.nameSimple name of the Java class or interface
    @WebService.serviceNameSimple name of the Java class + "Service"

  • In GlassFish, the URL to access WebService and endpoint-address-uri in sun-\*.xml are related as follows:
    Endpoint TypeWebService URL

  • One can also find out the port-component-name by checking the generated webservices.xml in admin console as follows: Web Services > YOUR_WEB_SERVICES > Webservices.xml, and then correct the port-component-name, repackage and redeploy your application if necessary.

Wednesday Mar 14, 2007

Password echo of appclient in GlassFish?

In SJSAS 8.x, 9.0 and GlassFish v1, when one tries to access a password protected EJB or Web Service through appclient, it will prompt user name and password if this is not specified in command line or programmatically. By default the prompt will be a GUI dialog box. In remote environment, it may be desirable to have the prompt in text mode. One can achieve this through appclient as follows:

  appclient -client <location of the client jar> -textauth

And username and password will be prompted and user's response will be as follows:

  Enter Username:javaee
  Enter Password:javaee

Note that the password is echoed in the console. This is highly undesirable from security point of view.

In GlassFish v2 with JDK 6 environment, the password is no longer echoed in text password prompt. One can switch the JDK of GlassFish server by editing the AS_JAVA in config/asenv.conf for non-Windows systems and config/asenv.bat for Windows systems. If one reruns the above appclient command, then one will have

  Enter Username:javaee
  Enter Password:

Note that the password is no longer echoed in the console.

One may ask whether one can achieve this without modifying the JDK in whole installation. In GlassFish, one can package the appclient and install it in somewhere else. The command is as follows:


This will generate a appclient.jar . Then one can copy this to the desired location and unjar the content. After that, one may need to update the following inside the unjar directories:

  • change appclient script to execution mode if necessary. The command in unix is: chmod u+x appclient.
  • update the content of asenv.conf or asenv.bat, sun-acc.xml, appclient as described in SJSAS package-appclient doc. For instance, if one want to use webservice in client, then one need to update AS_WEBSERVICE_LIB. In our case, one need to update AS_JAVA to point to a JDK 6 installation.
In this case, the GlassFish installation will still use the same JDK as before. Only the JDK of the new appclient has been updated.

Friday Aug 11, 2006

assign-groups in GlassFish Security realm

In the Java EE environment, roles are logical privileges which convey/represent permission to operate on some particular set of resources in an application. The Sun Java System Application Server environment consists of several realms, which each contain a complete database of users and groups that identify valid users of an application. When the roles are mapped to users, the users are effectively granted the permissions conveyed/represented by the privilege.

In Sun One Application Server 7.0, you can assign a role to all authenticated users in all realms at the same time, and applications can define authorization for that role. This addresses the following common scenario in the LDAP environment:

  • All authenticated users can access some subset of protected resources within the application. It doesn't matter who you are, only that your identity has been established.
  • The set of user groups is large or dynamic, but not specific to the application.

We wanted an ability to grant permission based on authentication independent of identity, in other words, we wanted a way to grant all authenticated identities to a role, or to define a role that would be understood to be mapped to all authenticated identities, and not to unauthenticated entities.

In Sun Java System Application Server 8.x and GlassFish v1, various RFEs and issues have been filed requesting support of the above scenario. Therefore, this scenario has been addressed in GlassFish v2. In GlassFish v2, the assign-groups property in CertificateRealm has been extended to FileRealm, LDAPRealm, SolarisRealm, and JDBCRealm. With this change, it is possible to configure all realms so that they assign one or more common group principals as a result of successful authentication, and such that every user is effectively made a member of the common groups. Given that this is the case, mapping one or more of the group principals to a role, either explicitly or by default, will allow the role to be used as an ANYONE role, a role that conveys/represents permission to operate on all resources which are accessible to any authenticated user. You can assign additional groups to all authenticated users in a specific realm without having to add those groups to all authenticated users in all realms. This gives us a finer control and allows us to resolve the issues with the above scenario as follows:

  • Set the assign-groups property for a given realm. All authenticated users of the given realm will assign additional groups as specified by the assign-groups property, for instance,
  • Map additional groups to desired roles in Sun-specific deployment descriptors. For instance, in sun-applicaton.xml,

Thursday Jun 15, 2006

在 Glassfish 中使用 JDBCRealm

原文:JDBCRealm in Glassfish
作者:Shing Wai Chan
译者:Cheng Fang

JDBC realm 在近几个月来颇受瞩目。这篇网志总结了 JDBC realm 在GlassFish 实施中的演变,并解释其最新的实施运作。我要感谢 Jean-Baptiste 和 Richter 对此所作的贡献和评论。开源社区成员的参与也很有帮助。我欢迎大家的反馈和参与, 帮助我们更好地实现这一性能。

GlassFish 一直都支持 realm 插件。以下这篇文章描述了如何在 Sun Java System Application Server EE 8.0 中实现 Custom Realm:Authentication Using Custom Realms in Sun Java System Application Server。S1AS 7.x 也里有一个JDBC realm 的示例。Jean-Baptiste 正式提出要求这一性能, 并为 Glassfish 提供了一个以明文的方式实现的 JDBCRealm。Richter 也提供了另一个实 施方案,因为当时GlassFish 中的 JDBCRealm 与Tomcat 不兼容。

GlassFish 中最新的 JDBCRealm 是基于Jean-Baptiste 的实施,并包含了以下一些改动:

  • 增加 Message Digest 配置
  • 增加对 Digest 的 encoding 配置
  • 增加 charset 配置
  • 增加 realm 中数据库用户和密码配置
  • 解决为 EJB 获取连接的问题

在 GlassFish 中设计、实施 JDBCRealm 的过程中, 我们作出了以下一些决定:

  • 应该不应该支持明文密码? 我们决定支持明文密码,但不作为缺省存贮机制。在正常情况下, 密码不应该被明文地存放。
  • 是不是应该与 Tomcat 和 JBoss 兼容?  我们决定在实施中尽量做到兼容, 但不作为一个要求。


GlassFish JDBCRealm

下表概括了 GlassFish JDBCRealm 中可配置的属性:

属性 是否必须 缺省 备注
  和 Tomcat 一样, 我们假设 user-table 表和 group-table 表有这一栏
沿用连接池配置 如果连接池没有用户名或密码, 在这里定义db-user和db-password。这能够让我们指定一个安 全的资源,realm 可以通过用户名和密码来访问。
MD5 支持 JDK 中所有算法, 和 "none"
如果有 digest-algorithm, 就用 Hex。如果没有定义 digest, 则不用 encoding。
Hex, Base64, 不指定(没有 encoding)
  针对 digest 的 charset 

GlassFish 中的一个安全性能就是,你可以在 JDBCRealm 中, 而不是在连接池中,指定用户名和密码。这样, 其它应用就无法查找 datasource,得到连接来浏览用户名和密码表。

怎么在JavaEE 应用中使用JDBCRealm ?

  1. 为 JDBCRealm 创建数据库表。
    例如, 如果您使用 Derby, 您会创建如以下示例的数据库表:
    create table usertable(userid varchar(10) not null, password varchar(32) not null, primary key(userid));
    create table grouptable(userid varchar(10) not null, groupid varchar(20) not null, primary key(userid, groupid));
    alter table grouptable add constraint FK_USERID foreign key(userid) references usertable(userid);
  2. 通过Admin 控制台创建 JDBCRealm。
    例如, 你可以创建一个有以下属性的 JDBCRealm:
    Property Name Property Value
    datasource-jndi jdbc/security
    user-table usertable
    user-name-column userid
    password-column password
    group-table grouptable
    group-name-column groupid
    jaas-context jdbcRealm
    jdbc/security 是一个可以访问以上表格的 datasource。
  3. 指定 JDBCRealm 是当前应用使用的 realm。这在配置描述器里指明: sun-application.xml (对于 EAR) 或, web.xml (对于 WAR) 或 sun-ejb-jar.xml (对于 EJB Jar) 。
  4. 确认 sun-\*.xml 文件里有 <security-role-mapping>。 例如,
  5. 在数据库中创建一名用户。没有为 JDBCRealm 创造用户的管理工具。以下是一个用来创建 JDBCRealm 用户程序例子:
    private static final String userSql = "insert into usertable values(?, ?)";
    private static final String groupSql = "insert into grouptable values(?, ?)";

    private static String hashPassword(String password) throws Exception {
    //compute digest
    //encode the bytes

    public static void main(String args[]) throws Exception {
    String hPassword = hashPassword(password);
    Connection conn = DriverManager.getConnection(
    jdbcUrl, dbUser, dbPassword);
    PreparedStatement userStmt = conn.prepareStatement(userSql);
    userStmt.setString(1, user); userStmt.setString(2, hPassword);
      可以在这 里找到这个例子。当运行这一程序时, 要确认 JDBC 驱动在 CLASSPATH 里,例如, $GLASSFISH_HOME/javadb/lib/derbyclient.jar


从 Tomcat 或者 JBoss 迁移到 GlassFish 时,应考虑到以下一些不同的地方:

  • 在 Tomcat 和 JBoss, 没有缺省值。这对应于 GlassFish 中的 digest-algorithm="none"。
  • 在GlassFish 和Tomcat 中, 如果有 digest 被定义,那么 encoding 缺省值就是 Hex。在 JBoss 中, 如果有 digest 被定义,那么 encoding 缺省值就是 Base64。

註 : 根据意見, 我更新了数据库grouptable的SQL。

Thursday Jun 08, 2006

JDBCRealm in GlassFish

JDBC realm has a lot of attention in recent months. This blog summarizes the evolution of the JDBC realm implementation in GlassFish and explains how the latest implementation works. I would like to thank Jean-Baptiste, and Richter for their contributions and comments. The participation from the open source community definitely helps everyone. I encourage all of you to give feedback, participate, and help evolve this feature further.

GlassFish always had the capability for anyone to plug-in a realm. Implementing a custom realm in the Sun Java System Application Server EE 8.0 is described in the article Authentication Using Custom Realms in Sun Java System Application Server. In S1AS 7.x, there is a JDBC Realm bundled in sample. Jean-Baptiste formally filed an enhancement and provided a clear text version of JDBCRealm for GlassFish. Richter wrote another implementation because the GlassFish JDBCRealm at that time not compatible with Tomcat.

The latest GlassFish implemtation started with the Jean-Baptiste's implementation and includes the following modifications:

  • add message digest configuration
  • add encoding configuration for digest
  • add charset configuration
  • add database user and password configuration in realm
  • fix the issue with acquiring a connection for EJB

For the GlassFish implementation of the JDBCRealm, the following design descisions were made:

  • Should clear text passwords be supported? The decision was made to not to have clear text passwords as default storage mechanism. Under normal circumstances, passwords should not be stored as clear text.
  • Should this implementation be compatible with Tomcat and JBoss? The decision was made to attempt to implement this compatibility, but not require this compatibility.

I would appreciate hearing from you on both of these topics.

GlassFish JDBCRealm

The following table summarizes configurable properties for GlassFish JDBCRealm:

user-name-columnY as in Tomcat, we assume that both user-table and group-table has this column
jaas-contextY jdbcRealm
db-userNfrom connection pool configurationIf the connection pool doesn't have username or password, define the db-user and db-password here. This allows us to specify a secure resource that is accessible by an realm with user and password specified.
digest-algorithmNMD5support all algorithms in JDK, and also "none"
encodingNHex if there is a digest-algorithm,
no encoding if digest is not defined
Hex, Base64, not specified (no encoding)
charsetN charset for digest

In GlassFish, a security feature allows you to specify a username and password in the JDBCRealm instead of in the connection pool. By doing this, other applications cannot look up the datasource and get a connection to browse the user-name, password database tables.

How to use JDBCRealm in a JavaEE application?

  1. Create database tables for the JDBCRealm.
    For example, if you are using Derby, you would create database tables as shown in the following code example:
    create table usertable(userid varchar(10) not null, password varchar(32) not null, primary key(userid));
    create table grouptable(userid varchar(10) not null, groupid varchar(20) not null, primary key(userid, groupid));
    alter table grouptable add constraint FK_USERID foreign key(userid) references usertable(userid);
  2. Create a JDBCRealm using the Admin Console.
    For instance, one can create a JDBCRealm with the following properties:
    Property NameProperty Value
    where jdbc/security is a datasource can access the tables above.
  3. Specify that the JDBCRealm is the realm to be used as the realm for the application. This is specified in the deployment descriptor: sun-application.xml (for EAR) or, web.xml (for WAR) or sun-ejb-jar.xml (for EJB JAR).
  4. Make sure that you have <security-role-mapping> in sun-\*.xml. For instance,
  5. Create a user in database. There is no administration tools for creating users for JDBCRealm. A snapshot of a program that can be used to create users in JDBCRealm is as follows:
        private static final String userSql = "insert into usertable values(?, ?)";
        private static final String groupSql = "insert into grouptable values(?, ?)";
        private static String hashPassword(String password) throws Exception {
            //compute digest
            //encode the bytes
        public static void main(String args[]) throws Exception {
            String hPassword = hashPassword(password);
            Connection conn = DriverManager.getConnection(
                jdbcUrl, dbUser, dbPassword);
            PreparedStatement userStmt = conn.prepareStatement(userSql);
            userStmt.setString(1, user); userStmt.setString(2, hPassword);
    The sample program can be found in here. When we run the program, please make sure that the JDBC driver is included in CLASSPATH, for instance, $GLASSFISH_HOME/javadb/lib/derbyclient.jar

Notes on Migration

Consider the following differnces when migrating applications from Tomcat and/or JBoss to GlassFish:

  • In Tomcat and JBoss, there is no default. This corresponds to digest-algorithm="none" in GlassFish.
  • In GlassFish and Tomcat, the default encoding is Hex if there is a digest defined. In JBoss, the default encoding is Base64 if there is a digest defined.

Note: Based on comments, I have updated the SQL for creating grouptable.

Friday May 12, 2006

Glassfish with ECC

As computer hardware is getting more and more powerful, there is a corresponding need to increase the encryption strength of the key in cryptographic operations. There are several ways in which this can be accomplished:
  • Increase the length of the encryption key. This may negatively impact performance.
  • Use a different encryption algorithm, for example, Elliptic Curve Cryptography (ECC). In next-generation key technology, RSA will be 2048 bits and ECC will be 224 bits. Note that these two type of keys have the same ecnryption strength.

In this blog, I will summarize the steps needed for using HTTPS with ECC with the following configuration:

The following discussion assumes that GlassFish is installed in c:\\export\\glassfish, JDK 6 is installed in c:\\jdk6 and the NSS binaries are located in c:\\nss\\lib.
  • Configure your operation environment to run the GlassFish, JDK 6, and NSS.
    • For example in Windows, set the path variabled, as shown here:
        set path=c:\\jdk6\\bin;c:\\nss\\lib;c:\\export\\glassfish\\bin;%path%
    • For example, in Unix ksh, export these environment variables:
        export PATH=/jdk6/bin:/export/glassfish/$PATH
        export LD_LIBRARY_PATH=/nss/lib:$LD_LIBRARY_PATH
  • Create a provider configuration for NSS in c:\\ecc\\nss.cfg as follows:
  • Add the NSS provider to the JDK 6 configuration by adding the following line in the file c:\\jdk6\\jre\\lib\\security\\ : c:\\\\ecc\\\\nss.cfg
  • Create an ECC key in JKS keystore using keytool, where ${HOST} is hostname of your machine.
      cd \\export\\glassfish\\domains\\domain1\\config
      keytool -genkeypair -alias myecc -keyalg EC -keysize 224 -keystore keystore.jks -storepass changeit -dname "CN=${HOST}, OU=ECC Test 224, O=GlassFish" -keypass changeit
  • Update the version of the JDK used by GlassFish by updating the value of AS_JAVA in the file c:\\export\\glassfish\\config\\asenv.bat.
  • Start the GlassFish server if necessary.
      asadmin start-domain domain1
  • Update the certificate nickname of the HTTP listenser http-listener-2 to myecc using the Admin Console, which can be accessed by entering the following URL in your browser: http://serverName:4848

  • Stop and then restart the GlassFish server in order for the changes to become effective
      asadmin stop-domain domain1
      asadmin start-domain domain1
  • To access the HTTPS with ECC, install the latest versions of web browsers, as only the latest versions have support for ECC. The latest versions can be found in here.

    If you try access https://serverName:8181 using your existing browser, you will see an error message like this:
      cannot communicate securely because they have no common encryption algorithms.

  • Configure the latest version of the browser to use an ECC algorithm, for example, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, by following these steps:
    • Open your web browser.
    • Enter the following URL in the browser: accessing about:config.
    • In this window, set Filter to security.
    • Select and double click ssl3.ecdh_ecdsa_aes_128_sha

Now, you can access https://serverName:8181, the browser will prompt for accepting the certificate and you can verify that this is the ECC certificate that you just created.

A preliminary benchmark of HTTPS with ECC in GlassFish on the Windows XP platform shows that the performance of ECC is double that of RSA in next generation-key technology.

More details on using JDK 6 with NSS can be found in Andreas's blog: Elliptic Curve Cryptography in Java.

Saturday Apr 08, 2006

Enterprise Java Bean over SSL

Enterprise Java Bean provides a component based architecture for distributed business application. Security is very important in the enterprise environment. SSL/TLS provides security at the transport layer to meet the security requirement in an enterprise environment. In this blog, we discuss how to configure SSL for use with enterprise beans and how to access enterprise bean from a client.


  • In the Java EE 5 SDK, Glassfish and the Sun Java System Application Server (SJSAS), the keystore password and key passwords are the same, and the keystore and truststore passwords are the same for a given domain.
  • The SJSAS EE uses NSS for keystore management. The SJSAS PE uses JKS keystores for keystore management. The application client containers, however, use JKS keystores for keystore management regardless of whether the Application Server is EE or PE.

Running Enterprise Beans over SSL

In SSL/TLS, there are two kinds of authentication: SSL server authentication and SSL mutual authentication. To specify SSL/TLS for an enterprise application, use the <transport-config> subelement of the corresponding <ejb> element in the runtime deployment descriptor, sun-ejb-jar.xml.

The two options are specified in slightly different ways, as shown in the following examples:

  • SSL: Server Authentication

    In this case, the client verifies the identity of the server by checking its certificate in the truststore. When using sever authentication, make sure that the truststore of the client trusts the certificate of the server. To do SSL server authentication, set the integrity element and confidentiality element to required. For instance,


  • SSL Mutual authentication

    In this case, both the client and the server verify the identity of each other by checking certificates in mutual truststores. When using mutual authentication, make sure that the truststore of the client trusts the certificate of the server and the truststore of the server trusts the certificate of the client. To do SSL mutual authentication, set the integrity element, the confidentiality element, and the establish-trust-in-client to required. For instance,



    Using SSL with the Applicaton Client Container

    To use SSL with the Application Client Container (ACC), you need to set VMARGS environment variable.
  • Set environment variable VMARGS in shell.

    For example, in ksh or bash shell the command to set this environment variable would be:
    export VMARGS="${keystore.db.file}${truststore.db.file} word=${ssl.password}${ssl.password}"

  • Set the env element in the ant script. For instance,
    <target name="runclient">
      <exec executable="${S1AS_HOME}/bin/appclient">
        <env key="VMARGS" value="${keystore.db.file}${truststore.db.file}${ssl.password}${ssl.password}"/>
        <arg value="-client"/>
        <arg value="${appClient.jar}"/>

    Using SSL with Standalone Client

    When the application client, the enterprise bean is looked up using the ejb-ref-name element, as shown in the following example sun-application-client.xml:

    When using a standalone client, however, we use JNDI name for the lookup. So, in the standalone client class, we have:
        context = new InitialContext();
        obj = context.lookup("SSLConverter");

    To run the standalone client, make sure your classpath contains the following:

    • appserv-rt.jar, this will have an implementation ORB with the implementation of CSIv2
    • j2ee.jar in SJSAS 8.x or javaee.jar in Glassfish
    • the interfaces class of the corresponding EJBs and other library classes needed

    The following ant target provides an example of how to configure an ant target for running over SSL:
    <target name="run-standalone-client">
      <java classname="${test.client}"
          classpath="${test.classpath} failonerror="true" fork="true">
        <jvmarg value="-Dorg.omg.CORBA.ORBInitialHost=${}"/>
        <jvmarg value="-Dorg.omg.CORBA.ORBInitialPort=${orb.port}"/>
        <jvmarg value="${keystore.db.file}"/>
        <jvmarg value="${ssl.password}"/>
        <jvmarg value="${truststore.db.file}"/>
        <jvmarg value="${ssl.password}"/>

    Debugging SSL Communication

    To enable debug messages for SSL communication, pass the jvm option, which will show all the information during SSL communications.
  • Monday Jan 23, 2006

    Troubleshooting Deployment in Glassfish

    In Java EE 5, there are many new and exciting features. For example, developers can specify annotations in Java files instead of putting metadata in deployment descriptors. In some cases, applications can be completely free of deployment descriptors. This simplifies the development of Jave EE applications. This blog describes two common troubleshooting scenarios for deployment in GlassFish.

    1. Runtime error due to annotations not correctly processed (if at all) during deployment.
      In this case, even though one may go through deployment, there will be a runtime error. For instance, suppose you have the following resource injection
          private @Resource(name="jdbc/__default") DataSource ds;
      then, you will have a NullPointerException when you try to access the ds. Verify that the annotation is not processed by doing the following:

      • turn on deployment log to FINE in the domain during deployment time
        In this case, you will see a FINE message in server.log as follows:

            Annotation is not processed for this archive.


      • Make sure that standard deployment descriptor files, such as ejb-jar.xml, web.xml, application-client.xml refer to the correct version of the XML schemas in the ear/war/jar files before deployment as the Java EE platform spec requires that annotations are only processed if the deployment descriptor uses the latest schema.
        Deployment DescriptorVersion of XSD
        ejb-jar.xml ejb-jar_3_0.xsd
        web.xml web-app_2_5.xsd
        application-client.xml application-client_5.xsd

      • If the descriptors mentioned above refer to the correct schema versions, then please make sure that the full attribute is "false" or not specified there as the Java EE platform spec indicates that annotations are only processed if the deployment descriptors using the latest schema have the full attribute either specified as "false" or not specified. For instance,

        <ejb-jar xmlns="" xmlns:xsi="" full="false" version="3.0" xsi:schemaLocation="">

        <ejb-jar xmlns="" xmlns:xsi="" version="3.0" xsi:schemaLocation="">

      • If the above are correct and the annotation in application client is still not processed, then please make sure that the application client jar file has a MANIFEST.MF containing a Main-Class entry pointing to the fully qualified name of the app client main class. Note that, in general, one can specify the application client main class as an appclient command line argument. But in cases with annotation, one needs to have MANIFEST.MF with Main-Class entry.

    2. A library jar file is incorrectly treated as app client jar in a deployment descriptor free application. This is due to the fact that the given library jar file has a Main-Class entry in MANIFEST.MF file. Note that even though the application may be able to deploy, it may not be as you desired. For instance, if the given ear does not have an app client jar, deployment may interpret a library jar as an app client jar. One can verify this in admin GUI.


      • Include application.xml and defined each module explicitly. In this case, the library jar is not mentioned in application.xml.
        For instance,
        <?xml version="1.0" encoding="UTF-8"?>
        <application xmlns="" xmlns:xsi="" version="5" xsi:schemaLocation=">
      • Or remove the Main-Class entry in MANIFEST.MF if it is unnecessary for the library function properly.

    Friday Dec 16, 2005

    How to use Verisign cert in Glassfish and SJSAS 8.x?

    This blog describes the steps needed to use Verisign certificates in GlassFish which can be downloaded from These steps will also work with the SJSAS 8.x products. You will need to go to the Verisign website to get a certificate if you don't already have one. In the following, we will outline steps on how to use Verisign certificate in Sun Java System Application Server (SJSAS) 8.x PE and Glassfish.

    Steps On Using Verisign Certificate

    1. Generate a private key in keystore resided in domains/your_domain/config/keystore.jks.
      keytool -genkey -alias myservkey -keysize 1024 -keyalg RSA -keystore keystore.jks -dname ",OU=Testing,O=Java,L=Santa Clara,S=California,C=US"
      Note that
      • there cannot be a space in the CN name. Verisign can only accept RSA at this time, DSA algorithm is not supported.
      • the password for keystore and the key must be the same
      • the password for keystore.jks in default installation is changeit
    2. Create a certificate request.
      keytool -certreq -alias myservkey -sigalg SHA1WithRSA -keystore keystore.jks -file myservkey.csr
    3. Backup your keystore. This is very important as no one can recover the private key if it is lost.
    4. Go to Verisign website to process the certificate.
    5. Once the certificate is processed, Verisign will send an email to you with certificate inside the email. Please cut and paste the certificate and save it to a file, say, myservkeyveri.cer. Please make sure there is no extra whitespace in the file.
    6. Make sure root CA certificate is in
      • domains/your_domain/config/keystore.jks
      • domains/your_domain/config/cacerts.jks
      • in your browser if it is used as SSL client.
      If you are using a Verisign certificate, then root CA has already been there. But if you are using Verisign testing certificate, then you need to import the Verisign testing root CA certificate which can be found in hyperlink of email from Verisign.

      The commands for importing CA root certificate is as follows:
      keytool -import -v -trustcacerts -alias verisigntestroot -file vertestrootca.cer -keystore keystore.jks
      keytool -import -v -trustcacerts -alias verisigntestroot -file vertestrootca.cer -keystore cacerts.jks
    7. Import the certificate to keystore.
      keytool -import -v -alias myservkey -file myservkeyveri.cer -keystore keystore.jks
    8. Verify that the certificate is imported correctly.
      keytool -list -v -alias myservkey -keystore keystore.jks
    9. Update the certificate alias from admin GUI:
      For https certificate alias:
      Configuration > HTTP Service > HTTP Listeners > http-listener-2
          Input certificate alias name in Certificate NickName text box.
          Enable SSL3, TLS, cipher suites if necessary

      For iiop certificate alias:
      Configuration > IIOP Listeners > SSL / SSL_MUTUALAUTH
          Input certificate alias name in Certificate NickName text box.
          Enable SSL3, TLS, cipher suites if necessary

    10. Restart the server by using asadmin.
    11. Verify the server is using the certificate. If you are setting https as above, then you can use browser to access https://your_host:8181 and it will prompt to you to accept the certificate you have just imported.


    1. keytool -import -keystore keystore.jks -alias myservkey -file myservkeyveri.cer
          Enter keystore password:
          keytool error:
      Please double check there is no extra whitespace in the file.
    2. keytool -import -v -alias myservkey -keystore keystore.jks -file myservkeyveri.cer
          Enter keystore password:
          keytool error: java.lang.Exception: Public keys in reply and keystore don't match
      The certificate reply does not match with the key in your keystore. You may need to check whether alias name is correct or get backup keystore having the private key.


    The same procedure work for SJSAS EE by using certutil instead of keytool:
    • use certutil -S to generate a key
    • use certutil -R to import a certificate
    • use certutil -A to import a certificate
    • use certutil -L to list a certificate
    More information on how to use certutil in SJSAS can be found in Key Management and PKCS#11 Tokens in Sun Java System Application Server 8.1, May 19, 2005.

    Shing Wai Chan


    « April 2014