Tuesday Apr 24, 2007

Rails-Integration 1.1.1

rails-integration 1.1.1 has just been released !
Source and binaries can be downloaded from http://rubyforge.org/frs/?group_id=2014
rails-integration details can be found at http://www.headius.com/jrubywiki/index.php/Rails_Integration

The fundamental change in this release as compared to 1.0, is performance and compatibility. When used in standalone mode, rails-integration 1.0 had this hack wherein it performed installation of JRuby and the rails libraries at runtime (during web application initialization i.e., once per web container process lifetime). Not only was this ugly, but it also caused compatibility problems with GlassFish since it expected the web application constituents after deployment (implementation dependent)in a particular format. No runtime installation needed with 1.1.1 !
Aiding to the performance improvements is JRuby 0.9.9 which has made great strides in improving performance as compared to its previous versions.
Also, this release is compatible with rails 1.2.x, solves session related and other issues that plagued 1.0.

All in all, a great release that blends the two architectures - RoR and Java EE - giving you the best of both worlds - the development convenience of RoR and the enterprise level features of the Java EE platform. No longer do you have to pick and choose one !

Many thanks to all the folks involved in getting the release out !

Sunday Mar 18, 2007

Understanding Groovy - lesson #1

Since, I've been briefly looking at groovy, I decided to read up on one of the much talked about features - closures. Looking at the documentation here on Groovy site, the example (excerpts below) did not fully make sense to me.
public class GVector extends java.util.Vector {
  public void apply( c ){
     for (i in 0..<size()){
        this[i] = c(this[i])
     }
  }
}
...
def gVect = new GVector()
gVect.add(2)
...

def c = { numberToSquare -> numberToSquare \* numberToSquare }
gVect.apply(c) // the elements in the GVector have all been squared.

The expression 'this[i]' is the one that I did not quite understand - how exactly did this[i] result in the calling get and set on the java.util.Vector So, dug a little deeper at got at the
'Operator Overloading' section. Well, it defines the relevant overloaded functions as
a[b] 	        a.getAt(b)
a[b] = c 	a.putAt(b, c)
A quick glance at the java.util.Vector and there exists no 'getAt' method. A few google searches did not reveal anything meaningful. javap of the groovy classes did not help. Hmmm ...
This is what I decided to do - the oldest java trick in my book when I have access to the source and it seldom fails me. Modify the GVector class by defining a get method:
public Object get(int index) {
    Thread.dumpStack(); // my favourite debugging tool ;)
    return super.get(index);
}
And it all made sense. DefaultGroovyMethods (a humungous class) defined here holds the the key. This class seems to be a doing a lot of behind the scenes groovy(the-dynamic-language)magic. The magic doesn't really sink in till you've understood the inner working of a few of them. Revelant sections of the stacktrace
java.lang.Exception: Stack trace
	at java.lang.Thread.dumpStack(Thread.java:1158)
        ...
	at GVector.get(TestClosure.groovy:9)
	at org.codehaus.groovy.runtime.DefaultGroovyMethods.getAt(DefaultGroovyMethods.java:2034)

	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.groovy.runtime.ReflectionMetaMethod.invoke(ReflectionMetaMethod.java:69)
	at org.codehaus.groovy.runtime.NewInstanceMetaMethod.invoke(NewInstanceMetaMethod.java:109)
	at org.codehaus.groovy.runtime.MetaClassHelper.doMethodInvoke(MetaClassHelper.java:713)
	at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:560)
	at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:450)
	at org.codehaus.groovy.runtime.Invoker.invokeMethod(Invoker.java:131)
	at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:111)
	at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodN(ScriptBytecodeAdapter.java:187)
	at GVector.apply(TestClosure.groovy:4)
        ...
Also, further on in the example, the following statement is called
gVect.apply{ value -> println(value) }
Hmmm, would this not mutate the state of the object ? Well it should, but wasn't entirely sure. So, this helped
def gVect = new GVector()
gVect.add(1)
gVect.add(2)
gVect.add(3)

def c = { numberToSquare -> numberToSquare \* numberToSquare }
gVect.apply(c) 
gVect.apply{ value -> println(value) }
gVect.apply{ value -> println(value) } // AGAIN

Runtime output for the above code:
1
4
9
null
null
null

The 'return value' from a closure is formally defined in the Closure document under the heading 'Closure Return Value'. Essentially a closure ALWAYS has a return value. No points on guessing what a void maps to.

All in all, closures are a very powerful concept and there are proposals to introduce them in the Java language !

Friday Mar 09, 2007

Groovy Gravy ?

I think so.

This debate about language preferences can get religious and being an atheist I know how that goes ;) That's not so to say that Groovy is the only religion language that should exist. Horses for courses, right ? But, I think Groovy is good. How good ? Well - really good. Off the cuff in no particular order here's why:

  • Groovy code compile to Java bytecodes - Java tool support, Java Security, Java EE ...
  • Syntax is simple - code more maintainable
  • MOP (Meta Object Protocol) - enables some wicked maneuvers
  • In-built XML support - unbelievable how simple it is to read/write XML
  • Groovy has Grails !
  • ... and more. You can find the entire feature set on the Groovy site.
Currently only Rhino, a JavaScript interpreter, is bundled with Java SE 6. The Java platform is capable of integrating with any script engine conforming to JSR 223, and it can't possible bundle ?-too-many scripting engines out of the box, but Groovy and (the language) Java seem to be a perfect fit. Is the Groovy gravy spicy enough for Java's palate ? I hope so ...

Wednesday Feb 21, 2007

RoR with Embedded Derby

I got a request on trying to figure out how to get a RoR application working with Embedded Derby. And even though it's pretty straightforward it took me a couple of hours, so maybe the details below will save you some time.

The starting point would be to modify the RoR configuration in database.yml to look something like

 ...
    driver: org.apache.derby.jdbc.EmbeddedDriver
    url:    jdbc:derby:${database-name}

The Apache Derby database data files persists in the directory defined by 'derby.system.home'. And if this system property is not explicitly defined, the current working directory defaults as the property value. Surprisingly, in my case I could not locate the database files in the working directory used to start the database process. Well, it so happens that the derby isn't very user friendly in it's spewing error messages(at least not version 10.1.2.1). Even though on startup the server would emit 'Server is ready to accept connections on port 1527', it was not the process serving client requests(though I suspect it has got something to do with network interface bindings). There was another long running Derby process which was actually responsible for servicing the requests and its 'derby.system.home' will forever remain a mystery. After killing the long running process I was able to give a meaningful value to derby.system.home

For the RoR application running as a WAR in GlassFish, I made the following modification to domain configuration

   <java-config ...
     ...
     <jvm-options&rt; -Dderby.system.home=/my/value/of/derby/system/home <jvm-options>
     ...

Last but not the least, the Derby database process probably has some kind of an locking mechanism so that multiple processes do not step over each other. So, it means something like this - one process per database. You can use either the embedded or network mode Derby to serve you data from 'the' database.

Thursday Feb 15, 2007

RoRaWAR - A War Roar ?

No, it's not a war roar. This is not a current world affairs blog ;)

RoRaWAR stands for Ruby on Rails as Web ARchive.

The rails integration (under the jruby-extras umbrella) project's purpose is to WAR'up a RoR application. And it's great to see how far the project has come along since its inception a few months ago !

The creation of a web archive from a RoR application is as simple as

  • svn checkout svn://rubyforge.org/var/svn/jruby-extras/trunk/rails-integration rails-integration
  • cd rails-integration; mvn install;

  • the couple of steps above are one time only

  • cp -R <rails-integrations>/plugins/war <rails-app-dir>/vendor/plugins/
    cp -R <rails-integrations>/plugins/war-snapshot/\* <rails-app-dir>/vendor/plugins/war
  • cd <rails-app-dir>; rake war:standalone:create or rake war:shared:create ;
    #standalone mode will archive the rails framework libraries whereas the shared mode will not
It's as simple as that !

Note: There are a few implied dependencies here - JRuby, maven2, rails framework libraries. Also, since the rails-integration project is trying to utilize the latest and greatest JRuby releases, you might have to install the jruby.jar into maven2 repository via the maven install:install-file command. Eventually, the first couple of steps will not be needed once the rails-integration libraries are downloadable as a binary release, but until then ...

Wednesday Jan 24, 2007

Grizzlicious ... Part II

Part I described how to get a RoR application working on GlassFish. However, if you want something lightweight for development only or do not want anything to do with configuration changes(which btw, will only get simpler) or any other reason, read on ...

The elegant design of Grizzly allows you to write just a few lines of code and have a custom HTTP server suited to your specific requirements. The rails module does just that for primarily running a RoR application. This is how:

  1. Download rails-module-0.1-SNAPSHOT.jar
  2. The latest Grizzly binaries
  3. And of course, the JRuby binaries, Rails framework libraries and database specific JDBC drivers. For JRuby, I recommend working with the current trunk version as there are issues with the 0.9.2 release and the rails-module. Copy plaincharset.jar(in ${JRUBY_HOME}/lib) to jre/lib/ext of your Java runtime. For installing the Rails framework including activerecord-jdbc see steps #3 and #4. Use the flags(--no-rdoc --no-ri) for faster gem installation.

    Steps 1,2 and 3 are one time only.

  4. Create the Rails applications. Perform the following edits
    • Database related configuration edits - db config and step #8 here
    • In config/development.rb set config.breakpoint_server to false. See JRuby issue for more details.
  5. That's it ! Run it as something like
    # set the CLASSPATH variable to include
    # rails-module, grizzly, JDBC driver, 
    # JRuby libs(jruby.jar, jvyaml, asm, jline and jruby-openssl)
    java -classpath ${CLASSPATH} -Djruby.base=${JRUBY_HOME}
        com.sun.grizzly.rails.standalone.Main ${RAILS_ROOT}
    
    Or download the script and modify it to run in your environment.

Friday Jan 19, 2007

Grizzlicious ... RoR on GlassFish

In some of the previous episodes, I described how to go about deploying a Ruby on Rails(RoR) application on GlassFish. The first post described the method of deployment in which the RoR application is packaged as a WAR file, whereas the later posts(here and here) utilized the native Ruby interpreter at runtime(RoR-as-WAR runs on JRuby).

However, both deployment models are not quite ideal for a production deployment scenario. Enter Grizzly ! The NIO based HTTP connector for GlassFish. The beauty of Grizzly is not only that it's fast and scalable but that it is also highly extensible. TAKAI Naoto was quick to realize this fact and quickly dished out the first version of the adapter code needed to deploy RoR applications directly on top of Grizzly.

Without further ado, the steps to get a RoR application working as a first-class citizen within GlassFish:

  1. Download and install GlassFish in ${GLASSFISH_HOME}
  2. Download rails-module-0.1-SNAPSHOT.jar to ${GLASSFISH_HOME}/lib
  3. Checkout the latest source of JRuby and build it.
    Copy jruby.jar jvyaml.jar and asm-2.2.2.jar and to ${GLASSFISH_HOME}/lib
  4. Copy plaincharset.jar(in ${JRUBY_HOME}/lib) to jre/lib/ext of your Java runtime installation.
  5. Install the rails libraries including activerecord-jdbc. See step #3, #4 .
  6. The rails applications should installed under ${GLASSFISH_HOME}/domains/${domain-name}/applications/rails and you still need to do edits to get jdbc adapter working with JRuby. See steps #7 and #8 from the previous post
  7. Modify GlassFish configuration (which persists in ${GLASSFISH_HOME}/domains/${domain-name}/config/domain.xml - you could use the great administration UI - typically available at port 4848 via the browser or the command line interface to perform the changes) as below.
    • Add RoR specific http listener
          <http-listener acceptor-threads="1" address="0.0.0.0" 
                blocking-enabled="false" default-virtual-server="server" enabled="true" 
                family="inet" id="http-listener-rails" port="8081"
                security-enabled="false" server-name="" xpowered-by="true">
      
              <property name="selectorThreadImpl" value="com.sun.grizzly.rails.RailsSelectorThread"/>
          </http-listener>
      
    • To the java-config add the system property 'jruby.base'
          <jvm-options>-Djruby.base=jruby-home-value-here</jvm-options>
      
  8. Modify $GLASSFISH_HOME}/domains/${domain-name}/applications/rails/config/environments/development.rb by setting the property config.breakpoint_server to false. Once issue #427 in JRuby is fixed this step will not be needed.
Give it a shot !

Monday Jan 08, 2007

Derby's Identity column trivia

In a previous post, I described the SQL command to create a table with an auto increment column in the Derby database. It turns out that the 'GENERATED' keyword can be used in a couple of way.
  • GENERATED ALWAYS AS IDENTITY
  • GENERATED BY DEFAULT AS IDENTITY
For details see the relevant section of the Derby manual here. Essentially the distinguishing fact being - by using 'BY DEFAULT' the user has the ability to specify a value for the identity column as part of a (insert) SQL statement, whereas with 'ALWAYS', the column can never be explicitly edited by the user. Also 'ALWAYS' guarantees uniqueness, but 'BY DEFAULT' does not (use the UNIQUE/PRIMARY KEY attribute to achieve the same effect though)

So, the better suited SQL statement for creating a table in Derby in a RoR application would be

CREATE TABLE ${table-name} ( 
    ID INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, 
    ${other-columns-and-constraints} )
Running a simple RoR application with the underlying identifier created via either option works without any issues. However, if you want to run the tests that comes in built with Rails framework the difference is detrimental (since the test framework seems to explicitly want to insert a value for the identifier column).

Tuesday Dec 19, 2006

GlassFish and Ruby - Part II

In part I, a GlassFish server was dedicated to hosting a single RoR application running on a native Ruby interpreter. Starting with changes in build 29, GlassFish v2 is capable of hosting multiple RoR applications along with your other web/ejb applications. All you need to do is make a couple of minor tweaks
  • Specify the context root associated with the RoR application in the descriptor file and the 'stripRequestURI' initialization paramater along with others specified in part I as illustrated below.
         <servlet>
            <servlet-name>cgi</servlet-name>
            <servlet-class>org.apache.catalina.servlets.CGIServlet</servlet-class>
            ...
            <init-param>
              <-- stripRequestURI parameter value maps to context root -->
              <-- in this case - http://.../ror/${ror-request-URI} -->
              <param-name>stripRequestURI</param-name>
              <param-value>/ror/</param-value>
            <init-param>
            ...
        </servlet>
    
    
        <servlet-mapping>
              <servlet-name>cgi</servlet-name>
              <url-pattern>/ror/\*</url-pattern>
        </servlet-mapping>
    
  • Since Rails auto-generates links, you'll have to make the following change in config/routes.rb
      # Specify the context root as the relative URL for all requests
      ActionController::AbstractRequest.relative_url_root = "/ror"
    
      # Install the default route as the lowest priority.
      map.connect ':controller/:action/:id'
    
Rails application will be accessible @ http(s)://.../ror/...
Simply replicate the configuration with the customized value of context root and the 'cgiPathPrefix' initialization parameter pointing to each application's specific 'dispatch.cgi' to host multiple RoR applications !

Friday Dec 15, 2006

GlassFish and Ruby

GlassFish and JRuby are obviously a great match, since both run on the Java virtual machine which allows Ruby code running on JRuby to access Java framework and libraries and the possibilities are endless.

However, GlassFish is also capable of running Ruby code via a native Ruby interpreter. This is possible using CGI support in GlassFish. For this first post, I shall keep things simple and demonstrate how you could go about dedicating an entire instance of the GlassFish server for a single RoR application

All you'll need is to install GlassFish v2 build 29 or greater, the native Ruby interpreter with the Rails framework libraries and make some minor modifications to GlassFish configuration. Of course, you'll need to create your rails application. The minor modifications amount to modifying ${glassfish.home}/domains/${domain-name}/config/default-web.xml with the following CGI configuration enabled.

   <servlet>
        <servlet-name>cgi</servlet-name>
        <servlet-class>org.apache.catalina.servlets.CGIServlet</servlet-class>
        <init-param>
          <!-- in this case the path will be appended to
               ${glassfish.home}/domains/${domain-name}/docroot/ -->
          <param-name>cgiPathPrefix</param-name>
          <param-value>${rails-root-dir}/public/dispatch.cgi</param-value>
        </init-param>
        <init-param>
          <param-name>executable</param-name>
          <param-value>${path-to-ruby}</param-value>
        </init-param>
        <init-param>
          <param-name>passShellEnvironment</param-name>
          <param-value>true</param-value>
        </init-param>
        ...
    </servlet>

    <servlet-mapping>
        <servlet-name>cgi</servlet-name>
        <url-pattern>/\*</url-pattern>
    </servlet-mapping>

On UNIX make sure that the native Ruby interpreter is in the PATH and LD_LIBRARY_PATH is set to satisfy the native Ruby interpreter's/Rails dependencies in the GlassFish process's environment. This is the reason for configuring the 'passShellEnvironment' parameter to true.

That's pretty much all there is to it. Accessing http://${host}:${port}/ should bring up the default Rails page !

Wednesday Dec 06, 2006

RoR in GlassFish with Derby as the database

In my previous post I wrote about getting a RoR application to work on GlassFish as a web application. Since the Derby database is embedded in GlassFish why not try to use it as the backend datastore for the RoR-web-application.

Getting a Ruby on Rails application to work with a Derby database should be relatively straightforward. Well it is, but only after you've jumped through a few hoops trying to figure things out for the first time.

1. Modify config/database.yml to something like:

development:
 adapter: jdbc
 driver: org.apache.derby.jdbc.ClientDriver
 url: jdbc:derby://${database host}:${database port}/${database name}
 username: ${username}
 password: ${password}
Derby has the concept of 'database name' and 'schema'. If 'schema' is unspecified it defaults to 'username'

2. Create table(s) in Derby. Points to note here are:
   2.1 Use upper case for all table/column names
   2.2 To create the auto increment identifier column use the following SQL statement

CREATE TABLE ${table-name} ( ID INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, ${other-columns-and-constraints} )


I'm not sure what the SQL standard is on auto increment column types but this was bit of a pain to figure out. Also remember that 'DESC' is a reserved work in SQL terminology so do not use that as a column name.

The Derby jdbc driver should by default be in the classpath of GlassFish application server so no tweaking needed on that account. However, if you are running the RoR application on JRuby/WEBrick combo you'll need to add the derby-jdbc-driver library to the classpath.

That's pretty much all there is to it. Give it a shot !

Friday Dec 01, 2006

Deploying a Ruby on Rails application in GlassFish

There are a few ways a Ruby on Rails(RoR) application could be deployed on a Java EE based application server platform. One of them is to create a WAR file containing the RoR application with the appropriate adapter code between the application server and the ruby application. On how to go about developing and deploying a RoR application on GlassFish - an open source Java EE 5 application server - follow the steps below:

1. Install GlassFish application server
2. Install JRuby
3. Install Rails Framework 'gems install rails -y' (if behind a http proxy, set HTTP_PROXY=http://${http-proxy-host}:${http-proxy-port}/)
4. Install activerecord-jdbc 'gems install activerecord-jdbc'
5. Install database/jdbc driver (for mysql http://mysql.com/)
6. Generate your Ruby on Rails application (http://rubyonrails.org/)
7. Modify database.yaml
       development:
           adapter: jdbc
           driver: com.mysql.jdbc.Driver (for mysql)
           url: jdbc:mysql://${database-hostname}/${db-schema}
           username: ${username}
           password: ${password}
8. Modify environment.rb by adding
       require 'active_record/connection_adapters/jdbc_adapter'
9. Download rails-integration-${version}-SNAPSHOT.jar into the WEB-INF/lib by checking out and building the rails-integration project
svn://rubyforge.org/var/svn/jruby-extras/trunk/rails-integration
10. Modify the template web.xml(see references below) with the right value of jruby.home
11. Create a WEB-INF directory in the RoR application directory
12. Copy web.xml to WEB-INF and rails-integration-${version}-SNAPSHOT.jar under WEB-INF/lib
13. Jar up the RoR application directory contents as a WAR file

Requests to appropriate context-root of the deployed web application should invoke your Ruby code !

If you'd like to automate the building of a war file:
Edit rails-integration/build.xml and add the following XML snippet
  <property environment="env"/>
  <target name="build-rails-war" depends="jar">
    <delete file="${rails-app-dir}/${rails-app-name}.war"/>
    <mkdir dir="${rails-app-dir}/WEB-INF/lib"/>
    <copy todir="${rails-app-dir}/WEB-INF/lib">
      <fileset file="${maven.build.directory}/${maven.build.final.name}.jar"/>
      <fileset file="${maven.repo.local}/org/jruby/jruby/0.9.1/jruby-0.9.1.jar"/>
      <fileset file="${maven.repo.local}/asm/asm/2.2.2/asm-2.2.2.jar"/>
      <fileset file="${maven.repo.local}/javax/activation/activation/1.1/activation-1.1.jar"/>
    </copy>
    <copy todir="${rails-app-dir}/WEB-INF/">
      <fileset file="samples/scaffold/WEB-INF/web.xml"/>
    </copy>
    <replace file="${rails-app-dir}/WEB-INF/web.xml" token="/usr/local/jruby" value="${env.JRUBY_HOME}"/>
    <jar jarfile="${rails-app-dir}/${rails-app-name}.war" basedir="${rails-app-dir}"/>
  </target>

Make sure JRUBY_HOME is set and run
'ant -Drails-app-dir=${ror-app-dir} -Drails-app-name=${war-file-name} build-rails-war'
${ror-app-dir}/${ror-app-name}.war should be ready for deployment !

All of this is work in progress some of this might change very soon. Thanks to rail-integration developers and the jruby folks !
About

whacko

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today