Friday, July 25, 2008

BlackBerry + WiFi

Taking a short break from the planned article list to rant about my new least favorite feature of BlackBerry OS.

If you have a BlackBerry that also happens to support WI-FI, you might be surprised to find out that your favorite networked application doesn't favor WI-FI over APN when you're in range.

In fact, these applications will never favor WI-FI over your regular data network link. This is because the BlackBerry operating system does not abstract this for the application programmer. In other words, unless the application programmer knows about this and explicitly codes support for WI-FI in his application, the application will never use WI-FI.

So how do you get WI-FI support in there? You have to append the magical string ";interface=wifi" to any URL that you want to open. So, assuming standard Java ME/CLDC network programming here, you would do something like:

String uri = "http://www.spectrumdt.com;interface=wifi";
HttpConnection c = (HttpConnection) Connector.open(uri);

Otherwise, your application will inform you that "Could not open tunnel - Max time out" or go over your regular data link.

And that's about it. Oh, and by the way, if you don't have WI-FI or you're out of range, the OS won't do you any favors and fall back to your regular data link, so you have to make sure to try out the regular data link in case of errors too.

Finally, I would like to say that this feature is mostly impossible to find in the BlackBerry documentation. I found it in a beta version of an upcoming manual. The only reason I noticed this is that my development BlackBerry doesn't have a sim card, the browser worked, but my application didn't. Also of interest, almost all non first party applications I tested couldn't use WI-FI either. I'm guessing quite a few people don't know about this.

Sunday, July 13, 2008

GWT + j_security_check

I went out looking for a solution to this recently and found very little information. A lot of questions unanswered led me to believe I was not the only one who wanted to do this. I present here a small sample on how to accomplish this.

 

Before presenting the sample, let's go over a few things first on why you would want to do this and more importantly why you still need an Ajax style security token for all your GWT services even after this is implemented.


The reasons you would want to do this? One is automatic single sign-on (SSO) if supported by your container, e.g., Glassfish supports SSO out of the box for its web, EJB and web services tiers. If you use j_security_check, you're in; you're into all these services. Furthermore, if you use a J2EE authorization agent like OpenSSO inside Glassfish, you're into any services or sites provided by that federation. So even if you’re fronting your application with GWT, your back end might be composed of all these elements and more.


This also allows you to use any standard security annotations in your EJBs or, if you're orchestrating like me, in OpenESB. You can do this because you now have a Java security principal setup inside the container.


This also allows you to leverage the clustering features, if any, of your application server. Glassfish, again, contains great out of the box support for clustering. Session replication and expiration is managed for you and it just works. One call can proceed on one server while the other can proceed on another while the caller remains completely oblivious to where its work is actually being executed.

 

What you can’t do however is assume that you should no longer have an object representing your logged in user in your GWT client code. GWT RPC calls are still out in the open with this technique, you should pass in at the very least your session id value as a parameter to the GWT remote call so that the RCP service can do validations.

 

You still need SSL. Use SSL to make sure your password is transmitted securely over the wire. Hashing on the client side is completely useless. First, because MD5 is usually about the only thing available in JavaScript and that is easily broken. Second, because you should be counting on SSL to do its job correctly. Third, once on the server, you should hash your password with something that is secure, i.e., not MD5 and throw away the clear text representation of the password.

 

I use NetBeans 6.1 and release candidate 1 of GWT 1.5 for this example. The idea is to use the RequestBuilder class to touch a resource that has been marked off limits by the container. We do this because the j_security_check, in Java EE 5 at least, cannot be invoked directly in a portable manner. When the server sees the access to the protected resource by an unknown, it redirects you to the login page. When this occurs, the method “onResponseReceived” is called. We ignore the redirect and submit our own form. The form just needs to have at least 3 named elements present:

  • The form name, j_security_check
  • The user name field, j_username
  • The password field, j_password

If you don’t touch a protected resource first and simply submit your form immediately, you will find that you still don’t have a security principal since no login actually occurred in the container.

 

You would apply the same rules for defining a protected area than you would for a normal non-GWT application. You can see on how to do this in Glassfish and Tomcat by following the tutorial here.

 

Here is some sample code that fulfills a login on Java application server. I originally had more here in terms of error handling but it seemed it got to be too large for presentation purposes.

 

package org.codepimps.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.FormHandler;
import com.google.gwt.user.client.ui.FormPanel;
import com.google.gwt.user.client.ui.FormSubmitCompleteEvent;
import com.google.gwt.user.client.ui.FormSubmitEvent;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.PasswordTextBox;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;

public class MainEntryPoint implements EntryPoint {
  public void onModuleLoad() {
    final FormPanel formPanel = new FormPanel();
    VerticalPanel vpanel = new VerticalPanel();
    HorizontalPanel hpanel = new HorizontalPanel();
    TextBox userName;
    TextBox password;

    formPanel.setAction("j_security_check");
    formPanel.setMethod(FormPanel.METHOD_POST);
    hpanel.add(new Label("User name:"));
    hpanel.add(userName = new TextBox());
    userName.setName("j_username");
    vpanel.add(hpanel);

    hpanel = new HorizontalPanel();
    hpanel.add(new Label("Password:"));
    hpanel.add(password = new PasswordTextBox());
    password.setName("j_password");
    vpanel.add(hpanel);

    vpanel.add(new Button("Login", new ClickListener() {
      public void onClick(Widget arg0) {
        String url = GWT.getHostPageBaseURL() + "protected/protectedText.txt";
        RequestBuilder rb = new RequestBuilder(RequestBuilder.GET, url);
       
        rb.setCallback(new RequestCallback() {
          public void onResponseReceived(Request request, Response response) {
            formPanel.submit();
          }

          public void onError(Request request, Throwable caught) {
            throw new UnsupportedOperationException("Not supported yet.");
          }
        });

        try {
          rb.send();
        } catch (RequestException ex) {
          // TODO present the error in a text area or something
        }
      }
    }));
   
    formPanel.addFormHandler(new FormHandler() {
      public void onSubmit(FormSubmitEvent event) {
      }

      public void onSubmitComplete(FormSubmitCompleteEvent event) {
        // TODO actually make sure the login succeeded.  Present error in a text area
        // for the user to see
        Window.Location.assign(GWT.getHostPageBaseURL() + "/protected/protectedText.txt");
      }
    });

    formPanel.add(vpanel);
    RootPanel.get().add(formPanel);
  }
}

Java2html

Wednesday, July 9, 2008

Upcoming

The blog has been very silent but I have quite a few articles in the queue coming up. Upcoming on this blog are:

I'm so busy I don't know when I'll have time to write any of these but hopefully they will be up before long.