Say hello world to comet

A couple of weekends ago I inflicted upon myself a quest to discover what all the buzz was about regarding Comet. What I discovered is that there is quite a bit of code out there to help you get started but the documentation around that code, and about Comet in general, is severely lacking. All I really wanted to find was a Comet-based Hello World, which as any developer knows, is the cornerstone of any programming language or methodology.

Since I couldn’t find one on Google, I ascertained that no Hello World exists for Comet and therefore I took it upon myself to write one.

For those of you who are new to Comet, the first thing you should do is read Alex Russell’s seminal blog post on the topic. At its core, Comet is really just a message bus for browser clients. In one browser, you can subscribe to a message and in another you can publish a message. When a message gets published, every browser that’s subscribed (almost) instantaneously receives it.

What? I thought clients (browsers) had to initiate communication per the HTTP spec. How does this work?

Under the covers, Comet implementations use a little-known feature of some web server implementations called continuations (or hanging gets). I won’t go into details here, but at a high level, a continuation initiates from the browser (as all HTTP requests must do) and then, when it’s received by the server, the thread handling it basically goes to sleep until it gets a message or times out. When it times out, it wakes up and sends a response back to the browser asking for a new request. When the thread on the server receives a message, it wakes up and sends the message payload sent back to the browser (which also implies that it’s time to send a new request). Via this mechanism, HTTP is more or less “inverted” so that the server is essentially messaging the client instead of vice-versa.

A few questions immediately pop into mind, so let’s just deal with them right now:

Why is this better than Ajax alone?

It boils down to latency and users’ tolerance for it. In the worst case, traditional web applications force entire page refreshes. Ajax applications are a little better, because they can refresh smaller parts of a page in response to users’ actions, but the upshot is that the users are still waiting for responses, right? A Comet-driven application has essentially removed the user from the picture. Instead of the user asking for fresh data, the server just sends it along as soon as it changes, given the application more of a “realtime” feel and removing virtually all perceived latency.

So are we back to client server again?

Sort of. Comet gives you the benefit of server-to-client messaging without the deployment issues associated with fat clients.

Can’t applets do this?

Of course they can. But who wants to download an applet when some lightweight Javascript will do the trick?

Why the name Comet?

Well, clearly it’s a pun on Ajax. But it’s not the only name for this sort of technology. There’s something out there called pushlets which claims to do the same thing as Comet, but which didn’t seem to catch on, I guess.

Back to the whole point of this post: my hello world. I pieced this example together using dojo.io.cometd and a recent version of Tomcat that into which I dropped the relevant parts of Jetty to provide support for continuations.

It’s finally time to say “hello world” to my hello world.

First off, download one of the more recent dojo builds that contains support for dojo.io.cometd. Drop dojo.js on your Java-based web/application server. (I used Tomcat, but you can use JBoss, Jetty, Weblogic, Websphere or any other web server with support for servlets.) Add this page in the root of your application:

<script src="js/dojo.js" type="text/javascript"></script>
<script type="text/javascript">//<![CDATA[
  dojo.require("dojo.io.cometd");
  cometd.init({}, "cometd");
  cometd.subscribe("/hello/world", false, "publishHandler");
  publishHandler = function(msg) { alert(msg.data.test); }
// ]]></script>
<input type="button" value="Click Me!" />

Without a cometd-enabled web server behind it, the above page does absolutely nothing.

So, to make this work, I needed to find a Java-based web/application server with support for continuations. I’m sure there are many ways to skin this cat, but I picked Jetty. You can get Jetty source and binaries if you’d like to follow along. Since all of our customers who embrace open source are lightyears more comfortable with Tomcat than they are with any other open source web/application server (ahem . . . Jetty), I decided to embed Jetty in Tomcat rather than run everything on Jetty alone. It’s all just Java, right?

Here I ran into a few snags. The maven build file for Jetty didn’t work for me, so I dropped everything in org.mortbay.cometd and org.mortbay.cometd.filter into my Eclipse project and just integrated it with the ant build.xml I was already using to build my web application. Here’s the relevant portion of my build.xml:

<javac srcdir="${srcdir}" destdir="${classdir}" debug="true" debuglevel="lines,vars,source">
<classpath>
<pathelement location="${jetty.home}/lib/jetty-util-6.0.1.jar"/>
<pathelement location="${jetty.home}/lib/servlet-api-2.5-6.0.1.jar"/>
</classpath>
</javac>

Once Jetty was essentially hacked into Tomcat, the rest was smooth sailing. I just wrote a JSP that dropped a “goodbye world” message onto the same old queue that I used in the last example, but I did so using server-side code. Here’s the JSP:

<%@page import="org.mortbay.cometd.*"%>
<%@page import="java.util.*"%>
<%
Bayeux b = (Bayeux)getServletContext().getAttribute(CometdServlet.ORG_MORTBAY_BAYEUX);
Channel c = b.getChannel("/hello/world");
Map message = new HashMap();
message.put("test", "goodbye world");
c.publish(message, b.newClient());
%>

This page does not produce any output of its own; rather, it just drops the “goodbye world” message on the queue. When you hit this page in a browser, any other browser listening to the /hello/world queue will get the message. The above JSP, along with the dojo page you created in the first step, should be enough to wire together two different flavors of Comet messaging: browser to server to browser and just plain old server to browser.

I’m curious 1) if this was helpful and 2) if you’d like to share what you’re doing with Comet with me (and please don’t say cleaning your kitchen).

How to Integrate PKI Certs or CAC Cards with ALI

In his 1947 speech to the House of Commons, Winston Churchill quipped, “It has been said that democracy is the worst form of government except all those other forms that have been tried.”

I’m not nearly as pithy as Sir Winston (nor as portly — at least not yet), but yet I feel the same way about passwords being used to protect web sites or other enterprise systems. In many ways, they’re the worst form of security out there except for everything else that’s been tried. Part of this has something to do with what I’ve coined Bucchere’s Axiom of Strong Passwords, which is a derivative of Murphy’s Law (which states that whatever can go wrong will). It goes something like this: the stronger a password is, the easier it is to hack. Why? Because if you force users into using a strong password, they’re more likely to write it down. And writing a password down defeats its purpose entirely.

The bottom line: passwords suck. But they’ve become the de-facto standard because they’re easier and cheaper than everything else we’ve tried, including PKI certs, biometrics (e.g. fingerprints, retina-scans), CAC cards, RSA secure IDs, etc. (Even for a cert-based authentication scheme, you still need a key to generate your cert, which is essentially just a glorified password.)

Just because passwords are the de-facto standard for authentication does not mean that we should quit trying to use other, ostensibly better forms of security, especially if 1) you’re protecting particularly sensitive data, 2) you’re open to the internet and 3) you have the resources (e.g. $$$) to invest in more robust forms of security. And I’m not talking about just buying an SSL cert from Verisign and continuing to have your users write down their passwords on post-it notes attached to their monitors. (Note to self: remove the post it note on your monitor with your password on it when you get back to the office.) I’m talking about using some sort of “soft” cert (e.g. PKI) or “hard” cert (e.g. CAC) to protect your system and your data.

Now if your system is ALI (formerly known as Plumtree Foundation or Plumtree Portal), you’re in luck, because the eggheads at what was once known as Plumtree have made this particularly easy to do. In fact, the hardest part is just getting the user’s identity out of the cert (see below the code snippet for some suggestions). Once you’ve done that, just drop a class into a jar that implements the ISSOProvider interface. (For those of you running on Windows, please don’t ask me to “port” this to C# — just take the Java code, drop it into Visual Studio.NET and then fix the syntax errors.)

But wait, SSO stands for “Single Sign On,” right? And what you’re really doing here is passing credentials from a cert to Plumtree and that has little or nothing to do with SSO. That’s a true statement. The subtlety here is that ISSOProvider, while it contains the letters SSO in its name, can be used for pretty much any form of authentication, whether you are using an SSO product or not.

CertIntegration.java

package com.bdgportal.alui.auth;

import com.plumtree.openfoundation.util.*;
import com.plumtree.openfoundation.web.*;
import com.plumtree.portaluiinfrastructure.sso.*;

public class CertIntegration implements ISSOIntegration {
 
   private XPHashtable settings;
 
   public CertIntegration() {
     ;
   }
 
   public boolean Initialize(XPHashtable settings) {
     this.settings = settings;       
     //String exampleSetting = ((XPArrayList)settings.GetElement("SettingName")).GetElement(0);
   }

   public String GetSSOProductName() {
     return "My Favorite Cert Integration";
   }

   /**
    * Gets the username from the cert and returns it to Plumtree. This will fail if the username
    * does not have a matching account in Plumtree. This can be a Plumtree database user or a user
    * imported from an authentication source, in which case you need to include the auth source
    * prefix in the username, e.g. "MyAuthSource/cbucchere"
    *
    * @param request The wrapped HttpServletRequest from the web container.
    * @return The object passed back to Plumtree for authentication with the portal.
    */
   public SSOLoginInfo GetLoginInfo(IXPRequest request) {
     String userName = ((XPRequest)request).GetUnderlyingObject().getUserPrincipal().getName();
     return new SSOLoginInfo(userName);
   }

   public String[] GetSecureCookies() {
     return null;
   }

   public String[] GetSecureHeaders() {
     return null;
   }

   public boolean OnLogout(IXPResponse response, String returnURI) {
     return false;
   }   
}

The hardest part about all this, as I said above, is getting the user name out of the PLI cert/CAC card/retina scan/etc. In the example above, I made MANY assumptions. First, I assumed that your portal is running on Weblogic, which understands and correctly implements Principal, which is a Java Servlet’s way of knowing who’s using it. Weblogic lets you plug custom implementations of the Principal class into its security infrastructure. All you need to do is extend java.security.Principal and then walk through a bunch of magical configuration steps to enable it.

Speaking of magical configuration, I neglected to mention that there are two small configuration steps that you need to perform in order to get your shiny new ISSOIntegration working in ALI. In portalconfig.xml, you need to set the value of SSOVendor setting to 100 (or greater) and then set the CustomSSOClass to the fully qualified name of the class you wrote that implements ISSOIntegration. For our Java example above, that would be com.bdgportal.alui.auth.CertIntegration and for .NET, it would the the name of your C# class.

Speaking of .NET . . . as many of you know, it is an entirely different animal with its own way of provisioning security to web applications (e.g. System.Web.Security).

Regardless of your platform, you need to get the user name out of whatever authentication method you’re using. Once you’ve accomplished that, just drop the code above into your project and replace the getUserPricipal().getName() with whatever mechanism you can find for getting your users’ names.

Assuming you trust your authentication mechanism to return the appropriate user name, you’ll have users getting logged into the portal via pretty much however you would like — CAC, PKI, biometrics, etc.

If only implementing a democracy were this easy . . . .

Comments

Comments are listed in date ascending order (oldest first)

  • This is wonderful article. How ever I’ve researched for a long time but still can not figure out what to do with Bea Weblogic to use Costom Identify Assertion. I wish this artical to have link to the document of how to “do the magical configuration steps”.

    Posted by: minh.tran on January 9, 2007 at 9:04 AM

  • This article was intended to be application server independent, but if you’re using BEA WebLogic, there’s a great article on how to set up custom identity providers which should work with this ALUI SSO solution.

    Posted by: bucchere on January 10, 2007 at 6:44 PM

  • NOTE: 1. the user’s password in the portal must be empty string. 2. jar should be put in portal.war and lib/java.

    Posted by: luotuoci on April 28, 2007 at 8:31 PM

Public Sector Breakfast Seminar: Ajax, Java & Mission Critical Applications

I’m very pleased to announce that I’ve been selected by Nexaweb to sit on a panel of Enterprise Web 2.0 experts. My co-panelists include:

  • Brant West, VP Federal Sales, Autonomy
  • Brenda Dixon, Commerce Industry Account Lead, IBM Federal Systems
  • David Bock, Technical Director, FGM
  • David McFarlane, Chief Operating Officer, Nexaweb Technologies, Inc.

The event takes place on Wednesday, 11/8 at 8 AM at The L’Enfant Plaza Hotel in DC.

If you’d like to attend the free seminar, please register here. Hope to see you there!