Saturday, March 31, 2007

A Java based ActiveRecord?

One of the things I really love in the Rails-world is working with ActiveRecord. So I thought, someone most have done this in Java too,... no? Here is a short list of existing projects I found:
But none of them seems to have released anything yet :-(

There is so much the Java community can learn from ActiveRecord. Yes, I now it is intrusive on the model by forcing us to inherit its base class. Yes, I know you get the most from it by accepting all its defaults. And yes, you have to demarcate transactions in your code. But it is a lot easier to work with.

We should be able to work around some of these shortcomings while keeping some of the good stuff.

Is this a good Ruby copy-constructor?

I had the need to copy a model instance in a Rails application I'm writing. I am not a Ruby wizz yet, so I am just going to ask. Is this a good copy-constructor in Ruby/Rails terms?
  def copy_constructor
course = Course.new(self.attributes)
course.id = nil # make copy transient

# the "trainers" n-m relation is not copied in "new", as it is not an attribute on the model
course.trainers = self.trainers
course.trainer_ids = self.trainer_ids

return course
end
The model I am copying is "Course" and it has a N-M relation to a "Trainer" model. I use the "self.attributes" call, to set attributes on the new instance, but the relations are not copied. Also, I set "id" to nil, to make ActiveRecord think it is transient, which makes ".save" do an insert.

Any better way to do this?

Friday, March 30, 2007

JRockit hangs itself... any advice?

UPDATE: Found out the problem. We are not on a system which supports NPTL and this blog entry explains why this is bad with JRockit.

Any advice on options to apply for further debugging on this issue?

I have a problem with JRockit freezing/hanging when under heavy load. It does not crash, but it looks like it is spinning on the CPU. The CPU is a XEON with hyper threading, so it is viewed as two CPUs on our linux host.

Here is the scenario:
  • Suddenly, CPU goes up to 50% and stays there (looks like JRockit is spinning/busy looping on the one "cpu")
  • While spinning at 50% CPU, the heap keeps growing until it hits max
  • The application and JRockit stops responding (but it does not crash)
  • There is no information on stdout or stderr. Also no information in weblogic.log
The setup
  • BEA WebLogic 5.1
  • SuSE Linux 9.0 (*not* enterprise linux)
  • Kernel: 2.4.21_166, SMP 4G
  • JRockit v26.4.0.63 (JDK 1.4.2_11)
  • XEON 3GHz with HT
  • JAVA_OPTIONS="-jrockit -Xms512m -Xmx512m -Djrockit.ctrlbreak.enableforce_crash=true"
Has anyone else experienced this or things like it? I've found many issues about JRockit crashes (which actually gives some information to work with), but our process is not crashing. It just freezes/hangs and uses a lot of CPU.

mvnq - *small* maven tip

Here's a quickie...

In "some" projects that use maven for building, it can be annoying to run tests on each build of an artifact when developing. So, often I run "mvn -Dmaven.test.skip=true", up until before checking in. On the shitty Windows command prompt, where you do not have stuff like search in history, it is nice with this little .bat file:

SET MAVEN_OPTS="-Dmaven.test.skip=true"
mvn.bat

I always call it "mvnq.bat" ("q" for "quick build") and put it in my path.

Of course, when on a *nix, this is more elegantly solved by a simple alias command in the shell...

Wednesday, March 28, 2007

Integrating OpenLaszlo in a maven build

With the OpenLaszlo 4.0.0 release and the resent hype about flash-based applications, I wanted to try out producing a flash front using a Java stack. So, I went to OpenLaszlo.org and downloaded the release.

But what the...., OpenLaszlo is distributed as a war package (yes, there is a binary installer, but that is just a tomcat with the war deployed in it). Hmm, ... I use maven for building, so I tried to find the dependencies in the maven repository. No luck there.

I ended up pulling apart the warfile, finding the stuff I needed and producing a pom from that. Here are some of the problems I encountered:
  • OpenLaszlo seems to use some dependencies that most other Java applications does not (at least not the ones I build or use). For instance, it is long time since I worked with saxon, and what is "jing" from "thaiopensource"? :-)
  • Many of the dependencies in WEB-INF/lib inside the warfile had no versioning information, neither in the filename nor in the manifest
  • The saxon parser in WEB-INF/lib was a modified one, where the "META-INF/services" part of the jarfile, that sets it up as the default SAXParser, had been taken out. In the pom I produced, I first took the liberty to use a saxon from the maven repository, but this made spring and tapestry from the technology stack of my application fail (the SAXParser in saxon failed to support validation and schemas)
  • The jdom.jar in WEB-INF/lib was something like a 1.0.1, but the latest release from jdom.org is 1.0. Hmm, strange, but the one in the distribution includes a fix for xml comments, which is needed to make things work
In the end, there were some dependencies, that I needed to put in my local maven repository. Either because I could not locate them in the global repository or because I needed to use the special version from the warfile (like with saxon and jdom). The dependencies listed below ended up in my local repository:
  • jdom.jar
  • saxon-6.5.3-lz-p1.jar
  • swflib.jar
  • jing.jar
  • lps-4.0.0.jar
The rest of the dependencies, I could let my pom resolve from the global maven repository. You can download the pom from here, and you then need to do this:
  • Upload it in a maven repository you control (path: org/openlaszlo/openlaszlo/4.0.0/openlaszlo-4.0.0.pom)
  • Download the OpenLaszlo 4.0.0 distribution
  • Upload the 5 jars from WEB-INF/lib (jdom, saxon, swflib, jing, lps) and name them properly to enable the pom to find them (org/openlaszlo/openlaszlo-jdom/4.0.0/openlaszlo-jdom-4.0.0.jar, org/openlaszlo/openlaszlo-lps/4.0.0/openlaszlo-lps-4.0.0.jar, org/openlaszlo/openlaszlo-saxon/4.0.0/openlaszlo-saxon-4.0.0.jar, org/openlaszlo/openlaszlo-swflib/4.0.0/openlaszlo-swflib-4.0.0.jar, org/openlaszlo/openlaszlo-thaiopensource-jing/4.0.0/openlaszlo-thaiopensource-jing-4.0.0.jar)
  • In the pom of your warfile project that is to use OpenLaszlo, include a dependency on the new pom (see below)
  • Include "other stuff you need" from the warfile (see below)
Dependending on the new OpenLaszlo pom is done like this:

<dependency>
<groupId>org.openlaszlo</groupId>
<artifactId>openlaszlo</artifactId>
<version>4.0.0</version>
<type>pom</type>
</dependency>

Are we there yet?
No, we are not. Because OpenLaszlo includes a lot of stuff in the webapp in "lps/" and in "WEB-INF/lps" that is needed to make anything work. In addition to this, you will need some files from "WEB-INF/classes". I did this by depending on the OpenLaszlo warfile in the pom of my application and then specify exactly which parts to include as a war overlay. Like this:

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.0</version>
<configuration>
<dependentWarIncludes>lps/**,WEB-INF/lps/**,WEB-INF/classes/org/openlaszlo/**</dependentWarIncludes>
</configuration>
</plugin>
Of course, you will need to upload the warfile in your repository too. It should be placed and named like this: "org/openlaszlo/openlaszlo/4.0.0/openlaszlo-4.0.0.war".

But why not use war overlays to simply include all from WEB-INF/lib and then avoid all the above stuff about the new pom and uploading jars in a repository you control. Well, you can. But you will end up with a lot of uncontrolled jar dependencies without version information, inside your distribution unit. Not nice.

And then what?
You should then go on and define the OpenLaszlo servlets (see the web.xml of the warfile distribution) and read the documentation.

BTW: I've uploaded the pom to an existing issue in the OpenLaszlo bugtracking tool. Would be nice if they would distribute to the global maven repository when releasing....

Monday, March 19, 2007

Choosing view technology is a hard job these days

Some time ago, I did some .Net web development. You can say a lot about development with the M$ stack, but you do not have the problem of choosing between 30-or-so web technologies yourself. They have laid out the path for you. Use Visual Studio and ASP.NET.

On the Java platform, web view technologies are flocking and shouting "choose me, choose me",... and we must constantly evaluate and weigh the alternatives against our requirements. There are so many contenders, each with their own unique stuff but in the end, I like it that way--having choice.

Maven dependency versioning and java.lang.NoClassDefFoundError: org/apache/maven/archiver/MavenArchiveConfiguration

Hmmm, sometimes I love maven and sometimes I hate it. I love it for all the tedious work that it does for me and I hate it for not always doing it well enough :-)

Today, I had the exception below, when I tried to build an existing project at work. As I went through debugging, I had various versions of it (the exception). There was another one like it, but with the "maven-war-plugin" instead. I guess the basic error in them is the "java.lang.NoClassDefFoundError: org/apache/maven/archiver/MavenArchiveConfiguration" part.

Eventually, I solved it by deleting "$HOME/.m2/org/apache/maven" and letting maven download all its dependencies, including plugins, again. I guess it was a version conflict between some of all of mavens dependencies :-(

[INFO] ------------------------------------------------------------------------
[ERROR] BUILD ERROR
[INFO] ------------------------------------------------------------------------
[INFO] Internal error in the plugin manager executing goal 'org.apache.maven.plugins:maven-jar-plugin:2.1:jar': Unable to find the mojo 'org.apache.maven.plugins:maven-jar-plugin:2.1:jar' in the plugin 'org.apache.maven.plugins:maven-jar-plugin'
org/apache/maven/archiver/MavenArchiveConfiguration
[INFO] ------------------------------------------------------------------------
[DEBUG] Trace
org.apache.maven.lifecycle.LifecycleExecutionException: Internal error in the plugin manager executing goal 'org.apache.maven.plugins:maven-jar-plugin:2.1:jar': Unable to find the mojo 'org.apache.maven.plugins:maven-jar-plugin:2.1:jar' in the plugin 'org.apache.maven.plugins:maven-jar-plugin'
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoals(DefaultLifecycleExecutor.java:543)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoalWithLifecycle(DefaultLifecycleExecutor.java:480)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoal(DefaultLifecycleExecutor.java:459)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoalAndHandleFailures(DefaultLifecycleExecutor.java:311)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeTaskSegments(DefaultLifecycleExecutor.java:278)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.execute(DefaultLifecycleExecutor.java:143)
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:330)
at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:123)
at org.apache.maven.cli.MavenCli.main(MavenCli.java:272)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.codehaus.classworlds.Launcher.launchEnhanced(Launcher.java:315)
at org.codehaus.classworlds.Launcher.launch(Launcher.java:255)
at org.codehaus.classworlds.Launcher.mainWithExitCode(Launcher.java:430)
at org.codehaus.classworlds.Launcher.main(Launcher.java:375)
Caused by: org.apache.maven.plugin.PluginManagerException: Unable to find the mojo 'org.apache.maven.plugins:maven-jar-plugin:2.1:jar' in the plugin 'org.apache.maven.plugins:maven-jar-plugin'
at org.apache.maven.plugin.DefaultPluginManager.getConfiguredMojo(DefaultPluginManager.java:541)
at org.apache.maven.plugin.DefaultPluginManager.executeMojo(DefaultPluginManager.java:398)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoals(DefaultLifecycleExecutor.java:539)
... 16 more
Caused by: org.codehaus.plexus.component.repository.exception.ComponentLookupException: Unable to lookup component 'org.apache.maven.plugin.Mojoorg.apache.maven.plugins:maven-jar-plugin:2.1:jar', it could not be created
at org.codehaus.plexus.DefaultPlexusContainer.lookup(DefaultPlexusContainer.java:335)
at org.codehaus.plexus.DefaultPlexusContainer.lookup(DefaultPlexusContainer.java:440)
at org.apache.maven.plugin.DefaultPluginManager.getConfiguredMojo(DefaultPluginManager.java:532)
... 18 more
Caused by: org.codehaus.plexus.component.factory.ComponentInstantiationException: Could not instanciate component: role: 'null', implementation: 'org.apache.maven.plugin.jar.JarMojo'
at org.codehaus.plexus.component.factory.java.JavaComponentFactory.makeException(JavaComponentFactory.java:77)
at org.codehaus.plexus.component.factory.java.JavaComponentFactory.newInstance(JavaComponentFactory.java:62)
at org.codehaus.plexus.DefaultPlexusContainer.createComponentInstance(DefaultPlexusContainer.java:1464)
at org.codehaus.plexus.component.manager.AbstractComponentManager.createComponentInstance(AbstractComponentManager.java:93)
at org.codehaus.plexus.component.manager.PerLookupComponentManager.getComponent(PerLookupComponentManager.java:48)
at org.codehaus.plexus.DefaultPlexusContainer.lookup(DefaultPlexusContainer.java:331)
... 20 more
Caused by: java.lang.NoClassDefFoundError: org/apache/maven/archiver/MavenArchiveConfiguration
at org.apache.maven.plugin.jar.AbstractJarMojo.(AbstractJarMojo.java:83)
at org.apache.maven.plugin.jar.JarMojo.(JarMojo.java:30)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:494)
at java.lang.Class.newInstance0(Class.java:350)
at java.lang.Class.newInstance(Class.java:303)
at org.codehaus.plexus.component.factory.java.JavaComponentFactory.newInstance(JavaComponentFactory.java:44)
... 24 more

Thursday, March 08, 2007

I took the tests...

And here is what I came out as:

You are OS2-Warp. You're plagued by feelings of abandonment and disgust for your backstabbing step-brother.  Oh, what might have been.
Which OS are You?


You are .cgi Your life seems a bit too scripted, and sometimes you are exploited.  Still a  workhorse though.
Which File Extension are You?



You are Perl. People have a hard time understanding you, but you are always able to help them with almost all of their problems.
Which Programming Language are You?


You are slashdot.org You  are geeky.  You value freedom, but not spelling.  People look to you to find out what's going on.  You inspire spirited discussions and ad hominem attacks.
Which Website are You?


The first word that springs to my mind is "oops!".

Saturday, March 03, 2007

Mongrel-based Rails Deployment

So, the CGI-based deployment did not perform that well at all. So, I wanted to try out Mongrel. Mongrel is a web-server written in Ruby, but not targeted alone at serving Rails applications. When hosting Rails applications though, it can do so very well. Because Rails is not thread-safe, there needs to be some synchronization on simultanous requests in Mongrel. And so there is. But Mongrel only synchronizes the Rails part. All the other parts about handling the HTTP protocol and streaming the result back to the client is done in parallel.

There is good documentation on getting this running. Here is what I did, to get it running on my new ubuntu installation. I will assume you have read the previous post on the CGI-based approach.

Installation and Configuration

sudo aptitude install build-essential ruby1.8-dev
sudo gem install mongrel mongrel_cluster --include-dependencies

I then jumpted into the application dir "/var/opt/rails/app" and configured my mongrel cluster:

sudo mongrel_rails cluster::configure \
-e production \
-p 4000 \
-N 5 \
-c /var/opt/rails/app \
-a 127.0.0.1 \
--user www-data \
--group www-data

This created a YAML configuration file for the "cluster" of mongrel instances. Now don't get too excited. This is not a cluster as we know it in Java terminology. Is is simply a bunch of mongrel instances that can be managed (started and stopped) together. This "cluster" is five instances, running on the ports 4000, 4001, ... 4003 in production mode.

The "cluster" can be started and stopped using:

sudo mongrel_rails cluster::start
sudo mongrel_rails cluster::stop

What was really nice was the the mongrel_cluster gem comes with a SYSV init script, ready to copy into "/etc/init.d". It reads all YAML configuration files from the directory "/etc/mongrel_cluster", so I simply linked my new cluster configuration file into such a directory and did a "sudo /usr/sbin/update-rc.d -f mongrel_cluster defaults" to get it auto started and stopped on boots.

I then followed this guide to install Apache 2.2.4. I needed Apache >=2.2 to get access to "mod_proxy_balancer", and I could not find such a new Apache as a .deb package :-( So, I had to compile it from scratch.

After compiling and installing a brand new Apache, I made this virtual host configuration:

NameVirtualHost appcluster
<virtualhost appcluster>
ServerName appcluster
DocumentRoot /var/opt/rails/app/public/
ErrorLog /var/opt/rails/app/log/apachecluster.log

<directory /var/opt/rails/app/public>
Options FollowSymLinks
AllowOverride None
Order allow,deny
Allow from all
</directory>

RewriteEngine On
RewriteRule ^/$ /index.html [QSA]
RewriteRule ^([^.]+)$ $1.html [QSA]
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
RewriteRule ^/(.*)$ balancer://app_cluster%{REQUEST_URI} [P,QSA,L]
</virtualhost>

<Proxy balancer://app_cluster>
BalancerMember http://127.0.0.1:4000
BalancerMember http://127.0.0.1:4001
BalancerMember http://127.0.0.1:4002
BalancerMember http://127.0.0.1:4003
BalancerMember http://127.0.0.1:4004
</Proxy>

And I was up and running.

Performance was much better
Again, the same small performance test was made, and it was much better:
  • 1 request, with no db-access took around 0,0-0,1 second
  • 5 simultanous requests, to same page, < 1 second, with an average of ~0,1 second/request
  • 15 simultanous requests, to same page, < 2 seconds, with an average of ~0,1 second/request

CGI-based Rails deployment

As I mentioned in the previous post, I tried the simplest thing possible for my rails deployment--a CGI based approach. My setup was this:
Installation and Configuration
So, I installed ubuntu, got all updates and installed them and enabled "universe" and "multiverse" in the apt sources list. Now I was ready to install ruby, rails, mysql and apache2.


sudo aptitude update
sudo aptitude install ruby rdoc ri rake apache2 mysql


Then I got RubyGems as source and installed using ruby. I was a bit sad that I couldn't get the "gem" tool using aptitude, but it looked like the ruby installer actually made a gem package and installed it as a gem, so from here on, it can be updated using itself, nice!

Then, I installed Rails:


sudo gem install rails --include-dependencies


And after installing rails, I installed my application:
  • Installed (copied) my application into "/var/opt/rails/app"
  • Recursively changed user and group on that dir to "www-data:www-data", as this is what apache runs as.
  • Edited environment.rb to uncomment line about RAILS_ENV (makes it default to production if nothing explicit set)
Now I was ready to create the apache setup:
  • Created a new file "/etc/apache2/sites-available/app"
  • Linked it to sites-enabled as "001-app"
  • Linked "/etc/apache2/mods-available/rewrite.load" into "mods-enabled" (the .htaccess file in the rails application is using mod_rewrite to dispatch requests to "dispatch.cgi")
  • Edited "/etc/hosts" to make the name "app" resolve to localhost IP (as I am using virtual host based apache setup)
I then edited the "sites-available/app" file to have this content:


NameVirtualHost app
<virtualhost>
ServerName app
DocumentRoot /var/opt/rails/app/public/
ErrorLog /var/opt/rails/app/log/apache.log

<directory>
Options ExecCGI FollowSymLinks
AllowOverride all
Order allow,deny
Allow from all
</directory>
</virtualhost>


And simply restarted apache.

And then, stuff went wrong, and I found out I had to do these things:
  • Put "socket: /var/run/mysqld/mysqld.sock" into "databases.yml", as ActiveRecord was using the socket file to connect to MySQL and the ubuntu installed MySQL has placed it under "/var/run/mysqld"
  • Enabled x-bit on "dispatch.cgi" (because I had copied files from a windows partition)
  • Change the shebang line in "dispatch.cgi", as it included a horrible "c:/..." line to where ruby is placed on my windows partition
And then it worked, but, ...

Performance was horrible
As mentioned previously, the site is not going to take many hits at all. So I thought, with todays CPUs and forking speeds of Linux, this might be okay. Here are some numbers, from my 2GB, 2.26GHz Dell Latitude D810.
  • 1 request, with no db-access took around 0,8-1,0 second (Auch!)
  • 5 simultanous requests, to same page, ~5 seconds, with an average of ~5 seconds/request
  • 15 simultanous requests, to same page, ~15 seconds, with an average of ~15 seconds/request
Hugh? There seems to be no real advantage in executing requests in parallel. As in, one process working when others are in IO-wait. But then again, the load sky-rocketed when handling the requests, so this must be the ruby VM startup and Rails framework interpretation cost, that is very high. Funny though, that all request are taking equally long to finish, about linear to the number of simultaneous requests.

Maybe it would be better, if I was running the yarv vm?

Next Post
Will be about the mongrel setup I did instead, which performs much better, but is a bit more complex to setup.

Still, even this simple setup, is far more complex than dropping a .war file in a web-container.

Thursday, March 01, 2007

Rails deployment for a Java developer

As some might know, lately I have been given the chance to work seriously with the Ruby on Rails framework in a real world project for a customer. Besides me loving most--but not all--of Rails, I am now giving the production deployment environment a serious look.

Through many years now, I've been developing mostly using Java, and as a consequence of this, I have in depth-knowledge on packaging and deployment of Java applications. I will do a couple of posts here about my experience on deploying and hosting my rails application in a real production environment.

Comparing to Java, what's different?
Well, lots. When developing, you get the feeling of speed real quick. It is really nice to keep delivering business value. Now, when I am going to deploy, I feel less productive than in Java.

No Standardized Deployment Unit
Personally, I think other platforms, including Rails and .Net, can learn a lot from the Java platform, with respect to deployment. The standardization of deployment units like the .jar, .war and .ear files are very nice. You do not really know what you have got, until it is gone.

In Rails, there is no such thing. The simplest way is to just upload your directory structure to the production server, and then, it is deployed. That is the simple way. But it is not good enough. Some files should not go into production. Some are different in the development environment, and should not be copied, but merged into deployment environment.

Then, there is Capistrano, which can help you. But when compared to a .war file, it lacks a lot. What is so great about Java deployment is the standardization. You can drop one .war file in any compliant web container, and it will deploy.

Setting up environment around Ruby is a manual process
Another thing is the need to install third party libs in all environments you deploy to. My rails application happens to use RedCloth, so before deploying a version of my application, that needs this, I must remember to do a gem install redcloth. In Java, this is simply packaged as a part of the deployment unit in WEB-INF/lib, much nicer.

Also, some ruby libraries depend on native libraries being installed. These must be installed too, in correct versions, as are their dependencies. We are not used to this is Java. So much is available on the Java platform, so it is very rare, that any deployment depend on anything native being installed outside the deployment unit.

Actually, these problems can quickly make it a problem developing on one platform, like Windows, and deploying on another, like Linux.

Thread-safety - not in Rails
A last thing is the problem of simultaneous requests and thread-safety. The Rails framework is not thread-safe. I acknowledge, that this can make both developing the framework itself and developing the applications using the framework, simpler, but it sure makes the deployment harder.

You cannot rely on simply starting and monitoring one multi-threaded server to serve all requests. We are back to multi-processes for scaling the number of simultaneous requests, which seems so yesterday to me. This means CGI, Fast-CGI or some other solution like mongrel.

Not so bad, afterall
But then again, I guess this is the Rails way. You only need to setup deployment environments a limited number of times. So it is okay, for this to be a bit harder. On the other hand, actually developing the applications using Rails must be easy, which it is, as this is where we produce actual value.

Posts coming up
My next post will be about my first, naive attemt on deploying Rails using CGI. Yes, I know, it is said to be slow. But it seemed the simplest solution, and the site was not going to take a lot of hits.

See how that went, in my next post.