Tuesday, November 20, 2007

JRuby and Hibernate

The beautiful thing about Rails on JRuby is that you have access to Java. Perhaps you have existing code you'd like to reuse or maybe there's some functionality in a Java library you'd like to use. Whatever the reason, it's good to know how to do it.

So today I created a new Rails app to demonstrate how to use Rails' controllers and views with some model classes from one of my earlier Java projects. My old app used the typical HibernateUtil singleton (section 1.2.5 of the Hibernate docs). I also liked the ActiveRecord pattern so my Java domain classes had some handy "find" methods. So in order to make this work in a Rails app, I installed the goldspike plugin:
script/plugin install http://jruby-extras.rubyforge.org/svn/trunk/rails-integration/plugins/goldspike

and then ran the rake task war:standalone:create

I copied the classes and lib directories from my old apps' WEB-INF directory to the WEB-INF directory in my Rails app and then generated a controller and modified it to look like this:
require 'java'
import 'com.example.domain.Person'
import 'com.example.util.HibernateUtil'

class PersonController < ApplicationController
def list
with_transaction do
@people = Person.find_all.to_a
@people.sort!{|p1, p2| p1.name.downcase <=> p2.name.downcase}
end
end

def with_transaction
begin
tx = HibernateUtil.session_factory.current_session.beginTransaction
yield
tx.commit
HibernateUtil.session_factory.current_session.close
rescue
tx.rollback
end
end
end

Finally, I added a list.rhtml template for the "list" action and voila! I have a Rails-ish app that uses Hibernate instead of ActiveRecord.

Saturday, November 03, 2007

Ruby Cocoa

RubyCocoa and the Scripting Bridge are two software development technologies bundled with Leopard. The app presented here demonstrate how to create a native Mac interface written in Ruby that communicates with iTunes via Applescript. It does nothing more than display the iTunes version number.
  1. Create a "Ruby-Cocoa Application" from the "New Project" dialog in XCode
  2. Add a "Ruby NSObject subclass" to the project called "ITunesController"
  3. Add an ib_outlet for a text field and an ib_action called "show_version" to the ITunesController ruby class.
  4. Generate an Objective-C header file that scripting bridge can use to communicate with iTunes. Use the two terminal command, sdef and sdp, within your project folder like this: "sdef /Applications/iTunes.app | sdp -fh --basename iTunes"
  5. Add the header file to your project.
  6. Add "ScriptingBridge.framework" to your project.
  7. Open "MainMenu.nib" in Interface Builder and add a button and a label to the Window. Also instantiate an NSObject subclass.
  8. Set the NSObject's class to "ITunesController". (You may need to select the "Read Class Files..." menu item to get ITunesController to show up as a valid option)
  9. Wire the button to your controller's "show_version" method and wire the controller's text_field outlet to the label.

Here's what my project looks like:


Here's what my Ruby "ITunesController" class looks like:

require 'osx/cocoa'

include OSX
class ITunesController < NSObject
ib_outlet :text_field
def show_version(sender)
iTunes = SBApplication.applicationWithBundleIdentifier:'com.apple.iTunes'
@text_field.setStringValue("iTunes version: #{iTunes.version}")
end
ib_action :show_version
end

Here's what the app looks like:


Now you should be able to run the project and get the iTunes version number displayed in your own application. Yeah, the end result is kind of lame but the journey was pretty cool. The interesting bits for me were:
  1. My Ruby class extends Cocoa's NSObject class.
  2. The class methods ib_outlet and ib_action are parts of the Ruby DSL for doing Cocoa development with Ruby.
  3. The RubyCocoa framework makes it really simple to swap out Objective-C for Ruby when doing Cocoa development.
  4. Everything else is the same. I still use XCode for my controllers and model and still use Interface Builder to graphically build my GUI and bind it to my code. The only difference is that I get to use Ruby and all of its beauty to get the job done.
  5. The downside is that I don't get anything too useful in the way of code completion while in XCode. Still might be worth it if you really dig Ruby.

Friday, November 02, 2007

Yahoo Address Book & Leopard

Leopard's new address book allows you to sync with your Yahoo! contacts.  It's not intuitive so here goes:
  1. Open the general tab of Leopard's Address Book preferences.
  2. Check "Synchronize with Yahoo!"
  3. Click on the Configure Button and enter your Yahoo! ID and Password
  4. Open iSync's Preferences (here's where it gets weird)
  5. Check "Show status in menu bar"
  6. Select "Sync Now" from the new menu bar icon on the right
  7. Enjoy!
Of course you should back up everything before you try this.  I've been using Yahoo! Mail for years so it's my most correct and complete contact list.  I'm glad I can now benefit from this in Leopard.  Now if Yahoo! would just be as good as Google and give me IMAP support for free or if Apple would give me a GMail Address Book Sync, I'd be able to use Apple's mail client most of the time.

Now that I think about it, it'd also be nice to have iCal sync up with either Yahoo's or Google's calendar too!


Sunday, October 28, 2007

Leopard Installed

It's my second day running with Leopard and just about everything has gone smoothly.  I installed it without too much trouble on my MacBook and my PowerMac G5.  I say not too much trouble because not everything was perfect.  

Twice since installing Leopard on my G5 it's not gone to sleep on demand.  Instead it goes into a coma and starts snoring.  By that I mean all the USB devices shut off, the video signal goes away but the computer doesn't quite sleep.  Instead, all the fans start to spin up. For anyone that's never heard a G5's fans go nuts... it's like a small jet engine.  The only recourse is a hard restart.  I installed the first Leopard update and it hasn't repeated this stunt but I'm keeping an eye on it just in case.

The second thing is that MySQL won't start.  I've read a few things on the Internet and it just doesn't look the MySQL project is ready for Leopard. They had bug reports before 10.5 shipped but decided to just wait until it went gold. I guess I'll just have to patiently wait for an update.  No biggie.  I'll just use the built-in SQLite instead.

The third thing that I wasn't prepared for was the retirement of Classic OS 9 support.  I don't personally use any OS 9 apps  but my kids used to play the occasional game.  Sorry Rolly McFly.  Your days are over.

The last thing that isn't too big of a problem but a big disappointment is that the Java 6 SDK is MIA.  I guess I never really saw official mention of it, but I really expected Java 6 to ship with Leopard.  C'mon Apple!  I was there when Steve Jobs and Avie Tevanian stood up at JavaOne and said they'd have the best Java implementation on any platform.  You're definitely letting me down on this one.

Now onto the good news.  Everything else just works!  Mail is cool.  Safari is blazingly fast.  Dropped network connections don't mean a frozen finder anymore. Quick Look is sweet.  And Ruby is built-in.  I just deleted my old custom Ruby installation and everything (other than MySQL) continues to work.  My IDEs function properly (Eclipse & NetBeans) and despite the lack of Java 6, Java 5 on my Mac is still noticeably faster than Java 5 on Windows at work.  I don't mind the translucent menu bar and Cover Flow in the finder is nice but mostly unnecessary for me.  

The biggest shock to the system was the developer tools.  I've built the occasional Mac app, and while I'm no wizard at it, I got along just fine thanks.  So naturally I wanted to try the Ruby/Cocoa integration.  Then I opened Interface Builder.  Yikes!  That certainly  got a major overhaul!  Looks better, but now I see that I need to spend some time trying to figure out what's what.

So if someone asks whether they should upgrade or not.  I'd definitely give it the thumbs up.  Just make sure you don't need classic and if you're a geek like me, be prepared for a little bit of adjustment to the changes under that spiffy new exterior.

Thursday, October 25, 2007

JRuby and Rails in the Enterprise

At work, management is finally getting the message about dynamic languages like Ruby and starting to recognize that frameworks like Rails may have productivity benefits.  We're actually going to look at doing a pilot project.  I like to think that I've had some influence in that but you never know...

Anyway, I would love to make this work but this is a Java shop through and through.  We can only deploy to a Java web container, specifically Weblogic 8.1 (the transition to Weblogic 10 is happening but it's months away).  I've used JRuby (works great BTW) and the JRuby extras to help me deploy an app to Tomcat, but I can't manage to get JRuby and Rails working in Weblogic 8.1.  Any ideas?

Secondly, Groovy and Grails is the other contender.  I like the idea behind Groovy and Grails but I can't help feel that it's a pale imitation of Ruby and Rails.  Anybody have enough experience to compare and share?

Finally, I'm not naive enough to think that this will cure all the ills of working in the "big enterprise".  When I think about all the things that bug me, the technology is probably the smallest thing that causes me pain.  But on the other hand, this would be a wonderful distraction. :-)

Leopard

I think it's fair to say that I could be characterized as a Mac "fanboy" from time to time.  I've been known to spout pro-Mac statements occasionally.  I take some unfounded pride in the fact that Apple's market cap is now bigger than IBM or Intel. And I love the fact that Apple just had yet another record quarter....

Perhaps it's just the validation of what I've felt in all the years that I've used a Mac, it's just plain better than Windows!  So now I sit here waiting for my pre-order of Mac OS X - Leopard to get to my house.  It's supposed to arrive tomorrow but the status already changed to "shipped".  To fill some of my waiting time I watched Apple's guided tour. I read some of the reviews (evolutionary not revolutionary) but I still can't help but be excited.  It's like a little bit of Christmas in October.

I'm sure I'll be underwhelmed a bit once the glow of a new OS has worn off but for awhile I'll revel in my fanboyism.  So if you too are a bit excited about the next Mac OS release, roll over to AppleInsider and feel free to take part in the unbridled Mac love fest and read some of the "Road to Mac OS X Leopard" series. Some of them are quite well written and will give you a good perspective of where Apple and NeXT have been and how the features in Leopard came to be.  Ignore the fact that some of the statements are a little too biased toward Apple and just enjoy the moment...

Thursday, September 27, 2007

Marching on...

I used to develop applications with PowerBuilder (PB) for about 8 years.  I started with PB 1.0.  In the beginning it was new and exciting.  Windows was new.  Client/Server was new and even object oriented development in a mainstream tool was relatively new (at least in the corporate software development scene).  But as technology progressed I began to see exciting things elsewhere.  At the time Delphi was the cool new product.  The speed, the beauty, the elegance... ahhh I was smitten.  But the other PB developers around me didn't see it.  I fought for Delphi and even did one or two project with it.  But unfortunately Delphi wouldn't become my mainstream development tool.  PB was entrenched and the developers around it protected it with a passion.  It was disappointing. 

Then Java came along.  To my eye Java shared a lot in common with Delphi.  I liked it too.  But still the PB developers around me couldn't seem to appreciate this new tool. In fact, a lot of them were actually pretty hostile to anything that might threaten the sacred cow that was PB.  Fortunately for me the hype of the Internet and the .COM boom turned Java into something people couldn't ignore.  And I decided to ride the wave and finally leave PowerBuilder behind.

Now another 8 years has gone by and I find myself in the middle of a community of Java developers who are extraordinarily similar to that crowd of PowerBuilder developers.  They only see Java.  They've built a career around it, they're entrenched and they protect it with a passion.  For me, I see the appeal of other languages and tools.  In particular Ruby is beautiful and Rails elegantly solves many of my Java web development headaches.  But many of the people around me don't see it and quite frankly I doubt that they've even looked.  And that's the disappointing thing.  They're just like the PB guys with the blinders on.  They're hostile to anything that isn't Java.

In my current group there seems to be a new love forming for GWT.  Personally, the Google Web Toolkit and its unmistakable Swing/AWT flavor of Web development just seems wrong.  It's definitely web development for Java developers and I can see why they like it, but to me it's an artificial abstraction that doesn't sit well with me. Ruby and Rails isn't perfect either, but even a couple years after I started looking at it I still think it's better than GWT.  Rails embraces the browser technology of HTML, CSS, and Javascript and makes it easy to work with.  GWT on the other hand, puts me behind a Java facade where I can pretend to be developing Swing and it will generate the HTML and Javascript for me.  Ick.  No thanks Google.

Anyway, my point is that I think I'm getting to another point in my career where I need to migrate to a new place with like-minded people.  I'm tired of trying to get others to see what I think is self-evident if only they'd look...

Friday, September 07, 2007

Java & Ruby HTTP Clients: Part 2

Several months ago I wrote an article about how to create an HTTP client in Java or Ruby. I included examples in both languages for getting by BASIC and FORM based authentication. I also showed you how to resubmit the value of an HTTP cookie that many websites use to store state.

What I didn't mention is that quite often web applications store state not in a cookie but within the HTML itself. In order for you to programmatically interact with the website, you'll need to get that data out of the HTML and put it in your next request.

Java

So the basic recipe is to to use HttpClient (the way I demonstrated last time) to get the raw HTML. Then feed that text into NekoHTML, an HTML parser. It can correct the various problems you see in old-school HTML, namely unbalanced tags, missing parent tags, and mismatched elements.

Neko returns a standard Document object, but personally, I don't care much for the standard XML API. Instead I prefer to use the simpler API of DOM4J. So the next step is to take the XML that NekoHTML provides and feed it into DOM4J so that I can use XPath expressions to find what I need.

Now the question becomes what data do you need to post. Well my answer is to simply look through the FORM you're trying to submit, and resubmit everything. Look for 'hidden' tags, input tags of type 'text', 'password', and 'select', gather the names and values of all those tags. Override the ones where you need to provide the information (like id and password for example) and then do the POST.

Ruby

The Ruby approach is exactly the same: get the raw HTML, parse it, use XPath to get the name-value pairs of the FORM elements, override some of the values, and resubmit. The only additional rubygem from my last article is hpricot. It provides the same functionality of NekoHTML and DOM4J in Java. The typical script might look like this:


require 'net/http'
require 'rubygems'
require 'hpricot'

res = Net::HTTP.new('myserver', 80)
# res.set_debug_output $stderr #uncomment this to get console debug info
res.start do |http|
#go to the first page
get = Net::HTTP::Get.new('/home.aspx')
response = http.request(get)

#collect the cookie information
cookies = ''
response.response['set-cookie'].split(';').each{|c|
cookies += c.split(/path=.*?,/).last.strip + ';'
}

#collect the existing form data
doc = Hpricot(response.body)
form_data = {}
['text', 'password', 'hidden'].each{|t|
elements = doc.search("//form[@name='loginForm']//input[@type='#{t}']")
elements.each{ |e|
form_data[e['name']] = e['value'].to_s
}
}

#override some of the values
form_data['username'] = 'my_username'
form_data['password'] = 'my_secret'

#login
post = Net::HTTP::Post.new('/login.aspx')
post.set_form_data(form_data)
post['Cookie'] = cookies
puts http.request(post)
end

In this case all I did was print out the resulting HTML. At the very least you'd probably do the hpricot thing one more time to retrieve the data in which you're interested.

Other

Finally one last gotcha... At least half the websites I've tried to scrape do something "interesting" with Javascript to set various form elements. Since we don't have a Javascript engine executing you should expect that you'll have to parse the HTML and Javascript yourself to figure out what's going on, and set the fields manually in your script. I highly recommend Firefox and the "Web Developer" and Firebug plugins for inspecting the HTML, JS files and the HTTP Requests that the browser submits.

P.S.: I was a bit lazy this time and didn't provide any Java code. If you're really having trouble and can't get it working, leave me a message and I'll put together an example. Secondly, there are other Java HTML parsers out there that may work just as well or maybe even better than Neko, but since I don't have any personal experience with them I didn't mention them. If you like something else, leave a message.

Thursday, May 03, 2007

Aptana RADRails & JRuby

I thought I'd go and take a look at what's been happening on the RADRails project lately. As it turns out, quite a bit! Aptana has taken over the project and are making quite a bit a progress integrating it with their Eclipse based IDE. The most impressive thing I saw was refactoring support! Fantastic! It's great to see RADRails getting some attention again. Go download the beta.

Then I went over to the JRuby project to see what they've been up to. JRuby 0.9.9 is out and looks very good. The Java integration is quite good. But I've been mostly interested in seeing support for deploying a Rails app in a JEE Web Container. Well nestled in beside the JRuby TAR file is a file called sample-rails-warfile.tar.gz. I downloaded it and pretty quickly had a rails application running in Tomcat. The app doesn't do much of anything but it's still pretty impressive to see it. My hope is that this kind of integration will help to get Ruby & Rails adopted into some of Java-only environment I tend to work in. The next thing I did was build my own Rails app and deploy it in Tomcat. I also tried deploying it in WebLogic but didn't have as much luck. Obviously there's still some work to do. Check out the JRuby WIKI for details.

Friday, April 13, 2007

Ruby OLE

The other day someone came over to my desk and asked me how they could programatically invoke the "Find Computer" function of the Windows shell. Normally you just press CTRL-WINDOWS-F and up pops an Explorer window ready for searching. I didn't have a quick answer for him... so I immediately took up the challenge to figure it out.

After a couple dead ends I started looking at Windows Script Host (WSH). It allows you to write VB or JavaScript to access the ActiveX API of a Windows application. After a bit of googling I found the "Shell.Application" documentation on MSDN. After futzing around with Javascript for a few minutes I started thinking that I should be able to do the same things with Ruby. So with a little more searching I discovered that by adding "require 'win32ole'" at the top of my ruby script I could instantiate an instance of "Shell.Application".

The only problem left was finding the right API call. A three line Ruby script like this gave me a list of methods:

require 'win32ole'
wsh = WIN32OLE.new('Shell.Application')

wsh.ole_methods.collect{|m| m.to_s}.sort.each{|m| puts m}


A quick change and my final script looked like:

require 'win32ole'
wsh = WIN32OLE.new('Shell.Application')

wsh.FindComputer()


or in JavaScript:

wsh = WScript.CreateObject("Shell.Application");
wsh.FindComputer();


If you want to take this scripting stuff to the next level I suggest you go check out David Mullet's "Ruby on Windows" blog. He describes some of the same things I just demonstrated above and goes into a lot greater depth about how to use Ruby to script think like Word and Excel.

And in case you're wondering why I had to use Ruby to get a list of methods instead of just looking it up in MSDN, well just try looking at the MSDN page I referenced above with Firefox instead of IE.... That's right... the site navigation only works correctly in Internet Explorer.

Tuesday, April 10, 2007

It's spelled S-Q-L

Why do so many Java developers suck at SQL? Are they just born that way? Are they brainwashed? Do they think that Hibernate will just do it all for them? What's the problem?

The reason I ask is that I'm working on a Java system where the developers obviously thought nothing of letting Hibernate lazily instantiate every relationship as they iterated through collections and navigated down relationships. Not once did they stop to consider the SQL that was being thrown across the network to the database or the performance penalty that it would incur. Just a couple weeks ago I turned on Hibernate's SQL logging just to see 631 queries go flying by in order to prepare the data for a single JSP. WTF?! 631 Queries!!?

Then today I saw another atrocious example of bad code that instantiated several collections of some very large classes from the database to just throw them all away after navigating through them to get a count. Whatever happened to count(*)? In the end I replaced hundreds of lines of convoluted Java code with about twenty lines of SQL. Yes SQL, not HQL (which rocks BTW), but just wasn't appropriate for this task.

Perhaps it's unfair of me to pick on Java developers but for one reason or another a lot of the Java developers I run into have this resistance to leveraging the relational query engine at their disposal. They don't strike a good balance between the object oriented and relational worlds that their systems stride. Use the right language for the right task. And if you can't determine when to turn to SQL, turn on SQL logging and watch what the heck is going on. When you see SQL tearing by, just pause for a moment and ask yourself if there's a better way to do what you want. And remember you have decades of querying technology just waiting to be used. Don't be afraid of writing an elegant query.

Tuesday, April 03, 2007

Prototype, Sitemesh and Struts

The Java environment for my current project is stuck at Java 1.4, but that doesn't mean I have to build my webapps like it's 2002 (that's when 1.4 was released). As a matter of fact I decided to take some lessons from Ruby on Rails.

First of all I decided to ditch Tiles in favor of Sitemesh. Tiles gets the job done but at the expense of a little too much abstraction for my taste. Sitemesh uses the decorator pattern and just simply feels a whole lot cleaner. While not as simple as dropping an RHTML file into a "layouts" directory, it's certainly a whole lot easier than the XML config needed for Tiles.

Secondly, I use DispatchAction to get multiple "action" methods in one Struts Action subclass. It's nothing I haven't done before but keeping the number of classes down is a good thing.

Finally I decided I liked the way RoR uses Prototype and Scriptaculous to provide a little more responsiveness to my webapps. So I decided to jump into Javascript and use Prototype in my Struts webapps. I had to configure Sitemesh to leave my AJAX responses alone and I needed to read a bit of the Prototype docs but it was pretty straightforward. I used the Rails partial convention to let my Java app generate HTML fragments through a Struts action and a JSP and then let my Javascript function replace a piece of the DOM with the result of the AJAX call.

When given lemons, make lemonade. When given ancient Java technology, spice it up with the techniques you learn by looking at alternative implementations.

Objective-C 2.0

Being a bit of a Mac-head and programmer to boot I've taken various looks at writing native Mac OS X apps withe XCode (the IDE), Cocoa (the frameworks), and Objective-C (the language). In general I've come away relatively happy with the toolset. But I must admit, I've filed my own share of enhancement requests with Apple. The top of my list was refactoring support in the IDE. I know it probably sounds lame to the hard core coders but after developing with Java for so long I just expect it and perhaps more embarassingly I feel I need it. Global regex search and replace just doesn't cut it. Secondly, I dislike all the boiler-plate "noise" that appears in your code to declare properties (instance var & accessor/mutator methods) and all the manual memory management.

Well Apple's been looking around at the competition and listening to feedback and judging by their website (here and here) and a few message boards (like this one), Apple has a lot in store for developers. Objective-C 2.0 sports Garbage Collection for the first time, some syntactical sugar for iterating over collections, and a nice simple way of declaring properties. XCode provides refactoring (YAY!) and more enhancements to keep your eyeballs on the code while editing and debugging. And even Interface Builder got cleaned up a bit and exposes nice simple ways of using core animation in Leopard. I'm looking forward to seeing it all in action.

BTW: I read on the RubyCocoa mailing list a few months ago that it might be bundled with Leopard too. Writing Mac OS X apps with Ruby... very cool... Have to wait and see if that one happens.

The New C#

About a year and a half ago I blogged about LINQ (Language INtegrated Query). Anders Hejlesberg starred in a video demoing object/xml/database query functionality and some new language features that Microsoft was playing with.

It's been in the oven for quite a while so it should be just about done, and as a matter of fact it looks like C# 3.0 is scheduled to be released this year. I suggest checking out this MSDN site to read the details for yourself or watch some of the videos. Things like implicitly typed local variables, extension methods, lamda expressions, and anonymous types all seem to be inspired by the features of the so-called scripting languages like Ruby, although Microsoft goes out of their way to make sure you don't confuse the similarity in syntax with the underlying implementation (and with good reason).

In short I'm still quite impressed. I've seriously looked at C# in the past and mostly dismissed it as a Java clone. But with these new features I think it's finally coming out from under it's Java shadow and beginning to shine on its own.

Thursday, March 22, 2007

My MacBook

Over the past few weeks, the developers at work have been taking turns bringing in their laptops... It wasn't something we planned but just started happening spontaneously. One guy brought his Centrino laptop running Linux, another guy brought a 17" screen Dell that he uses for CAD, one guy brought his shiny new Vista laptop and a couple days ago it was my turn to take in my MacBook.

In general my little black MacBook received a fairly warm welcome. People were surprised at how thin it was and the usability of a relatively small 13.3" screen. They liked the built in webcam and although the single button touchpad attracted a bit of grief they quieted down when I showed them that a two finger tap brought up contextual menus. I gave them the flashy demo with Exposé, the magnifying dock and Dashboard and gave them the super abbreviated tour of the iLife and iWork applications.

But in the end the one thing that seemed to get the most attention was the MagSafe Power Adapter. From the magnetic connector to the little brick with "wings" for winding the cord and a removable AC adapter, people definitely liked the design. But having just checked the Apple Store comments on this little item I'm hoping that my little white brick lasts a little longer than everyone else's. Yikes!

But other than the black finish which picks up fingerprints like nothing else I've ever owned, I'm very happy with my laptop. And now my co-workers don't think I'm so crazy for owning a Mac.

Saturday, January 06, 2007

Java & Ruby HTTP Clients

In a mythical IT universe designed around a service oriented architecture (SOA) you could assemble several loosely coupled, autonomous services into a business solution. Each service would communicate in a platform and technology agnostic manner and XML would be the lingua franca. For example, if you needed to integrate data from several business partners you could call various SOAP or RESTful services, get structured XML, and then transform the results into something you could use.

But unfortunately that's not what happens in the real world. Instead of nice composable services you usually get web sites targeted at people not machines. That means you need to automate what would typically be browser conversations with various websites to get the data you need. And then you need to deal with the format of the resulting data. If you're lucky, the data may be structured in a comma separated value (CSV) text file. But undoubtedly you'll have to parse unstructured text representations of a report or get the data out of an excel spreadsheet or even a PDF. It's not pretty.

But lets forget that unpleasantness for the moment and deal with the first problem you'll encounter in trying to automate a browser conversation, getting by the various authentication mechanisms. Let's look at BASIC authentication first. It's pretty common and well supported in the Java-based Jakarta Commons HttpClient library and the Ruby Net::HTTP Standard Library.

BASIC Authentication

To start things off I created a simple Java-based "Dynamic Web Project" using the Eclipse Web Tools Project (WTP) plugins. To keep things simple I created a servlet that returns a string. Then I configured the application to protect the url for that servlet with basic authentication in web.xml. Then in Tomcat's server.xml I modified the context element for my web app to include a reference to the default Tomcat in-memory user database:
<Context docBase="MyWebApp" path="/MyWebApp" [...]
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
debug="0" resourceName="UserDatabase"/>
</Context>
With that in place, and the server running we're able to write a couple of methods to access the servlet. In Java:
public static void basicAuthDemo()
throws HttpException, IOException{
HttpClient client = new HttpClient();
List<String> authPrefs = new ArrayList<String>();
authPrefs.add(AuthPolicy.BASIC);
client.getParams().setParameter(
AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs);

client.getState().setCredentials(
new AuthScope("localhost", 8080, "localhost:8080"),
new UsernamePasswordCredentials("tomcat", "tomcat")
);

GetMethod get = new GetMethod(
"http://localhost:8080/MyWebApp/myservlet");
get.setDoAuthentication(true);
client.executeMethod(get);
System.out.println(get.getResponseBodyAsString());
get.releaseConnection();
}
In this example I limited HttpClient's default authentication mechanism to BASIC. I know what my target system uses so why complicate matters with DIGEST or NTLM? Then it was a simple matter of defining the credentials and telling HttpClient to automatically use them and then executing the Http GET method. It looks surprisingly similar in Ruby:
def basic_auth_demo
url = URI.parse('http://localhost:8080/MyWebApp/myservlet')
get = Net::HTTP::Get.new(url.path)
get.basic_auth('tomcat','tomcat')
response = Net::HTTP.new(url.host, url.port).start do |http|
http.request(get)
end
puts response.body
end
The difference between the two implementations is that the Java HttpClient is doing some housekeeping for you. You define a scope for your authentication and as long as you GetMethod is configured to do authentication it will automatically pick up any necessary credentials from the HttpClient instance. In Ruby you need to set the credentials on the GetMethod explicitly.

FORM based authentication

The next most common method is form-based authentication. When you make a request for a web resource, the response contains a cookie that identifies your session on the server. If that session indicates that you haven't been authenticated yet, then you're redirected to a form to enter your id and password. You fill in the values and then submit the form. Now assuming you entered the right credentials your session on the server will indicate that you're authenticated and every subsequent request (which includes the cookie to identify your now authenticated session) will execute normally. There are variations on this theme that may add more than one cookie so just be sure to capture the cookies and continue to submit them on every request in your conversation.

In order to test this I modified the web.xml file for my Java web app:
<login-config>
<auth-method>FORM</auth-method>
<form-login-config>
<form-login-page>/login.jsp</form-login-page>
<form-error-page>/login-error.jsp</form-error-page>
</form-login-config>
</login-config>
and added the requisite JSP pages. The login.jsp contains a form that looks like this:
<form method="POST" action="j_security_check">
Username:<input type="text" name="j_username"><br/>
Password:<input type="password" name="j_password"><br/>
<input type=submit value="Login">
</form>
So the Java code to access the servlet using form based authentication looks like this:
public static void formAuthDemo()
throws IOException, HttpException {
HttpClient client = new HttpClient();

// make the initial get to get the JSESSION cookie
GetMethod get = new GetMethod(
"http://localhost:8080/MyWebApp/myservlet");
client.executeMethod(get);
get.releaseConnection();

// authorize
PostMethod post = new PostMethod(
"http://localhost:8080/MyWebApp/j_security_check");
NameValuePair[] data = {
new NameValuePair("j_username", "tomcat"),
new NameValuePair("j_password", "tomcat")
};
post.setRequestBody(data);
client.executeMethod(post);
post.releaseConnection();

//resubmit the original request
client.executeMethod(get);
String response = get.getResponseBodyAsString();
get.releaseConnection();
System.out.println(response);
}
The Ruby code looks like this:
def form_auth_demo
res = Net::HTTP.new('localhost', 8080).start do |http|
#make the initial get to get the JSESSION cookie
get = Net::HTTP::Get.new('/MyWebApp/myservlet')
response = http.request(get)
cookie = response.response['set-cookie'].split(';')[0]

#authorize
post = Net::HTTP::Post.new('/MyWebApp/j_security_check')
post.set_form_data({'j_username'=>'tomcat', 'j_password'=>'tomcat'})
post['Cookie'] = cookie
http.request(post)

#resubmit the original request
get['Cookie'] = cookie
response = http.request(get)
puts response.body
end
end
Again, the two implementations are remarkably similar. The biggest difference is that the Java HttpClient library is again doing the housekeeping, by tracking and automatically resubmitting the cookies for you. In the Ruby code you have to fetch the cookie yourself from the response header and set the HTTP header for all future requests.

So there you go, you're past the website authentication and are ready to make whatever requests you need to get the data you require.

Digg!