Friday, June 26, 2009

Apache, SSL and Tomcat Clustering

With help from the Apache HTTPd docs, the Apache Tomcat docs, and lots of Google-help, I've set up a failover-friendly load balanced server pair that seriously ups our game in terms of high availibility, and boosted performance as a by-product.

Background (skip this if you just want to get to the server setup details)
My group does custom web applications for use the Operating Rooms. We interface with multiple clinical and administrative systems and show, collect, and share data that's critical to the operations and to patient safety. Our longest running application, ORview, is a Java web app that collects pre-operative assessments and post-operative assessments, provides airport monitor-style big screen views throughout the OR area, and provides other billing, reporting and QA/QI functionality. Our newest application is RequestOR, which collects posting requests for new case postings, and shares, via HL7, this data with GE's Centricity Periopertive Manager and IDX. Our old ORview prod server was at end-of-life, and we were needing a new server to deploy RequestOR on so we designed, spec'd and installed a new server cluster that'll meet the needs of both of our major applications, and give us some room to migrate some of our more minor applications (Java and JRuby).

Server Hardware
Qty 2 - IBM HS21 Blade Server, Dual Xenon Quad-core @ 3.0GHz, 8GB PC2-5300 RAM, 146G SAS HDD.

Server Software
Red Hat Enterprise Linux Server release 5.3 (Tikanga)
Java SE Runtime Environment 64-bit (build 1.6.0_13-b03)
Apache HTTPd 2.2.3 (httpd-2.2.3-22.el5_3.1) with mod_ssl (mod_ssl-2.2.3-22.el5_3.1) and mod_proxy_ajp (as part of the httpd install)
Apache Tomcat 6.0.18

Network/DNS Configuration
The key to doing multiple SSL-secured applications is that each unique SSL certificate NEEDS it's own IP address and host name to bind to. (There are some budding ways around that but none of those were mature enough going into this process to be a viable production option.)

requestor: 10.20.215.226, 10.20.215.228 - configured as dns round-robin
orview2: 10.20.215.227, 10.20.215.229 – configured as dns round-robin

Those IPs get distrubuted to each server that's hosting that application, along with each server having their own ip.

server 1
10.20.215.223 – blade1
10.20.215.226 – requestor
10.20.215.227 – orview2
228.0.0.23 - multicast

server 2
10.20.215.224 – blade2
10.20.215.228 – requestor (dns rr)
10.20.215.229 – orview2 (dns rr)
228.0.0.23 - multicast

Apache HTTPd Configuration
First, the explanation. There's a default port 80 host (in black) that just answers requests on the machine's unique IP. We use this index.html to point to a simple machine ident. The RequestOR section is next (in blue). It defines a port 80 host that redirects all requests to the port 443 (ssl) version of itself. The second virtual host in the blue is the ssl version. This section contains config info for the SSL certificates, and a reference to balancer://ajpCluster/requestor as handler for all requests to this virtualhost. The ORview2 section (in red) largely duplicates this configuration for the second ssl application on these servers. The second server's config files are identical, except the IPs are changed to match that server's configuration.

<VirtualHost *:80>
DocumentRoot /var/www/html
ServerName blade1.foo.edu
</VirtualHost>
# RequestOR virtual hosts
<VirtualHost 10.20.215.226:80>
DocumentRoot /var/www/html/requestor
ServerName requestor.foo.edu
Redirect permanent / https://requestor.foo.edu/
</VirtualHost>
<VirtualHost 10.20.215.226:443>
DocumentRoot /var/www/html/requestor
ServerName requestor.foo.edu
SSLCertificateFile
/etc/pki/tls/certs/requestor.foo.edu.crt
SSLCertificateKeyFile
/etc/pki/tls/private/requestor.foo.edu.key
SSLEngine on
SSLProtocol all -SSLv2
<Location />
ProxyPass balancer://ajpCluster/requestor
stickysession=JSESSIONID
</Location>
# ErrorLog logs/dummy-host.example.com-error_log
# CustomLog logs/dummy-host.example.com-access_log common
</VirtualHost>

#ORview2 virtual hosts
<VirtualHost 10.20.215.227:80>
DocumentRoot /var/www/html
ServerName orview2.foo.edu
Redirect permanent / https://orview2.foo.edu/
</VirtualHost>
<VirtualHost 10.20.215.227:443>
DocumentRoot /var/www/html
ServerName orview2.foo.edu
SSLCertificateFile /etc/pki/tls/certs/orview2.foo.edu.crt
SSLCertificateKeyFile
/etc/pki/tls/private/orview2.foo.edu.key
SSLEngine on
SSLProtocol all -SSLv2
<Location />
ProxyPass balancer://ajpCluster/orstat stickysession=JSESSIONID
</Location>
</VirtualHost>


Apache mod_proxy_ajp Configuration
This is the config for the ajp load balancer. Tomcat on each server is set to listen for AJP requests on port 8009. This config file (the same on each server) tells the AJP balancer about the cluseter composed of TomcatA and TomcatB. In the absence of any other details, it'll default to sending new requests to the least loaded Tomcat server, and sending requests from existing sessions to the server that's been handling them. That's what the stickysessions attribute makes happen. Proxy listeners are configured for each application, same color scheme as above.

LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
#
# When loaded, the mod_proxy_ajp module adds support for
# proxying to an AJP/1.3 backend server (such as Tomcat).
# To proxy to an AJP backend, use the "ajp://" URI scheme;
# Tomcat is configured to listen on port 8009 for AJP requests
# by default.
#
<Location /balancer-manager>
SetHandler balancer-manager
</Location>
<Proxy balancer://ajpCluster>
BalancerMember ajp://blade1.foo.edu:8009 route=tomcatA
BalancerMember ajp://blade2.foo.edu:8009 route=tomcatB
</Proxy>
<Location /requestor>
ProxyPass balancer://ajpCluster/requestor stickysession=JSESSIONID
</Location>

<Location /orstat>
ProxyPass balancer://ajpCluster/orstat stickysession=JSESSIONID
</Location>



Tomcat's server.xml
The server.xml is generally a good sized file where most of the defaults are just fine. I've excerpted the relevant bits here that I had to change to get clustering working.

A connector is defined in the <server> portion of the file. It should be enabled by default.

<!-- Define an AJP 1.3 Connector on port 8009 -->
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />


There should be one <engine> element. Edit it to include the unique name used for this in the proxy-ajp config file - tomcatA in this case. The other server's server.xml looks the same except for tomcatB here.

<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcatA">


A cluster element is defined inside the <engine> element. The important thing to set here is the multicast address (in red) to be used by Tribes to synchronize session information across the servers in the cluster. The FarmWarDeployer (in blue) is experimental and (as of when this was written) isn't ready for prime-time.

<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
channelSendOptions="6">

<Manager className="org.apache.catalina.ha.session.BackupManager"
expireSessionsOnShutdown="false"
notifyListenersOnReplication="true"
mapSendOptions="6"/>
<!--
<Manager className="org.apache.catalina.ha.session.DeltaManager"
expireSessionsOnShutdown="false"
notifyListenersOnReplication="true"/>
-->
<Channel className="org.apache.catalina.tribes.group.GroupChannel">
<Membership className="org.apache.catalina.tribes.membership.McastService"
address="228.0.0.23"
port="45564"
frequency="500"
dropTime="3000"/>
<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
address="auto"
port="5000"
selectorTimeout="100"
maxThreads="6"/>

<Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
<Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
</Sender>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.ThroughputInterceptor"/>
</Channel>

<Valve className="org.apache.catalina.ha.tcp.ReplicationValve"
filter=".*\.gif;.*\.js;.*\.jpg;.*\.png;.*\.htm;.*\.html;.*\.css;.*\.txt;"/>

<Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"
tempDir="/tmp/war-temp/"
deployDir="/tmp/war-deploy/"
watchDir="/tmp/war-listen/"
watchEnabled="false"/>


<ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
</Cluster>


Conclusion
It's completely possible to get high-availibility load balancing and clustering working for Apache's HTTPd and Tomcat under Linux. The performance and fault-tolerant benefits are completely worth it. and thanks to a lot of work done by a lot of dedicated people, it's pretty easy to get set up and running. I send my thanks to every site I Googled figuring out how to get this working - there are too many to count. If you have questions or corrections, please post them here, and I'll do what I can to help figure them out.

Monday, July 16, 2007

Unix File Permissions

A useful tidbit that I just discovered: if you want to set unix file permissions for the group to match the owner it's as simple as:

chmod g=u filename

This just saved me boatloads of time.

Discovered at: comp.unix.questions

-Bill

Thursday, May 17, 2007

Harnessing Capistrano

Harnessing Capistrano - RailsConf2007 Tutorial
Jamis Buck

Slide aand demos available here.

Started out as a Deployment tool

Can also use it for:
+ monitoring tool across all servers (ps, df, uname, etc.)
+ server maintenance (mounts, symlinks, ...)
+ troubleshooting

A basic config file and a demo.

A config using a gateway and a demo.

A cool description and demo of 'cap -e shell' which creates and caches a connection to each deployment server. Can be scoped by role or host.

Cap 2.0 adds namespaces for tasks. Sort of a way of grouping like tasks. (eg. cap tail:showuploadlog groups the showuploadlog task into the tail namespace.

Can do variables.

And transactions, so you can make sure tasks complete on all servers or it rolls them all back - no inbetween. No good way to recover if a rollback fails.

All sort of options for including other cap files. This was always optional in Cap 1.x, but there is no default config in Cap2.0.

You can also script-check dependencies (cap deploy:check). That looks damn useful.

scrpit/spin tell cap how to start your application Many times i's script/process/spawner

cap deploy:cold <- first time deploy

cap deploy:rollback
cap deploy:migrations
cap deploy:web:disable
cap deploy:web:enable
cap deploy:cleanup <- clean up all but the last 5 releases

In addion to standard a version control-based checkout, you can do other types: export, copy, remote cache, or you can roll your own. (Set by :deploy_via option.)

There are all sorts of nifty options to get Cap to work with all sorts of version control systems.

Lots of helpers: run, sudo, stream, connect, find_task, etc.

In the ways of advanced usage, there are:
+ before_ and after_ events (before_deploy: run_tests)
+ custom callbacks: (email notifications, etc.) with complex rules
+ staging environments: you can script deploys to behave differently based on target

JRuby on Rails Tutorial

JRuby on Rails - RailsConf 2007 Tutorial

Advantages:
Take advantages of existing Java libraries
Run on Java infrastructure
Supports Java's multibyte/unicode strings.
Support Ruby's thread API, one native thread = one system thread, good multicore use.
True concurrent threading.

Great progress in the past year. From barely running rails 1.1 last year to running all of rails (except some lowlow demand dark corners) now, with good performance on everything but RDoc.

Most Gems just work. Anything pure Ruby (or with a JRuby port) runs.
Webrick works.
JRuby port of Mongrel works.

Differences:
1-Database support
Pure Ruby drivers work - mySQL
All the JDBC drivers you'd want to use work. (Yay!) (Some need custom coding to support migrations.
JDNI for connection pooling (More yay!)

2-No Native Extensions
Unless there's a port.
Mongrel - done
Hpricot - done
Database support - some done, some in progress
RMagick - in progress

3-Command-line Performance
Very good (possibly faster) once you're running, but typical java-slow startup performance.

Deployment
1. Mongrel works well. No process forking, process management
But why? Use Java App Servers via GoldSpike (or rails-integration) plugin.
2. Build WAR files for Rails apps.
One plugin, pure Ruby, out comes a deployable WAR file.
3. Glassfish server gem. (Sort of a "Pack" in the box implementation.)
Not yet. But soon.

Migrating existing Rails apps to JRuby/Rails
Be aware of the currently unsupported features.
1. Database support
- MySQL is great
- Derby & HSQL work well. Small embedable DB.
- Postgres - Few out of 1000+ tests.
- Oracle - starting to get attention
- MS SQL Server & DB - Need help, haven't really been worked on much.
Migrations mostly work well, tricky on some DBs that don't have all features.
Fixtures work well, parts of rails test. Issues generally YAML rather than DB issues.
2. Native Extentions
Option 1 - Use something else, aka don't do it.
Option 2 - Use an equivalent Java library. (binds you to jruby)
Option 3 - Port the library yourself.
Option 4 - Port by wrapping a java library.
3. Deployment Options
- Mongrel: works, but not the most efficient
- Existing WebApp Server: good concurrency, clustering, resource pooling
- Grizzly/GlassFish v3 option: lightweight, gem mongrel-like install

Monday, January 22, 2007

Cascading Drop-downs in Rails

We were looking for a solid example of cascading dynamic drop-down select lists to use in our rails application, and found the web sorely lacking in solid examples. We found a very good start at http://www.railsweenie.com/forums/2/topics/767 but it wasn't complete enough, or didn't work entirely. So my very good buddy and co-worker Sheri and I figured this out, and got it working for our app, and wanted to document it here in the hopes that it helps someone else. Here's the nutshell version:

It's probably already there, but make sure this line is in your standard_layout.rhtml:

<%= javascript_include_tag :defaults %>

Add this function def to application_helper.rb:

def update_select_box( target_dom_id, collection, options={} )
# Set the default options
options[:text] ||= 'name'
options[:value] ||= 'id'
options[:include_blank] ||= true
options[:clear] ||= []
pre = options[:include_blank] ? [['','']] : []
out = "update_select_options( $('" << onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_5">dom_id.to_s << "'),"
out << "#{(pre + collection.collect{ |c| [c.send(options[:text]), c.send(options[:value])]}).to_json}" << ","
out << "#{options[:clear].to_json} )"
end

This calls update_select_options which needs to go into application.js:

function update_select_options( target, opts_array, clear_select_list ) {

if( $(target).type.match("select" ) ){ // Confirm the target is a select box

// Remove existing options from the target and the clear_select_list
clear_select_list[clear_select_list.length] = target // Include the target in the clear list

for( k=0;k <>
obj = $(clear_select_list[k]);
if( obj.type.match("select") ){
len = obj.childNodes.length;
for( var i=0;i <>
}
}

// Populate the new options
for(i=0;i <>
o = document.createElement( "option" );
o.appendChild( document.createTextNode( opts_array[i][0] ) );
o.setAttribute( "value", opts_array[i][1] );
obj.appendChild(o);
}
}
}

Add something like this to the form.rhtml (changing the name of the observable field as appropriate):

<%= observe_field 'item[facility_id]', :frequency => 0.5,
:update => 'location_id', :url =>
{ :controller => 'item', :action=> 'refreshLocation' },
:with => "'facility_id=' + escape(value)" %>

Add something like this to the controller:

def refreshLocation
@facilities = Facility.find(:all)
@facility = Facility.find(params[:facility_id])
@locations = Location.find_all_by_facility_id(params[:facility_id])
render :update do |page|
page << text =""> :description} )
end
end


This tidbit in the form.rhtml is the ultimate target of all this work (this is the drop down we want to refresh)






<%= select_tag "item[location_id]", options_from_collection_for_select(@locations,:id,:description) %>



If I missed any code attributions from the various sources we pieced this together from, I'm sorry. Write me and I'll make good and give attribution where appropriate.

If you have any questions about this, post 'em and I'll take my best shot at answering.

And finally, thanks Sheri! Couldn't have done it without you!

Sunday, December 10, 2006

Rails Projects Abound

Okay, abound is a little strong. But we've got one new one and one to-finish Ruby on Rails project on our plates at work. The new project is a quickie tracking app for all inventory items containing human or animal tissue that get used in the operating rooms. Apparently, it's a JCAHO requirement that, as of January 1, 2007, all hospitals have to track all items that contain any amount of tissue - we've always had to track tissue-based implants: heart valves, bone chips, etc. The idea that was being floated was for a MS Access app, but I've grown way too tired of supporting those, plus with Rails we can now code a web app as fast as we can do a desktop Access app, all things being equal.

Politically speaking, it'll be very cool for us to pull this off in a two-week sprint, and as simple and straight-forward as it is, I'm thinking we'll be done much sooner, even with teaching Ruby and Rails to a couple of long-term Smalltalk/Java/Struts programmers. Technically speaking, the more Rails code we can get in production, the happier I'll be.

The other Rails app on our near horizon is to spend some time finishing Tart, the request management app that I've been learning and twiddling with for over a year now. It's an sort of an issue tracker that incorporates multiple approvals (IRB and departmental), and is tuned to our request process. It'll be good to get it launched and out there in people's hands.

Oh, the update I promised you in the last post? She interviewed very well, blew the socks off everyone she met, and she started on our team last week.

-Bill


Tags: ,

Wednesday, October 25, 2006

Falling in love all over again

You know, when you're hiring, and the perfect resume comes across your desk, you know it? Well, it happened today. Literally the perfect candidate fell into our laps. I talked to her, and she sounds like exactly (really, exactly.) what I'm looking for. She's got struts experience. She's got hibernate experience. She's got a serious value for test-driven development. Hell, she's got values as a programmer! She likes, understands, and wants to work in an Agile shop. From talking to her she's got a pretty laid-back personality. I think I'm in love.

Her interview is set up for Friday, and I'm really curious to see how she's going to click with the team.

Well, world.... I'll keep you posted on how it goes.

-Bill

Monday, September 04, 2006

Team Building as Magic

I've recently been tasked with staffing our group back up to it's fullest potential. Recent comings and goings have left us with 2 staffed and 4 open positions, not including mine. As I've been wading through resumes and talking with candidates something struck me: building a team from near-scratch like this has a great resemblance to the strategic card games Magic and Pokemon. To start a game in one of these, you sort through your collection of cards (offensive, defensive, special use, environmental, etc.) and build a game deck of 15 or so cards that you play that game with. Part of the gameplay is luck, but far more of it is strategy, hinging on how you pick what cards go in your deck and how you play those cards as the game progresses.

Building a team for success at work is no different. I have dozens of likely candidates as potential choices, each with their technical and personal strengths and weaknesses. The team that I put together will determine our success over the coming months and years. In addition to core technical competencies, I'm putting emphasis on professional developer talents such as testing and agile team experiences, and I'm banking that that emphasis will serve us well. Other traits I'm finding valuable are a passion for craft, meaning I want people to be passionate about doing things the right way (over just getting them done), and people skills over technical skills, meaning that I don't want an uber-guru who keeps everyone around themselves ticked-off or can't communicate. Ideally you want someone with all these skills and all the ideal traits in one package (or even a whole team of folks like that), but those folks as rare and hard to find as heros in Magic. I think a solid heterogeneous team with just the right blend of skills in a team that can work well together is far more realistic and achievable.

I'm off to build my winning deck.

Friday, July 28, 2006

OSCON 06 Day 5 - Morning Sessions

Session 1
Open Source Performance Monitoring Tools, Tips and Tricks for Java
Matt Secoske
matt@secosoft.net

Your project requires performance monitoring/planning/goals when your business requires it. You can do this through profiling, which is a focused look at system execution. To plan for performance: 1) determine your goals, 2) create testing scenarios, 3) determine monitoring / profiling needs. 4) Integrate this into your development process, and finally 5) integrate it into your production environment.

In planning, you need to know:

  • expected total number of clients

  • expected peak total number of clients

  • most common tasks these clients will be doing

  • acceptable response time

  • how long will the data stay around



and what you want to monitor:
hardware (web/app/db servers) cpu, memory, cache hit %, disk and network speed.
java specific (gc, app metrics)

JUnitPerf decorates JUnit test, great for benchmarking particular tests or test cases while refactoring, it's good at continuous performance testing, but not so good as a deployed monitoring solution. It's also available in JUnit 4.0 as an annotation.

The Grinder is a clusterable performance tester. It can do stress, load, capacity and functional testing. It can proxy traffic for recording and playback later as part of tests.

Apache JMeter does stress, load, capacity and functional testing. It's not clusterable and doesn't do proxy recording. It does have a plugin architecture for customization.

Log file analysis is another way to monitor performance. Since you're writing to disk it can have a noticeable negative impact on the performance you're trying to monitor. It requires changes to your source code, and doesn't accurately reflect how expensive your operation was, only the time required to execute it. If you're committed to logging, Aspects is a recommended way of doing it (AspectJ, AspectWerkz, Java Interactive Profiler, GlassBox Inspector).

JFluid / NetBeans Profiler - part of Sun's new JVM profiling tool, and also part of the NetBeans Profiler Extension. It supports local and remote profiling, and provides limited JVM support (mainly 5.0 and up).

On the other side, there's Eclipse TPTP. It does local and remote profiling, and requires a JVM agent for remote use.

Matt then did a brief demo of TPT in Eclipse, and a deeper demo of the NetBeans profiler, exploring many of the in's and out's.

He closed with a few tips and tricks:

  • Put in just enough metrics to get your performance measurements

  • Performance Test != Production

  • Real world data + real world usage patters + near-production environment = accurate benchmarks

  • Keep a little monitoring in production.




Session 2
Hacking Your Home Phone System (Year 2) - aka. Does the Phone Work Today?
Brian Aker

Terms:
PBX - private branch exchange
FXO - receives signal
FXS - generates signal
DMARK - demarcation point
PSTN - public switched telephone network

On wiring: put boxes in every wall, with 3/4 conduit (the largest you can fit
in a wall). Metal boxes survive better and are easier to mount. Run
electricity separately and cross at 90deg angles. More than 360deg worth of
conduit turns makes it hard to run cable later. Finally, don't leave boxes
empty for inspections - it worries inspectors.

Asterisk is an open source project that creates all sorts of phone
functionality in software (voicemail, conferencing, PBX, etc.) Digium makes
the best hardware (and funds Asterisk development). Skip the cheaper cards, and
get the TDM400P cards. Be careful of the FXO / FXS ports, as plugging the
wrong one into the wrong place can blow up the card.

Phone Instruments
BudgetTone: $40, cheap, cheap sounding, voice CID
Snom 190: $299, entirely scriptable via web services, can talk to multiple voip
services, no buttons for lines
Polycom SoundPoint: $200, good sound, buttons for lines, PoE, slow to boot

Analog to Digital Devices
Sipura 2000: 2port FXS analog adapter, $70
Sipura 3000: FXS/FXO bi-directional adapters, $70

The Computers: Any linux box will work.

The Software: Asterisk is hard to setup. The extensions file is the key to it
all. Plan on it taking a while.

The next generation is Asterisk@Home. The CD boots, wipes the drives, and
installs a working Asterisk based on Centos. It probably needs to be tweaked
after it installs, but it works. Recently they changed the name of the product
to Trixbox (which comes with SugarCRM built in).

High-availability: MySQL Cluster - set up a second machine, cluster them.

Mashups:

  • Front-door solenoid: unlock your doors via your phone

  • ipkall: free phone numbers!! (dial-a-song, a special number for certain
    callers)

  • dial-a-monkey network

  • Livejournal's mod_mp3: good for creative content, freaks out business people

  • AIM Bot: with the follow-to-phone feature, it sends IM messages when messages arrive (configurable for certain folks, if that suits you.



Hardware Vendors
Digium - http://www.digium.com/
Polycom - http://www.polycom.com/
Snom - http://www.snom.com/
Sipura - http://www.sipura.com/

Links
http://voip-info.org/
http://www.asterisk.org/
http://www.planetasterisk.org/
FreeWorld Dialup - http://www.freeworlddialup.com/
http://krow.livejournal.com/
<- Speaker's blog.


-Bill

Tags: ,

OSCON 06 Day 4 - Afternoon Sessions

Session 3
Outer Joins for Fun and Profit
Bill Karwin

I've never fully understood Outer Joins. Although I can use them, I'm far more comfortable falling back to the (+) from Oracle and += from MSSQL to get what I want. Bill started explaining outer joins with set theory and Venn diagrams, which open source databases support which kind of outer joins. Then Bill launched into several examples, each explaining concepts of increasing complexity. Beyond the obvious stuff, you can do things like mimic a NOT IN subquery on platforms (MySql4.0) that doesn't support subqueries. You can also do a 'Greatest row per group' subquery without having to use the max() function. This lets you return more rows than just the max'd one. In english, its "show the row for which no other row exists with a greater date for the same product".

One of the more interesting demo/solution bits he showed was sudoku solving with SQL (using outer joins, of course...).


Session 4
Using the Google Web Toolkit
Bruce Johnson and Bret Taylor

Ajax, the same old arguments: nothing to download, every app is only a URL
away, desktop-like functionality. In reality, it auto-reinstalls every
full-page load, so it'd better be small. It's sorta secure; it's so dang hard
to get it working at all that security is almost an afterthought. There's a
plethora of technologies, on it's own platforms with it's own quirks - it's no
wonder developers hate Ajax.

Looking at Ajax from a Java viewpoint, Google set out to leverage their Java
knowledge, and make Ajax front-ends that are still very webby. They came up
with the idea of translating Java into Javascript. They actually pulled this
off, and it really works. GWT can run in two mode: hosted mode (the whole app
runs in the jvm as java - this gives you java-based debugging - which is
beautiful and useful!) and native mode (that runs in a os-native web browser).

GWT moves all the stateful session logic to the client. This enables stateless
load balancing and (thus) server clustering. It also leaves as much UI-only
stuff on the client, requiring no server round-trip. GWT provides leverage for
the solutions - you can take any solution and wrap it in a class and have it
to reuse later.

GWT, as Java, brings static type checking (static types... lalalalala....) and
all the benefits and drawbacks that typically come with it. There are code reuse advantages: create your ajax libraries as jars, reuse your code.

They provide a fantastic palette of UI widgets that make cross-platform rich
UI creation pretty simple, and one less thing to worry about.

The Web Toolkit comes along with a RPC library as well. It's dead simple, and
lets you create objects remotely, and serialize them back to the browser.

History and linking are ususally a casualty of Ajax. AWT lets you (with a pretty simple set
of calls) provide a full history support and linking. It really is dead
simple!

But wait! There's more!! There's JUnit support for testing of both the Java
and Javascript sides!

Downloadable at http://code.google.com/webtoolkit/
and the GWT Widget Library at http://gwt-widget.sourceforge.net/ and more.

Wow. GWT is cool. I can't wait to get it and play with it.




Session 5
Taming an Audience with Laser and Snake
Robert Stephenson

The basic idea is that you have a webcam, an iSight in Robert's case, and a
laserpointer. You point at the screen with the laserpointer, and the iSight
and your software work like a mouse to control the computer running the
presentation. In Robert's case, he leveraged the AppKit (a python interface to
Apple system calls) to call the CocoaSequenceGrabber to capture frames. Robert
chose a set of RGB factors to pick an appropriate range of laser colors to
have the app look for.

-1.1r + 2.0g -1.1b > 0.9

But this didn't work so well. The problem is that that there's not enough
resolution in the sRGB color space for the camera to be able to resolve the
dot of the laser pointer. And/or there's not enough range in the sRGB gamma
when ends up clipping the intensity of the laser dot. Owing to the way that
the camera's automatic gain control tries to normalize the camera exposure,
Robert tweaked the camera's sensitivity settings to compensate and was
able to reliably track the laser about the screen.

He then, with some ObjectiveC, C, Python and AppleScript, he was able to
control PowerPoint (or was it Keynote). Robert also added capabilties to
ajust keystoning and skewing of the image for the camera.



Session 7
Web Heresies
Avi Bryany

Seaside is a web development framework written in Smalltalk. Chances are
you're not going to go out and use Seaside. That's okay with Avi. He just
wants to talk about the heresies of web development. Avi's first idea is that
the HTML files belong to the developer, and that designers work with the CSS
files. He did a brief apples to apples comparison of language semantics so we
non Smalltalk people could read his examples. Naming things, while a good idea
when absolutely necessary, should be avoided if it can. You can use the object
that the field is rendering for to source it's own information. If you don't
name your objects, they get numbers, and a behind-the-scenes hash maps the
numbers back to their objects and their accessors for each numbered field.
Kinda confusing, but I can see the reasons why this would be a good idea.

An aside from Avi's talk: Seaside is based on Squeak Smalltalk, which I've been meaning to check out.
From my Smalltalk days, grumble11grumble years ago, I remember the Virtual
machine being HUGE and somewhat slow, and hard to crack for outside use. I'm
really curious to give Seaside a shot, even if just to see how it does what it
does. I'm intreagued as heck about how a Smalltalk web app server would fit
together and work.

Avi went on to cover a few demos, showing source code, and running a few hello
world-type things. A question that came up (as part of his talk) is how would
you serialize a session? The short answer is that you don't. Just use session
affinity instead. The cost of a server failing and taking a session with it
tends to be very low, so (essentially) screw it. Use sessions like crazy. Put
anything in a session you want. Really. Anytime. (I'm guessing this is another
herasy.) Try on the idea that you can also save the current execution point
and put it in the session. This would mean that you can throw the current
execution stack in the session (and in this case putting it in the URL) as the
form is displayed. When the form gets posted, you pull the execution stack out
of the session and continue execution from where it was, thus processing the
form. Think about that one and let that one sink in. Does your brain hurt yet?
It should. (This overhead isn't that much, but amounts to about 1M / current
user.)

Another Seaside goodie is that you can change your page load order by changing
the method order in your Smalltalk code. Relying on Smalltalk gives you some
really interesting powers that I've never seen in web applications before.
Your server image is still a fairly full-featured image, including the ability
to vnc into the sever image and interactively debug it. That's another "Think
about it." moment. That's cool.

Q: What's the lineage of Seaside?
A: WebObjects + Paul Graham = Seaside. It's also similar to Tapestry.

Q: What have been the changes to Seaside over time?
A: Seaside used to have a templating system. That was ditched 3 years
or so ago in favor of the programamtic html creation. There were also a few
minor architectural changes over time as well.

Q: Ajax?
A: Yes! http://scriptaculous.seaside.st/

-Bill

Tags: , , , ,,,