Matt RaibleMatt Raible is a Web Developer and Java Champion. Connect with him on LinkedIn.

The Angular Mini-Book The Angular Mini-Book is a guide to getting started with Angular. You'll learn how to develop a bare-bones application, test it, and deploy it. Then you'll move on to adding Bootstrap, Angular Material, continuous integration, and authentication.

Spring Boot is a popular framework for building REST APIs. You'll learn how to integrate Angular with Spring Boot and use security best practices like HTTPS and a content security policy.

For book updates, follow @angular_book on Twitter.

The JHipster Mini-Book The JHipster Mini-Book is a guide to getting started with hip technologies today: Angular, Bootstrap, and Spring Boot. All of these frameworks are wrapped up in an easy-to-use project called JHipster.

This book shows you how to build an app with JHipster, and guides you through the plethora of tools, techniques and options you can use. Furthermore, it explains the UI and API building blocks so you understand the underpinnings of your great application.

For book updates, follow @jhipster-book on Twitter.

10+ YEARS


Over 10 years ago, I wrote my first blog post. Since then, I've authored books, had kids, traveled the world, found Trish and blogged about it all.

Labor Day Weekend at The Farm

The Castle (very solar efficient) This past weekend, the kids and I journeyed into the Colorado mountains for a weekend of camping, hiking and fishing. It was my good friend Matt Good's 50th Birthday Celebration at his place affectionately known as The Farm. We had beautiful weather, wide open spaces and lots of laughs with good friends. Jack even managed to land a girlfriend (Amanda), who somehow became his "ex" in the same hot tubbing session. They got back together the next morning. ;-)

On the way home, we took the long way and traveled from Ute Pass Road to Frasier, stopping at Lake Evelyn Trail for an hour hike and some fishing. I even let the kids drive a few times on the "bumpy road" and we managed to skip most of the traffic on the freeway.

More than anything, I was impressed with Abbie and Jack's ambition on this trip. They really seem to love hiking and fishing and don't drag their feet like they used to. They're also enthusiastic about camping and sleeping in a tent. I'm awful proud of my little munchkins. Below are some pictures from our weekend.

Picking out a campsite Jack getting a cooking lesson Jack and his new girlfriend

Side View of The Castle Crazy Kids Fishy

Thanks to Matt and Pam for hosting us and congrats to Matt on officially becoming an "old man". For more pictures, please see my Labor Day 2009 set on Flickr.

Posted in General at Sep 09 2009, 08:22:40 AM MDT 1 Comment

Happy Birthday Jack!

Five years ago today, Jack was born. I remember it like it was yesterday, even though it feels like it's been ages since he was a baby.

We were renting the house next door (while ours was being remodeled), I was just finishing up a contract with Open Logic (when it was just Rod and Eric) and Julie's contractions had begun. I finished my last hours with perfect timing (around 4:00). Julie was sitting at the kitchen table negotiating with a guy about some house-related stuff (our General Contractor had died and Julie took over 8 months pregnant). She told the guy she "had to go" because she was in labor and off to the hospital we went. The good news is it was only a few blocks away, so we made it there in short order. Our friend Tonya drove us.

Miraculously, Julie was able to "hold Jack in" until her sister flew in from San Diego. He was born just after midnight.

A few days later, my good friend Ryan called me out of the blue (hadn't talk to the guy in years). He asked me what I was up to and I told him I was a father for the 2nd time and just had a baby boy at Porter Hospital. He said, "No way - I just had a baby girl 2 days ago!" Turns out, his daughter Sophie was born 2 days after Jack, only a couple doors down. We'd left the hospital only a few hours before they arrived.

This weekend will be filled with multiple birthday parties, happy kids, and lots of good memories. I love this time of year. :-D

Happy birthday Jack!

Jack is 5!

Posted in General at Aug 28 2009, 03:12:45 PM MDT Add a Comment

My Experience with Java REST Frameworks (specifically Jersey and CXF)

Recently I was tasked with developing a server-side REST strategy for a client. When I started working with them, they were using GWT RPC for exposing services. They wanted to move to RESTful services to allow for a more open platform, that multiple types of clients could talk to. There were interested in supporting SOAP and GWT RPC as well, but it wasn't required. They are using Spring, a custom namespace (for easily creating remote services) and an HTML documentation generator to expose their API in human readable form.

When I first starting developing, I chose to try Enunciate. From Enunciate's homepage:

Enunciate is an engine for creating, maintaining, and deploying your rich Web service API on the Java platform. If that sounds complicated, it's not. All you have to do is define your service interfaces in Java source code. ... Then invoke Enunciate.

Sounds pretty sweet, eh? At first glance, the things I liked about Enunciate were:

  1. The ability to generate multiple endpoints (and clients).
  2. Generates nice-looking documentation.
  3. Allows selecting different frameworks (for example, CXF instead of JAX-WS RI).

Initially, the hardest part of using Enunciate was integrating it into my project. This was somewhat related to having a multi-module project, but moreso related to getting the settings right in my enunciate.xml file. After getting everything working, I encountered a few Spring wiring issues with the GWT Endpoints and with Jersey's Spring support.

The good news is I believe most of these issues were related to my project and how it proxies Spring beans that Jersey couldn't find. Jersey's Spring support only supports annotations at this time, so I was unable to tell it the proxied bean names via XML (or even its own annotations). I'm sure this problem is solvable, but after struggling with it for a day or two, I decided to give up on Enunciate. I had to get something working, and fast.

At this point, I was back to the drawing board. I knew there were plenty of good Java REST frameworks available, but Spring and CXF (for SOAP) were already available dependencies in my project, so I chose that route. I was able to get something up and running fairly quickly with this tutorial and CXF's JAX-RS documentation. I ran into an Invalid JSON Namespace issue, but was able to solve it by adding a custom namespace.

It's not all rosy though, there are still a couple CXF issues I haven't solved:

While CXF does take a bit of XML for each service, it's kinda slick in that it only requires you to annotate your service interfaces. It also generates documentation for your services (WSDL and WADL), but it's not as pretty as Enunciate's. To solve this, I added Enunciate for documentation only. To make Enunciate work, I did have to add some annotations to my service implementation classes and parse the generated HTML to fix some links to CXF's services, but ultimately it works pretty well.

In the end, I recommended my client not use Enunciate for generating endpoints. This was primarily related to their unique Spring configuration, but also because I was able to easily use the same classes for REST, SOAP and GWT Endpoints. However, I will continue to keep my eye on Enunciate. It could be very useful for the upcoming appfuse-ws archetype. I'm also eager to see better GWT support and the ability to generate Overlay types in future releases.

Posted in Java at Aug 27 2009, 02:20:51 PM MDT 15 Comments

The 2009 Rich Web Experience

Rich Web Experience 2009 Late last year, I decided to take a year off from speaking at conferences. My reason was simple: I wanted to hunker down and do some learning. Ever since November 2007, I'd been interested in getting into developing SOFEA applications. This year, I was able to accomplish that and I've learned a lot about GWT and RESTful architectures in the process. Now I'm happy to announce I'll be sharing my experiences at this year's Rich Web Experience.

I look forward to speaking on the following topics:

The first one might look familiar since I used the same title at OSCON 2008. The difference is this time I plan to dive much deeper into the frameworks and help the audience learn more. I also plan to re-visit developing with Flex and Rails in the near future. If there's anything in particular you'd like to see covered in the above talks, please leave a comment.

This is sure to be a fun conference. First of all, it's pretty close to Disney World. A few years back, TSS hosted the "Java In Action" conference in Disney World and while the conference didn't do too well, it was awesome being there with my family. I plan on taking my kids and having some fun when I'm not speaking. Secondly, this conference is the first to offer all-inclusive pricing for attendees - registration, flight (continental U.S.) and 3 nights lodging. This should allow you to get approval without having to jump through the traditional travel hoops. Lastly, the JSF Summit will be held concurrently and you get access to it at no additional cost.

So come on down to Florida in December, listen to all the great speakers, have some fun with your family and look me up for a beer or two. You know it'll be a good time! ;-)

Posted in Java at Aug 11 2009, 10:16:24 PM MDT 1 Comment

My attempt at browser-based username/password autocomplete with GWT

Last night, I did a quick spike to try and implement username/password autocomplete in my GXT application. By "autocomplete", I don't mean Ajax-style autocomplete, but rather browser-based autocomplete. The best information I found on doing this is in the following post:

http://osdir.com/ml/GoogleWebToolkit/2009-04/msg01838.html

I didn't use this technique (wrapping an existing form with a FormPanel) because I'm using GXT and didn't want to lose the look-and-feel of my login form.

I was successful in getting everything to work in Firefox (it populates both the username and password). In IE, it only populates the username, not the password. In Safari/Chrome, it doesn't work at all. Here's how I did it:

1. Created a hidden HTML form on my HTML page that embeds GWT.

<form method="post" action="javascript:void(0)" style="display: none">
    <input type="text" id="username" name="username" value=""/>
    <input type="password" id="password" name="password" value=""/>
    <input type="submit" value="Login" id="login"/>
</form>

2. When a user clicks on the "Login" button in my GWT application, populate the fields in this hidden form and "click" on the Login button (which will do nothing since the action="javascript:void(0)").

// Set the hidden fields to trigger the browser to remember
DOM.getElementById("username").setAttribute("value", username.getValue());
DOM.getElementById("password").setAttribute("value", password.getValue());
clickFormLogin();

...

public static native void clickFormLogin() /*-{
$doc.getElementById("login").click();
}-*/;

This works in Firefox 3.5 and prompts me to save the user/pass at the top of the screen. Why doesn't this work in Safari/Chrome? My guess is because the form's action doesn't go anywhere and the form is not submitted. If I change the action to be an actual URL and show the form, clicking on the form's Login button will save it in those browsers.

Grabbing the actual populated values when the screen loads did turn out to be a bit tricky. With IE, I was able to grab the username, but not the password. It seems that keyboard or mouse interaction (tabbing off or focusing/blurring) with the username field is necessary to get the password field populated. In Firefox, I was unable to grab the values if I did it straight away. The solution turned out to be wrapping everything in a Timer and waiting a 1/2 second.

// grab user/pass from browser and hidden login form (if user has stored them)
// runs 1/2 second after page loads so cursor goes at end of username and works in Firefox
Timer t = new Timer() {
    public void run() {
        username.setValue(getElementValue("username"));
        password.setValue(getElementValue("password"));
        if (username.getValue() != null) {
            password.focus();
        }
        validate();
    }
};

t.schedule(500);

...

public static native String getElementValue(String domId) /*-{
    return $doc.getElementById(domId).value;
}-*/;

While I'm glad I got it working in Firefox, I'm disappointed with IE's lack of password autocompletion. More than anything, I can't help but think there's a way to make this work in WebKit-based browsers. If you've successfully implemented cross-browser username/password autocomplete in GWT, please share your experience. If you share your experience about successfully doing it in Safari/Chrome, I might even buy you a beer or two. ;-)

Posted in Java at Aug 07 2009, 08:09:11 AM MDT 2 Comments

Integrating GWT with Spring Security

Yesterday, I wrote about How to do cross-domain GWT RPC with a ProxyServlet. Today I'll be discussing how to modify the ProxyServlet to authenticate with Spring Security. For the application I'm working on, the ProxyServlet is only used in development (when running GWT's hosted mode) and isn't necessary when deploying the client and server on the same server. Using the ProxyServlet allows cross-domain requests so you can run GWT in hosted mode and talk to your backend running on another server. This setup can be especially handy in that you can easily point your hosted client at different backends (for example, if you have testing and staging environments).

In this example, the backend application is a JSF/Spring application that has Spring Security wired in to protect services with both Basic and Form-based authentication. Basic authentication will kick in if a "Authorization" header is sent, otherwise Form-based authentication is used. Here's the Spring Security context file that makes this happen:

<?xml version="1.0" encoding="UTF-8"?>

<beans:beans xmlns="http://www.springframework.org/schema/security"
             xmlns:beans="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="...">

    <http auto-config="true" realm="My Web Application">
        <intercept-url pattern="/faces/welcome.jspx" access="ROLE_USER"/>
        <intercept-url pattern="/*.rpc" access="ROLE_USER"/>
        <http-basic/>
        <form-login login-page="/faces/login.jspx" authentication-failure-url="/faces/accessDenied.jspx"
                    login-processing-url="/j_spring_security_check" default-target-url="/redirect.jsp"
                    always-use-default-target="true"/>
    </http>

    <authentication-provider>
        <user-service >
            <user name="admin" password="admin" authorities="ROLE_USER"/>
        </user-service>
    </authentication-provider>
</beans:beans>

The easiest way to configure your GWT application to talk to a Spring Security protected resource is to protect your HTML page that GWT is embedded in. This is the documented way to integrate GWT with Spring Security (ref: GWT's LoginSecurityFAQ, search for "Acegi"). This works well for production, but not for hosted-mode development.

Basic Authentication
To authenticate with Basic Authentication, you can use GWT's RequestBuilder and set an "Authentication" header that contains the user's (base64-encoded) credentials.

private class LoginRequest {
    public LoginRequest(RequestCallback callback) {
        String url = "/services/faces/welcome.jspx";

        RequestBuilder rb = new RequestBuilder(RequestBuilder.POST, url);
        rb.setHeader("Authorization", createBasicAuthToken());
        rb.setCallback(callback);
        try {
            rb.send();
        } catch (RequestException e) {
            Window.alert(e.getMessage());
        }
    }
}

protected String createBasicAuthToken() {
    byte[] bytes = stringToBytes(username.getValue() + ":" + password.getValue());
    String token = Base64.encode(bytes);
    return "Basic " + token;
}

protected byte[] stringToBytes(String msg) {
    int len = msg.length();
    byte[] bytes = new byte[len];
    for (int i = 0; i < len; i++)
        bytes[i] = (byte) (msg.charAt(i) & 0xff);
    return bytes;
}

To use this LoginRequest class, create it with a callback and look for a 401 response code to determine if authentication failed.

new LoginRequest(new RequestCallback() {
    public void onResponseReceived(Request request, Response response) {
        if (response.getStatusCode() != Response.SC_UNAUTHORIZED &&
                response.getStatusCode() != Response.SC_OK) {
            onError(request, new RequestException(response.getStatusText() + ":\n" + response.getText()));
            return;
        }

        if (response.getStatusCode() == Response.SC_UNAUTHORIZED) {
            Window.alert("You have entered an incorrect username or password. Please try again.");
        } else {
            // authentication worked, show a fancy dashboard screen
        }
    }

    public void onError(Request request, Throwable throwable) {
        Window.alert(throwable.getMessage());
    }
});

If your GWT application is included in the "services" war, everything should work at this point. However, if you try to login with invalid credentials, your browser's login dialog will appear. To suppress this in the aforementioned ProxyServlet, you'll need to make a change in its executeProxyRequest() method so the "WWW-Authenticate" header is not copied.

// Pass the response code back to the client
httpServletResponse.setStatus(intProxyResponseCode);

// Pass response headers back to the client
Header[] headerArrayResponse = httpMethodProxyRequest.getResponseHeaders();
for (Header header : headerArrayResponse) {
    if (header.getName().equals("Transfer-Encoding") && header.getValue().equals("chunked") ||
            header.getName().equals("Content-Encoding") && header.getValue().equals("gzip") ||
            header.getName().equals("WWW-Authenticate")) { // don't copy WWW-Authenticate header
    } else {
        httpServletResponse.setHeader(header.getName(), header.getValue());
    }
}

I'm not sure how to suppress the browser prompt when not using the ProxyServlet. If you have a solution, please let me know.

Basic Authentication works well for GWT applications because you don't need additional logic to retain the authenticated state after the initial login. While Basic Authentication over SSL might offer a decent solution, the downside is you can't logout. Form-based Authentication allows you to logout.

Form-based Authentication

Before I show you how to implement form-based authentication, you should be aware that Google does not recommend this. Below is a warning from their LoginSecurityFAQ.

Do NOT attempt to use the Cookie header to transfer the sessionID from GWT to the server; it is fraught with security issues that will become clear in the rest of this article. You MUST transfer the sessionID in the payload of the request. For an example of why this can fail, see CrossSiteRequestForgery.

In my experiment, I didn't want to change the server-side Spring Security configuration, so I ignored this warning. If you know how to configure Spring Security so it looks for the sessionID in the payload of the request (rather than in a cookie), I'd love to hear about it. The upside of the example below is it should work with container-managed authentication as well.

The LoginRequest class for form-based authentication is similar to the previous one, except it has a different URL and sends the user's credentials in the request body.

private class LoginRequest {
    public LoginRequest(RequestCallback callback) {
        String url = "/services/j_spring_security_check";

        RequestBuilder rb = new RequestBuilder(RequestBuilder.POST, url);
        rb.setHeader("Content-Type", "application/x-www-form-urlencoded");
        rb.setRequestData("j_username=" + URL.encode(username.getValue()) +
                    "&j_password=" + URL.encode(password.getValue()));

        rb.setCallback(callback);
        try {
            rb.send();
        } catch (RequestException e) {
            Window.alert(e.getMessage());
        }
    }
}

If you deploy your GWT application in the same WAR your services are hosted in, this is all you'll need to do. If you're using the ProxyServlet, there's a couple of changes you'll need to make in order to set/send cookies when running in hosted mode.

First of all, you'll need to make sure you've configured the servlet to follow redirects (by subclassing or simply modifying its default). After that, add the following logic on line 358 (or just look for "if (followRedirects)") to expose the sessionID to the client. The most important part is setting the cookie's path to "/" so the client (running at localhost:8888) can see it.

if (followRedirects) {
    // happens on first login attempt
    if (stringLocation.contains("jsessionid")) { 
        Cookie cookie = new Cookie("JSESSIONID",
                stringLocation.substring(stringLocation.indexOf("jsessionid=") + 11));
        cookie.setPath("/");
        httpServletResponse.addCookie(cookie);
    // the following happens if you refresh your GWT app after already logging in once
    } else if (httpMethodProxyRequest.getResponseHeader("Set-Cookie") != null) {
        Header header = httpMethodProxyRequest.getResponseHeader("Set-Cookie");
        String[] cookieDetails = header.getValue().split(";");
        String[] nameValue = cookieDetails[0].split("=");

        Cookie cookie = new Cookie(nameValue[0], nameValue[1]);
        cookie.setPath("/");
        httpServletResponse.addCookie(cookie);
    }
    httpServletResponse.sendRedirect(stringLocation.replace(getProxyHostAndPort() +
            this.getProxyPath(), stringMyHostName));
    return;
}

Click here to see a screenshot of the diff of the ProxyServlet after this code has been added.

Figuring out that headers needed to be parsed after authenticating successfully and before redirecting was the hardest part for me. If you grab the JSESSIONID from the "Set-Cookie" header anywhere else, the JSESSIONID is one that hasn't been authenticated. While the login will work, subsequent calls to services will fail.

To make subsequent calls with the cookie in the header, you'll need to make an additional modification to ProxyServlet to send cookies as headers. First of all, add a setProxyRequestCookies() method:

/**
 * Retrieves all of the cookies from the servlet request and sets them on
 * the proxy request
 *
 * @param httpServletRequest The request object representing the client's
 *                            request to the servlet engine
 * @param httpMethodProxyRequest The request that we are about to send to
 *                                the proxy host
 */
@SuppressWarnings("unchecked")
private void setProxyRequestCookies(HttpServletRequest httpServletRequest, 
                                    HttpMethod httpMethodProxyRequest) {
    // Get an array of all of all the cookies sent by the client
    Cookie[] cookies = httpServletRequest.getCookies();
    if (cookies == null) {
        return;
    }
    
    for (Cookie cookie : cookies) {
        cookie.setDomain(stringProxyHost);
        cookie.setPath(httpServletRequest.getServletPath());
        httpMethodProxyRequest.setRequestHeader("Cookie", cookie.getName() +  
                "=" + cookie.getValue() + "; Path=" + cookie.getPath());
    }
}

Next, in the doGet() and doPost() methods, add the following line just after the call to setProxyRequestHeaders().

setProxyRequestCookies(httpServletRequest, getMethodProxyRequest);
 

After making these modifications to ProxyServlet, you can create LoginRequest and attempt to authenticate. To detect a failed attempt, I'm looking for text in Spring Security's "authentication-failure-url" page.

new LoginRequest(new RequestCallback() {

    public void onResponseReceived(Request request, Response response) {
        if (response.getStatusCode() != Response.SC_OK) {
            onError(request, new RequestException(response.getStatusText() + ":\n" + response.getText()));
            return;
        }
        
        if (response.getText().contains("Access Denied")) {
            Window.alert("You have entered an incorrect username or password. Please try again.");
        } else {
            // authentication worked, show a fancy dashboard screen
        }
    }

    public void onError(Request request, Throwable throwable) {
        Window.alert(throwable.getMessage());
    }
});

After making these changes, you should be able to authenticate with Spring Security's form-based configuration. While this example doesn't show how to logout, it should be easy enough to do by 1) deleting the JSESSIONID cookie or 2) calling the Logout URL you have configured in your services WAR.

Hopefully this howto gives you enough information to configure your GWT application to talk to Spring Security without modifying your existing backend application. It's entirely possible that Spring Security offers a more GWT-friendly authentication mechanism. If you know of a better way to integrate GWT with Spring Security, I'd love to hear about it.

Update on October 7, 2009: I did some additional work on this and got Remember Me working when using form-based authentication. I found I didn't need as much fancy logic in my ProxyServlet and was able to reduce the "followRequests" logic to the following:

if (followRedirects) {
    if (httpMethodProxyRequest.getResponseHeader("Set-Cookie") != null) {
        Header[] headers = httpMethodProxyRequest.getResponseHeaders("Set-Cookie");
        if (headers.length == 1) {
            extractCookieFromHeader(httpServletResponse, headers[0]);
        } else {
            // ignore the first header since there always seems two jsessionid headers
            // and the 2nd is the valid one
            for (int i = 1; i < headers.length; i++) {
                extractCookieFromHeader(httpServletResponse, headers[i]);
            }
        }
    }
    httpServletResponse.sendRedirect(
            stringLocation.replace(getProxyHostAndPort() + getProxyPath(), stringMyHostName));
    return;
}

I was also able to remove the setProxyRequestCookies() method completely as it no longer seems necessary.

Next, I'd like to figure out how to make Spring Security more Ajax-friendly where it can read an authentication token in the request body or header (instead of from a cookie). Also, it'd be sweet if I could convince it to return error codes instead of the login page (for example, when a certain header is present).

Posted in Java at Aug 06 2009, 08:50:15 AM MDT 10 Comments

How to do cross-domain GWT RPC with a ProxyServlet

Last week, I started working on a new project using GWT. On my last project, we used GWT HTTP Calls and my new project is using RPC. We'll likely migrate to a JSON backend eventually, but in the meantime, I wanted to be able to develop in hosted mode (localhost:8888) and call services on another host (localhost:8080), where the services are running in a JSF/Spring webapp.

At first, I thought it'd be easy thanks to the handy-dandy ProxyServlet I mentioned in Implementing OAuth with GWT. However, when I tried to hook it in and use it, I saw the following error in my server-side logs.

java.lang.NullPointerException
        at javax.servlet.GenericServlet.getServletName(GenericServlet.java:322)
        at javax.servlet.GenericServlet.log(GenericServlet.java:277)
        at com.google.gwt.user.server.rpc.RemoteServiceServlet.doGetSerializationPolicy(RemoteServiceServlet.java:219)
        at com.google.gwt.user.server.rpc.RemoteServiceServlet.getSerializationPolicy(RemoteServiceServlet.java:117)
        at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader.prepareToRead(ServerSerializationStreamReader.java:429)
        at com.google.gwt.user.server.rpc.RPC.decodeRequest(RPC.java:234)

Looking at RemoteServiceServlet.java:219, there's a logging call that fails for some reason (at least in my application).

/*
 * Check that the module path must be in the same web app as the servlet
 * itself. If you need to implement a scheme different than this, override
 * this method.
 */
if (modulePath == null || !modulePath.startsWith(contextPath)) {
  String message = "ERROR: The module path requested, "
      + modulePath
      + ", is not in the same web application as this servlet, "
      + contextPath
      + ".  Your module may not be properly configured or your client and server code maybe out of date.";
  log(message, null);
}

In the above code, you might notice that GWT is checking to make sure the client is hosted in the same application as the server. After I figured this out, it was pretty easy to modify my ProxyServlet to trick GWT RPC into thinking the client was in the same web application. In the ProxyServlet's handleContentPost method, I added the following code to replace "localhost:8888/" with "localhost:8080/services/" (in the content of the post to the server).

if (contentType.startsWith("text/x-gwt-rpc")) {
    String clientHost = httpServletRequest.getLocalName();
    if (clientHost.equals("127.0.0.1")) {
        clientHost = "localhost";
    }

    int clientPort = httpServletRequest.getLocalPort();
    String clientUrl = clientHost + ((clientPort != 80) ? ":" + 
                       clientPort : "");
    String serverUrl = stringProxyHost + ((intProxyPort != 80) ? ":" + 
                       intProxyPort : "") + httpServletRequest.getServletPath();
    postContent = postContent.replace(clientUrl , serverUrl);
}

After manipulating the posted content, I was successfully able to use GWT RPC cross-domain.

Woo hoo!

For your convenience, the full handleContentPost() method is listed below.

private void handleContentPost(PostMethod postMethodProxyRequest, 
                               HttpServletRequest httpServletRequest) 
            throws IOException, ServletException {
    StringBuilder content = new StringBuilder();
    BufferedReader reader = httpServletRequest.getReader();
    for (;;) {
        String line = reader.readLine();
        if (line == null) break;
        content.append(line);
    }

    String contentType = httpServletRequest.getContentType();
    String postContent = content.toString();

    if (contentType.startsWith("text/x-gwt-rpc")) {
        String clientHost = httpServletRequest.getLocalName();
        if (clientHost.equals("127.0.0.1")) {
            clientHost = "localhost";
        }

        int clientPort = httpServletRequest.getLocalPort();
        String clientUrl = clientHost + ((clientPort != 80) ? ":" + 
                           clientPort : "");
        String serverUrl = stringProxyHost + ((intProxyPort != 80) ? ":" + 
                           intProxyPort : "") + httpServletRequest.getServletPath();
        postContent = postContent.replace(clientUrl , serverUrl);
    }

    String encoding = httpServletRequest.getCharacterEncoding();
    debug("POST Content Type: " + contentType + " Encoding: " + encoding,
          "Content: " + postContent);
    StringRequestEntity entity;
    try {
        entity = new StringRequestEntity(postContent, contentType, encoding);
    } catch (UnsupportedEncodingException e) {
        throw new ServletException(e);
    }
    // Set the proxy request POST data
    postMethodProxyRequest.setRequestEntity(entity);
}

Update: In the comments, Ganesh asked for more details, so I figured it'd be a good idea to post the full source code. First of all, click here to see the code for the ProxyServlet:

I generally subclass ProxyServlet to provide my own configuration:

public class MyProxyServlet extends ProxyServlet {

    @Override
    public void init(ServletConfig servletConfig) {
        setFollowRedirects(true);
        setRemovePrefix(false);
        setProxyPort(8080);
    }
}

Here's another example that reads configuration settings from web.xml and proxies to a different domain name:

public class AlternateHostProxyServlet extends ProxyServlet {

    @Override
    public void init(ServletConfig servletConfig) {

        setProxyHost(servletConfig.getInitParameter("proxyHost"));

        String secure = servletConfig.getInitParameter("secure");
        if (secure != null) {
            setSecure(Boolean.valueOf(secure));
        }

        setFollowRedirects(false);
        setRemovePrefix(true);
        setProxyPort(80);
    }
}

After you've added these to your project, simply map the servlet (and its path) in your *.gwt.xml file (if you're using GWT) and your web.xml.

Posted in Java at Aug 05 2009, 04:06:12 PM MDT 17 Comments

My Summer Vacation in Montana

As an adult, I've often reflected upon my childhood and one of my favorite times of the year: Summer Vacation. The feeling that fills your mind and body on that last hour of school before summer break is simply awesome. I don't know when I began longing for that feeling again, but I'm pleased to say I enjoyed it again this year. I took an entire month off from "working" and enjoyed a proper summer vacation at my family's cabin in Montana.

My plan to take a month off began formulating years ago when we'd drive to the cabin for the 4th and only spend a few days before driving back. The trip was never long enough.

I started talking about spending all of July at The Cabin a couple years ago, but never committed to it. My opportunity finally came last December when Julie asked me if she could take the kids to Florida for 3 weeks for Christmas. I quickly realized that, as a kid, 3 weeks in Florida was an awesome opportunity and agreed to give up my time with them. However, I also realized I could get some negotiating power from the agreement and said "only if I can take them to the cabin for 3 weeks next summer".

For regular readers, you know the rest of the story. My Dad flew to Denver for Father's Day Weekend, stayed the following week and then we embarked on our journey to Montana on Monday, June 29th. The first week was great and we had tons of fun during the rest of July. Here's a list of some highlights:

  • Running a 5K while Abbie and Jack ran the 1 mile "fun run" with my Mom. It was Abbie's first race and she got 1st place with a time of 11:05.
    Ready for the Celebrate the Swan Race Abbie wins! 1 Mile in 11:05.
  • Hiking to Holland Falls.
    Hike to Holland Falls Holland Falls Mom at Holland Falls
    Feeding the Chipmunk Holland Falls View of the Missions from Holland Falls
  • Picking Huckleberries.
  • Greeting my sister after she rode her motorcycle for 13 hours in the rain.
  • Spending my birthday at Big Sky Waterslides, something I used to do every year for my birthday.
  • Golfing with my family in Columbia Falls.
    Golfing at Meadow Lake Golf Course Meadow Lake Golf Course
  • Building a set of bunkbeds for the kids.
    Helping with Bunkbeds Making Bunkbeds
  • Rides on "Rocket" (4 wheeler) with the kids, especially when Jack would say "go as fast as Rocket can go".
  • Golfing with my good friend Owen in Seeley Lake.
    Owen has mad golfing skillz
  • Prepping and pouring the floor on the New Cabin.
    Tamping the Floor Floor ready for Cement Time to Pour the Floor
    Floor Pouring Crew Floor Pouring Crew Floor Finished!
  • Fishing with my Dad in "Joe's Jungle" and installing a new window in the cabin afterward.
    New Window Best. Fit. Yet.
  • Teaching Abbie and Jack how to shoot a gun (a .22 pistol) for the first time.
  • Getting my next gig.
  • Traveling to Idaho for Clint and Autumn's Wedding Reception (they got married last December in Costa Rica).
    Happy Couple and Family Howdy Horseshoes Beautiful
  • Seeing Abbie get all googly-eyed over an older boy in Idaho.
    Water Fight! Abbie, Devon and Jared
  • Staying up to watch the sunrise in Idaho.
  • Milling D Logs for the kids' Treehouse.
    D Log Maker D Logs for the Treehouse D Logs on Treehouse
  • Making it from The Cabin to Denver in 13 hours with only one speeding ticket.

More than anything, it was great to spend so much time with my parents and children. The Cabin is a very special place to me and I'm proud I can take my city-slicker kids to let them experience the outdoors and live like I did growing up. In the city, they always have an adult nearby and are never left on their own for long. They wear helmets when they ride their bikes in the alley, even though they have training wheels on and couldn't fall over if they tried.

In Montana, the rules all change.

In Montana, the adults worked on the New Cabin and the kids were left to occupy themselves. Their imaginations ran wild and they played with each other for hours every day. They made me see and remember one of my favorite things about childhood - infinite possibilities. As I kid, I was a dreamer and used to think that anything was possible in the world (flying, teleporting, you name it). I'm happy to see that Abbie and Jack believe that anything is possible too.

A week ago, I returned to Denver after driving almost 4000 miles in 30 days. I feel refreshed after so much time off. I've got my goals and ambitions in line for the rest of the year and I'm looking forward to writing more, smiling more and doing great things for my new client. Over the next several months, I'll continue to work with GWT and even have plans to talk at The Rich Web Experience in December. When I travel for work, I'll be in Boston and Cupertino. Hopefully I'll see some of you along the way.

As usual, you can see all my pictures from the past few weeks in my Montana 2009: Weeks 2-4 set on Flickr.

Posted in General at Aug 01 2009, 11:38:40 AM MDT 1 Comment

Raible Road Trip #13 Trip Report

Mount Rushmore Last Monday morning, my Dad, Abbie, Jack and I loaded up our rig and embarked upon Raible Road Trip #13. We rolled through Custer, South Dakota around 4:30 in the afternoon and arrived at Mount Rushmore just after 5. After gawking at Rushmore, we took a meandering route through 1-car tunnels and Custer State Park. We saw a plethora of bison, some antelope and lots of nice campsites.

Buffalo in Custer State Park Antelope in Custer State Park Campsite near Custer

On Tuesday, we woke up early and began the 9-hour drive to Fairmont Hot Springs. We pulled in right around 5 and had a blast in the pool and on the water slide. When we got there, we discovered that the pools were open 24 hours. Abbie and I were still up when my Dad and Jack fell asleep, so we snuck out and played in the pool by the fading light of the 10:00 sunset.

Fairmont Playground Kids loved the slide

On Wednesday, we arrived at The Cabin around 5 after a brief stop in Missoula to get some clown costumes (for the parade) and have some of the best ice cream in the world (according to Jack). Abbie learned how to chop wood and Jack got to ride on all the tractors. My Mom arrived from Oregon later that night.

Ha yah! Learned how to chop wood for the first time Driving the Ford

Thursday and Friday, we worked on The New Cabin and got ready for the Swan Valley 4th of July Parade. While camping in Custer, Abbie and I decided to be clowns for the parade and we were fortunate enough to find costumes in Missoula. My Mom had to drastically shrink Abbie's to fit, but her hard work paid off when Abbie won 1st Place among all the walkers. She was sooo cute as a little clown and I was a proud Dad for pulling off another fun parade.

Abbie the Clown Clown Family

After the parade, we ate some huckleberry ice cream and watched the O-Mok-See for a couple hours. Then we joined up with my friend Owen and his family and enjoyed an afternoon boating on Holland Lake. We closed the night watching fireworks and got to bed really late.

Since we've been here, we've seen a couple bears (while riding the 4-wheeler with each kid) and my Mom saw a mountain lion walk in front of the cabin this morning. The mosquitos are vicious, but the weather is beautiful. For more pictures from the last week, see my Montana 2009 - Week 1 set on Flickr.

Posted in General at Jul 05 2009, 01:30:17 PM MDT 2 Comments

Raible Road Trip #13

Another year has passed and it's time for the annual trek to The Cabin for the 4th of July. Last year, we took the route through Yellowstone. This year, we're going to shake things up a bit and head through Mount Rushmore.

One of the highlights of this trip is sure to be Fairmont Hot Springs. We're staying their tomorrow night and I'm sure Abbie and Jack will love it.

After 3 weeks at The Cabin, we'll be heading to Clint and Autumn's wedding reception in Idaho. Having so much time off from work is sure to be strange, but I'm sure I'll get used to it.

Happy 4th everyone!

Posted in General at Jun 29 2009, 06:46:04 AM MDT 1 Comment