Main | Next page »

Scala Puzzlers, part 2

Jul 24 2008, 14:51 MSD |  [  Scala  ]

This puzzler is very strange. I have not completely figured out what causes the described performance problem yet, but I guess that it is a bug in the JVM, or in Scala compiler, or both.

I tried myself at Google Code Jam Qualification round this year and there was a nice problem called Fly Swatter (unfortunately there's no direct link to the problem description, but you can find it by clicking the “Qualification Round” link on the GCJ Contest page). It's fairly simple and it didn't take long to write a working solution in Scala. My solution produced correct results for the small test dataset, then I downloaded the large dataset (once downloaded one has 8 minutes to submit the results back), and…

Performance problem puzzler

… I figured out that my program was horribly slow. I didn't expect the program to work that slow: during the 8 minutes I'd had less than 30 (of 99) results were produced. Only after 32 minutes I got the results, alas, too late. I knew that in the worst case I'd get something like 250000 iterations for each test case, where each iteration computed couple of arcsines and several square roots, but hey, computers are fast these days, and I knew I didn't screw up with the algorithm (there were some obvious ways of improvement, but I figured that it was not necessary to use them — the performance should have been fine without them).

Now, here's the deal: my slow Scala program follows at the bottom of the post (you can also download it). Try to figure out what's wrong with it and how you can improve it's speed by two orders of magnitude (I'm not kidding you — it takes 8.5 seconds to run now on the same laptop). One caveat though: run the compiled program using Java HotSpot Server VM (it is used by default on my laptop, and only the Server VM shows the problem, not the Client VM), e.g.:

% time ( cat C-large.in | java -server -cp .:$SCALA_HOME/lib/scala-library.jar FlySwatter )

Input data should be piped to standard input, use the C-large.in dataset to test the program.

Spoiler

If you're Martin Odersky (and you want to check if it is a bug in Scala compiler) or if you're just too lazy to spend your time to inspect the code, profile, get puzzled because you don't get any meaningful results from the profiling, and then give up, take a look at the fixed program (there's a comment at the top which describes what has been done). Prepare to be surprised!

Slow version

/*
 * This is a slow version of the FlySwatter program.
 * It performs poorly on Server VM.
 * Try to find out why!
 */
import java.io._

object FlySwatter extends Application {
  case class Square(x1: Double, y1: Double, side: Double) {
    val x2 = x1 + side
    val y2 = y1 + side

    def area = side*side

    def isValid = side > 0

    def contractBy(f: Double): Square = {
      if (2*f >= side) {
        Square(x1 + side/2, y1 + side/2, 0)
      } else {
        Square(x1 + f, y1 + f, side - 2*f)
      }
    }
  }

  case class Case(f: Double, rr: Double, t: Double, r: Double, g: Double)

  def parseData: Seq[Case] = {
    val in = new BufferedReader(new InputStreamReader(System.in))
    def readCase: Case = {
      def readWords: Seq[String] = in.readLine.split(' ').filter(_.length > 0)

      val caseData = readWords.map(_.toDouble)
      Case(caseData(0), caseData(1), caseData(2), caseData(3), caseData(4))
    }

    val numCases = in.readLine.toInt
    val cases = for (i <- (1 to numCases).force) yield readCase
    cases
  }

  def calculateProbability(c: Case): Double = {
    def pointInCircle(x: Double, y: Double, r: Double): Boolean = {
      x*x + y*y < r*r
    }
    def squareAndCircleIntersectionArea(s: Square, r: Double): Double = {
      // calculates the definite integral from a to b of sqrt(r^2-x^2) dx
      def integrateCirclePart(a: Double, b: Double, r: Double): Double = {
        def i(x: Double): Double = x*Math.sqrt(r*r - x*x)/2 + r*r/2 * Math.asin(x/r)
        i(b)-i(a)
      }
      assert(s.x1 > 0 && s.y1 > 0)
      assert(r > 0)

      if (s.isValid) {
        val lowerLeftInCircle = pointInCircle(s.x1, s.y1, r)    // using lazy vals for these four booleans
        val upperLeftInCircle = pointInCircle(s.x1, s.y2, r)    // makes the program run ~25% slower,
        val lowerRightInCircle = pointInCircle(s.x2, s.y1, r)   // however real performance hit is probably lower
        val upperRightInCircle = pointInCircle(s.x2, s.y2, r)   // because the measurement precision is quite low

        val area: Double = (lowerLeftInCircle, upperLeftInCircle,
                    lowerRightInCircle, upperRightInCircle) match {
          case (false, _, _, _) => 0                                // square is outside of the circle
          case (true, _, _, true) => s.area                         // whole square is in the circle
          case (true, false, false, false) =>                       // single corner is in the circle
            val x = Math.sqrt(r*r - s.y1*s.y1)
            integrateCirclePart(s.x1, x, r) - (x-s.x1)*s.y1
          case (true, true, false, _) =>                            // left side of the square is in the circle
            integrateCirclePart(s.y1, s.y2, r) - (s.y2-s.y1)*s.x1
          case (true, false, true, _) =>                            // bottom side of the square is in the circle
            integrateCirclePart(s.x1, s.x2, r) - (s.x2-s.x1)*s.y1
          case (true, true, true, false) =>                         // 3 corners of the square are in the circle
            val x = Math.sqrt(r*r - s.y2*s.y2)
            (x-s.x1)*(s.y2-s.y1) + integrateCirclePart(x, s.x2, r) - (s.x2-x)*s.y1
        }
        assume(area >= 0)
        area
      } else {
        0 // empty square
      }
    }

    assert(c.g + 2*c.r > 0)
    val maxI = Math.ceil((c.rr - c.t - c.r)/(c.g + 2*c.r)).toInt
    val squares = for {
      i <- 0 until maxI; j <- 0 until maxI // until method creates a lazy list, thus we get a lazy list of Squares in the end
      x1 = c.r + i*(c.g + 2*c.r)
      y1 = c.r + j*(c.g + 2*c.r)
    } yield Square(x1, y1, c.g).contractBy(c.f)

    val squaresAndCircleIntersectionArea =
      (0.0 /: squares.map(squareAndCircleIntersectionArea(_, c.rr - c.t - c.f)))((a: Double, b: Double) => a+b)

    val flyIsSafeProb = squaresAndCircleIntersectionArea / (Math.Pi * c.rr * c.rr / 4)
    1-flyIsSafeProb
  }

  val cases = parseData
  for (i <- 0 until cases.length) {

    val prob = calculateProbability(cases(i))
    System.out.printf("Case #%1$d: %2$.6f\n", Array[Object](new java.lang.Integer(i+1), new java.lang.Double(prob)))
  }
}

Enjoy!

Scala puzzlers, part 1

Jul 18 2008, 04:17 MSD |  [  Scala  ]

I participated in the ICFP Contest this year. We decided to use Scala, and one of the reasons for using it was that we wanted to try the language out in a real-world stressful development project. There were four of us, and at least two of us (including me) had some experience with Scala. I'd been playing with the language for quite a while, and had a fair understanding of how to write programs in it. I'll probably post some write-up about ICFPC later.

During the contest we hit some trouble spots with the language, and lack of experience played some cruel tricks on us. This post is partly inspired by this experience. So, here we go, Scala puzzlers!

Puzzler #0: Weak typing

Consider the following code:

sealed abstract case class Msg
case class SaveTheWorld(from: String) extends Msg
case class FeedTheCat(name: String) extends Msg
case class GoToSleep extends Msg

object Thespian extends Actor {
  def act = {
    loop {
      react {
        case SaveTheWorld(threat) => println("Saving the world from the " + threat)
        case FeedTheCat(cat) => println("Feeding " + cat)
        case GoToSleep() => println("Zzzzz...")
        case Die(msg) => prinln(msg); exit 
      }
    }
  }
}

object WeakTyping extends Application {
  Thespian.start

  Thespian ! FeedTheCat("Panther")
  Thespian ! SaveTheWorld("Jabberwock")
  Thespian ! GoToSleep

  Thespian ! Die("The fair Ophelia! Nymph, in thy orisons be all my sins remember'd.")
}
Everything looks pretty simple, however there's a line missing in the output:
Feeding Panther
Saving the world from the Jabberwock
The fair Ophelia! Nymph, in thy orisons be all my sins remember'd.

The reason for this is that GoToSleep is not an object of type Msg, it is a function, which returns an instance of the case class GoToSleep when it is executed. You're not doing anything illegal when you're sending a function to the actor, so the compiler isn't going to stop you. And since parentheses are optional in a 0-argument method call, it's easy to forget to put the parentheses in case class instantiation.

There is another way to screw up: instead of matching on GoToSleep() one could try to pattern-match on GoToSleep in the react block.

A good way to solve this problem once and for all is to always use case objects instead of parameterless case classes. That way you won't need the parentheses and unless you have an apply method in your case object, compiler wouldn't allow you to use them.

Puzzler #1: val vs def

This one is quite obvious, but sometimes you can get caught on it during the refactoring.

def someEntity = new Entity(...)
val a = doSomething(someEntity)
val b = doSomethingElse(someEntity)

If doSomething and doSomethingElse expect to get the same object as an argument, you'd get a problem. It can be much more subtle if there is some method call instead of the explicit class instantiation in the first line. Again, it is easy to get confused because the parentheses are not required for 0-argument functions.


I have at least two more puzzlers and they are a little more involved. I'll try to publish them as soon as I get enough sleep (it's 4am already).

PS: If you know some Scala puzzlers, please send them to me, and I'll add them to the blog. Alternatively, publish them in your blog and be sure to send me the link!

Final Mercurial repositories of JDK7

Dec 06 2007, 10:52 MSK |  [  Java  ]
ivan@adagio:~/work % time hg fclone http://hg.openjdk.java.net/jdk7/jdk7/
[.]
requesting all changes
adding changesets
adding manifests
adding file changes
added 2 changesets with 26 changes to 26 files
26 files updated, 0 files merged, 0 files removed, 0 files unresolved

[corba]
requesting all changes
adding changesets
adding manifests
adding file changes
added 2 changesets with 1369 changes to 1369 files
1369 files updated, 0 files merged, 0 files removed, 0 files unresolved

[hotspot]
requesting all changes
adding changesets
adding manifests
adding file changes
added 2 changesets with 2895 changes to 2895 files
2895 files updated, 0 files merged, 0 files removed, 0 files unresolved

[jaxp]
requesting all changes
adding changesets
adding manifests
adding file changes
added 2 changesets with 1973 changes to 1973 files
1973 files updated, 0 files merged, 0 files removed, 0 files unresolved

[jaxws]
requesting all changes
adding changesets
adding manifests
adding file changes
added 2 changesets with 2307 changes to 2307 files
2307 files updated, 0 files merged, 0 files removed, 0 files unresolved

[jdk]
requesting all changes
adding changesets
adding manifests
adding file changes
added 2 changesets with 16527 changes to 16527 files
16527 files updated, 0 files merged, 0 files removed, 0 files unresolved

[langtools]
requesting all changes
adding changesets
adding manifests
adding file changes
added 2 changesets with 2974 changes to 2974 files
2974 files updated, 0 files merged, 0 files removed, 0 files unresolved

hg fclone http://hg.openjdk.java.net/jdk7/jdk7/  51.62s user 11.27s system 9% cpu 10:32.51 total

Yes, now you can really start working on your own fixes for JDK and be able to get them into the official repositories if they are that good. See also this post on Mark Reinhold's blog.

Enough said! Go get the JDK! :-)

OpenJDK Mercurial repositories are open

Nov 02 2007, 17:26 MSK |  [  Java  ]

Finally, we have the Mercurial repositories of JDK7 out in the open. They are still EXPERIMENTAL (yeah, in capitals), which means that these repositories (and their clones) are going to become invalid (totally unrelated to the repositories which would be used) in a couple of weeks, but you can still try to set up your environment and development process (if any) using them. Again, please don't do any real work on the clones of these repositories, the changesets you'd have probably would be useless when these repositories become officially open.

Here is a short instruction to get started with hacking the JDK. I assume that you have Mercurial set up. The build instructions here should work for Debian GNU/Linux (this is what I use on my laptop), however I don't promise anything :-) It should be relatively easy to do the same thing on Ubuntu (I doubt there are any differences other than the available package names/versions). It may require more work on other flavors of Linux. Still, if you have some problem building the JDK, you can ask me and I'll try to help you investigate (and, hopefully, fix) the problem.

Prerequisites

I'll assume that you have the following things set up and working:

  • JDK 6
  • Mercurial
  • GCC (>= 4.0)
  • Ant (>= 1.6.3)
  • FindBugs (>= 1.1)
  • GNU make
  • …and lots of other required libraries (see the README files for more info)

Getting the sources

Create the directory where you're going to have your repositories.

/home/ivan/work% mkdir openjdk
/home/ivan/work% cd openjdk

If you have the newer Mercurial with the forest extension, you can simply do

/home/ivan/work/openjdk% hg fclone http://hg.openjdk.java.net/jdk7/MASTER/
but if you don't, just follow the instructions below.

First, clone the master repository

/home/ivan/work/openjdk% hg clone http://hg.openjdk.java.net/jdk7/MASTER/

Clone all the other repositories into the MASTER repository:

/home/ivan/work/openjdk% cd MASTER
/home/ivan/work/openjdk/MASTER% for i in corba hotspot jaxp jaxws jdk langtools; hg clone http://hg.openjdk.java.net/jdk7/MASTER/$i/

After getting the sources, download the binary plugs from the OpenJDK download site and install them (run java -jar <downloaded-plugs-jar>) into some directory (I installed them in openjdk directory which I created above).

GCC 4.2 peculiarities

Now, if you are going to use GCC 4.2 or newer to build the JDK, you'll need to apply the patch to the HotSpot source before you continue. GCC 4.2 is more pedantic than the older ones and doesn't allow one to cast literal strings to char *, now you can only cast to const char * (which makes a lot of sense if you want to minimize the possibility of having nasty bugs which are hard to track). However the patch is a bit hackish: I use const_cast in a couple of places because it is the quickest and least intrusive fix I can provide there. This compilation issue should be addressed separately in JDK7 code-base (maybe in next build?).

First, download the patch, say, to /tmp/const_strings.patch. Now you have two choices: either use patch tool to apply the patch to the hotspot sources

/home/ivan/work/openjdk/MASTER/hotspot% patch -p1 < /tmp/const_strings.patch
or use the Mercurial Queues (the preferred way)
/home/ivan/work/openjdk/MASTER/hotspot% hg qinit
/home/ivan/work/openjdk/MASTER/hotspot% hg qimport /tmp/const_strings.patch
/home/ivan/work/openjdk/MASTER/hotspot% hg qpush const_strings.patch
You can read more about Mercurial Queues in this excellent tutorial (I suggest reading the other chapters too).

Setting up the environment

Build scripts use some environment variables to figure out what tools to use, where the JDK is, how many build threads should work concurrently, etc. You can either export these environment variables manually (like it is shown below) or create a script which sets the variables and executes the GNU make.

Here is what I did manually in shell:

% export LANG=C
% export ANT_HOME=/usr/share/ant 
% export FINDBUGS_HOME=~/work/findbugs-1.2.1 
% export ALT_BINARY_PLUGS_PATH=~/work/openjdk/openjdk-binary-plugs
% export ALT_BOOTDIR=/usr/local/java/jdk1.6.0
Fix the paths according to where you have the things installed. Then run make sanity and inspect the log. Fix the variables, run make sanity. Fix again, … I think you got it.

Although it isn't immediately clear from make sanity output and from the README files, you'd need to export one more variable: ALT_JDK_IMPORT_PATH, and according to the Glossary for JDK builds it should point to the path with a recent build of the same version of JDK we are building. Actually, it is a bug in the corba build files, and hopefully it would be fixed when the repositories are officially rolled out.

Building the JDK

Run make and go have a lunch :-)

Oh, you might want to have a tea first, just to be able to fix some minor problem that breaks the build in your case.

For reference: it took ~ 55 minutes to build the JDK on my 2Ghz laptop. The build directory size is 1.1Gb, although it may take more than that during the build (I didn't bother to measure).

Post-scriptum

Kudos to Kelly O'Hair and the JDK team, the build process now is very easy compared to what we had a little more than a year ago.

Remote access “life hack”

Oct 21 2007, 15:50 MSD |  [  Life hack  ]

Suppose that every time when you log in to some (possibly remote) machine using ssh you need to recall the path of some important directory, or that you want to be reminded to do something once you log in the next time. I recently found that this is a very usual scenario for me (because I ssh a lot and my memory is already stuffed with lots of things). So I invented a simple solution.

I added the following lines to my .zshrc file in my home directory:

if [ -f ~/.motd ]; then
  echo ----------------------------------------------------------
  cat ~/.motd
  echo ----------------------------------------------------------
fi
which simply cat's the contents of the .motd file whenever I log in. Now, if I want to be reminded of something, I can do
$ echo RTJ binaries are here: /net/xxx/yyy/zzz/nightly-build/build-abc/arch/bin >> ~/.motd
and the next time I log in, I'd get a message from which I can copy the useful path. It is also useful for setting up the reminders like “feed the cat, you lazy sod!”, although there is a risk for a cat to starve if you need to work at a single host without logging in for a long time.

You can simply remove a file once the validity of the notification expires or edit it using your favorite text editor to change/remove the bits of the contents.

Managing GlassFish server using JRuby

Apr 22 2007, 22:14 MSD |  [  JRuby  ]

If you are a developer writing an application which is inteneded to run on a GlassFish server, you are likely to do a lot of deployment work on your development/test servers. That's just the kind of work I do. And although I don't need to recreate the entire domains very often, I have to do that occasionally, and the process of configuring the application server domain through the web administration console is a pain, especially when you need to go through several wizards to create all those connection pools and JDBC resources which your application needs, set the JVM start parameters, etc., and you need to set up several domains that way (hey, GlassFish developers, is there some standard way I can do that work during the deployment of the application automatically?). Having done that several times, I decided that writing some tool to do all the work for me makes a lot of sense.

I tried to use shell scripts which called a bunch of asadmin commands to configure the domain and deploy the applications, but that didn't work out quite well. And I had some strange problems with remote application deployment using asadmin, so I had to ssh to the remote server and do the work locally. I guess I could create the necessary infrastructure for asadmin approach to work, but I felt that the result would be quite ugly. And I remebered that I had learnt about another interesting possibility last summer, when I had been helping guys to update the JDK samples for the upcoming release of the JDK, that was JMX. I did play with JMX for some time in the fall, but it never had any useful application in my work. This time I decided to use JMX for some real problem which I had been having for such a long time — application server domain configuration.

There are lots of good things in Java, but there are also some things which one may find frustrating to deal with under certain circumstances. One of such things is code verbosity, which is very thwarting when you need to write some small tool or just find out how the API work. One of the solutions to the code verbosity problem is to use some scripting language which can call Java APIs: there are BeanShell, JRuby, Jython, Groovy, Rhino, and many more other nice scripting languages. I'm watching closely the improvements in JRuby since last summer and I find Ruby adorable, hence the choice of a scripting language for the task was easy for me.

Configuration of the JMX connection on the server

Before we start connecting to the application server, we need to know some basic information: server address, JMX port (8686 by default), administrator user name (‘admin’ by default), and whether the server accepts only secure JMX connections, or not. JMX secure connections are disabled by default in GlassFish developer profile and enabled in cluster profile. However, I suggest you enabling the security for the JMX connector even for the developer domains: thus you'd understand better how the secure connections work and you wouldn't have the problems when you decide to deploy your applications on the server which has the secure connections enabled.

Creating connection pools

There is not much information about GlassFish management using JMX on the internet, at least I had a lot of problems finding the important pieces of information, most of them were obtained by reading the GlassFish sources. There is the AMX Javadoc, but it doesn't help to understand, how one should use the API. I discovered that the best way to understand, how the things work, is to use jirb — the JRuby version of the Interactive Ruby — in a typical session I simply instantiated some Java objects, looked at their method names and tried to call them and see what comes out.

To call the Java code from JRuby script, you need to do

require 'java'
in your script. You can use the same code in jirb. See the ‘Calling Java from JRuby’ page for more information on how to interoperate with Java code. There are also some code samples in JRuby distribution which explain the basic things. Unfortunately, you cannot load jar files in your JRuby code (or in a jirb session) and then use the classes from the library, so to use the AMX API you need to set the classpath before calling jirb:
$ CLASSPATH=/opt/glassfish/lib/javaee.jar:/opt/glassfish/lib/appserv-ext.jar jirb
/opt/glassfish is my path to the installed GlassFish. Now you can require 'java' and follow the rest of this post.

Connecting to the server

To connect to the application server you need to instantiate the com.sun.appserv.management.client.AppserverConnectionSource class with the parameters describing your connection:

conn = AppserverConnectionSource.new(AppserverConnectionSource::PROTOCOL_RMI, host, port, user, password, tls_params, nil)
conn.getJMXConnector(false)
Parameter names are self-describing, with the exception of, maybe, tls_params — it is an instance of the com.sun.appserv.management.client.TLSParams class and it contains the security settings for your connection. If you are using insecure JMX connection on the server, you may pass nil (which is treated as null by JRuby, when you pass it as a parameter to some Java method) in place of tls_params.

If you are using secure JMX connections, you have two basic strategies: you may trust any certificate which the server sends you during the TLS handshake, or you may want to check that you have this certificate (or the certificte of the authority which signed it, if the certificate is not self-signed) in your trust-store. It is possible to implement different authentication strategies (e.g., asking the user, whether she trusts the given certificate), but it is out of the scope of this blog post.

To implement the first (trust any certificate) strategy, you can use the following code:

TLSParams = com.sun.appserv.management.client.TLSParams
TrustAnyTrustManager = com.sun.appserv.management.client.TrustAnyTrustManager

tls_params = TLSParams.new(TrustAnyTrustManager.getInstanceArray, nil)
To implement the second (use trust-store to check the certificate) strategy, you should read the trust-store file and create the respective trust-store manager:
TLSParams = com.sun.appserv.management.client.TLSParams
TrustStoreTrustManager = com.sun.appserv.management.client.TrustStoreTrustManager
X509TrustManager = javax.net.ssl.X509TrustManager

store = java.io.File.new(trust_store_file_name)
store_password = java.lang.String.new(trust_store_password).to_char_array
trust_managers = X509TrustManager[1].new(TrustStoreTrustManager.new(store, store_password))
tls_params = TLSParams.new(trust_managers, nil)

Hopefully, you're able to connect to the server now. To control the domain configuration, we need to obtain the DomainConfig object from the established connection:

domain_root = conn.domain_root
domain_config = domain_root.domain_config
Notice how Java integeration code in JRuby automatically creates the Ruby-style accessors for the getters and setters.

Creating a connection pool

Creation of a connection pool is simple:

pool = domain_config.createJDBCConnectionPoolConfig(pool_name, datasource_class, nil)
where pool_name is an identifier of the connection pool and datasource_class is the name of the Java class used as data source (e.g. oracle.jdbc.pool.OracleDataSource for Oracle database). Having that line executed you can even check in the server admin console that the respective conection pool was created.

Then you need to configure the created connection pool. The first thing you might want to configure is the “Resource Type”:

pool.res_type = 'javax.sql.DataSource' # other legal values are ‘javax.sql.XADataSource’
                                       # and ‘javax.sql.ConnectionPoolDataSource’
All parameters which are available from the server admin console can be set using the corresponding methods of the pool variable (which is a proxy of an instance of JDBCConnectionPoolConfig class).

Apart from setting the standard connection pool parameters you might want to pass some additional properties to the database driver (see the ‘Additional Properties’ tab on the connection pool configuration page in admin console). To do this, you need to call the set_property_value(key, value) method of the pool object.

When you are done, you can use web administration console to test the connection (using the “Ping” button on the connection pool configuration page). Probably you can do it from JRuby script directly — but I have not figured it out yet.

Creating a JDBC resource

Once you have a connection pool, you need a corresponding JDBC resource which you can use from your application. The creation of the JDBC resource is a two step process (and it was very hard to figure out the second step — I had to read quite a lot of GlassFish code to understand what's going on): first we create the resource configuration object and after that we associate the created object with all the server instances on which we want to be able to access it.

domain_config.createJDBCResourceConfig resource_name, pool_name,
    HashMap.new({com.sun.appserv.management.config.ResourceConfigKeys::ENABLED_KEY => true })
domain_config.server_config_map['server-instance'].create_resource_ref resource_name, true
The first line here creates the JDBC resource configuration object, and the second adds the resource reference to the ‘server-instance’ server configuration.

Wrapping it all up in a script

After I got everything working in jirb, I decided to write a script, which would read the connection pool definitions from the YAML file and create them on the given application server instance, see the link below. When started it would print the usage information, you can also see the YAML configuration files examples in the script comments right after the license text. Note that it is actually a shell script which wraps up a JRuby script: thus it is possible to set up CLASSPATH (and, optionally, various Java VM options) before the jruby execution. Because of this it probably would not work on Windows out of the box — you'd need to strip out the environment setup code and jruby invocation (and don't forget to remove the END_OF_SCRIPT line!). Having that done you'd need to set up the CLASSPATH manually and start the script using the jruby.

You can download the script here or see a nice syntax-highlighted version here.

PS: I'm looking forward to hear your suggestions about using JRuby (or other nice languages) to do the similarly routine tasks!

Unicode support in JRuby

Apr 08 2007, 00:53 MSD |  [  JRuby  ]

I've been playing quite a lot with JRuby recently, reading its sources, writing cool scripts in it (I'm gonna post some here soon) and asking for help on JRuby developers mailing list. The reason why I needed to ask for help was a problem with Unicode support which I encountered: whenever I tried to pass a string with some Unicode characters across the Java-JRuby boundary (in either way), the characters got scrambled. Thus I couldn't even pass a Unicode string which I'd received from some Java method into the other method, the code

java.lang.System.out.println my_java_object.get_some_string()
would have printed a string of the characters with everything except the lowest byte discarded.

Once I figured out the most common scenarios of JRuby usage which could require the Unicode support, I decided to write a message to the JRuby interest alias (although my questions were redirected to the developers mailing list soon after that). After a couple of messages Charles decided to fix a problem, and now we have a working (although far from complete) Unicode support in JRuby trunk, see a recent blog post of Charles. Of course, Unicode support needs more work, and before Ruby 2.0 it is tricky to do the things in such a way that they don't get obsolete in future. But now, the major problem is fixed: after I'd updated my JRuby from SVN, passing Unicode strings back and forth between JRuby and Java started to work correctly. That means that I can use all the Java Unicode support methods straight from JRuby.

One thing which I really like about this, is how quickly the problem was resolved. And if you find that you have some major problem with JRuby, don't hesitate to ask to fix it (and don't forget to tell, how you'd prefer it to work when fixed) — feedback is very important for the developers, who love the product they are working on, and they'd love to help you resolve the problem.

Migrating to JRuby

Nov 23 2006, 02:17 MSK |  [  Ruby  ]

One of the applications I develop at Sun uses quite complex process of getting the data from the database. One of the parts is a JNLP application with Swing GUI which presents the data to the user. To get the data it executes the web service method with a parameter which defines the query name. For each query name there exists a script-like definition of the database query which is transformed by the web service into a SQL query. To make the things even more interesting, the web service can execute some Ruby scripts to transform/produce that query. Then it queries the database and returns the data set to the JNLP application.

Everything worked fine until some environment change was introduced: I tried to run the web service on my development machine to avoid testing the application on the production servers. It didn't produce the query and the error messages were rather cryptic. After some investigation my colleague found the reason: the Ruby scripts which were executed to produce the query were executed from the network share as simple UNIX executable files and they had a specific shebang line in the beginning of the file to specify which interpreter to run:

#!/net/some.server/path/to/ruby

It worked well for a lot of situations, but it didn't work for me: my development machine has the i386 architecture and it is running Debian GNU/Linux, but some.server was a Solaris SPARC server, and ruby is a binary executable. It was possible to work around the problem by various ugly methods, such as hardcoding the paths of the interpreter in the web service and choosing the correct one based on the host, changing the shebangs so that they pointed to the link to ruby which should be present on all the servers which need to run the scripts, etc. But after some time it occurred to me that I could simply use JRuby in the web service and thus not risk making problems to other users of the scripts.

First I thought of using the Bean Shell Framework (because of some GlassFish on JDK6 problems I can't use JDK6 and thus its scripting support), but I needed to get the data from the standard output of the script and this wasn't possible to do from the BSF. Then I looked at the Main class of JRuby and found that it was just the thing which I needed: it is possible to pass which Streams to use as stdin, stdout and stderr. I made some really small changes to the web service to make it call JRuby engine instead of creating the external process and it worked! I really feel the benefit of using the JRuby in such situation: running in heterogeneous environment is what Java is suited for, and hence what JRuby is suited for it too.