Dashboard > Terracotta Forge Labs > Home > Terracotta for JRuby
  Terracotta Forge Labs Log In   View a printable version of the current page.  
  Terracotta for JRuby
Added by Jonas Boner, last edited by Jonas Boner on Feb 08, 2007  (view change)
Labels: 
(None)

Terracotta for JRuby

Overview

This project provides integration between Open Terracotta and JRuby.

There are three ways this integration can be made:

  1. Create a JRuby API that makes use of Open Terracotta.
  2. Transparently hook into the JRuby interpreter.
  3. Transparently weave the classes that are generated by the JRuby compliler.

So far this project have only implemented #1 of the three solutions. We are currently looking into #2. #3, which is probably is the best alternative, has to wait until JRuby has a more feature complete compiler (the development of the JRuby compiler has just started).

Both #2 and #3 requires an extension (as well as extension points) of the Terracotta pattern matching language (the language that is used to pick out the shared state and the methods modifying it) in order to support the Ruby syntax. This would mean allowing the user to define the patterns based on the Ruby language but then, under the hood, actually map the Ruby syntax to the real Java classes that are generated by JRuby (the latter assuming using the compiler and not the interpreter). The benefit here is that it would be "transparent", in the same way as it is for regular Java applications.

If you want to help out, contribute code, ideas, docs or whatever. Subscribe to the mailing list (found on this page and send us an email with your ideas.

Implementation

Here is the Terracotta module providing the API for JRuby users to use:

# Usage:
#
# # create an instance of 'foo' as a DSO root this
# # means that it will be shared across the cluster
# foo = DSO.createRoot "foo", Foo.new
#
# # update 'foo' in a guarded block, get result back
# result = DSO.guard foo, DSO::CONCURRENT_LOCK do
#   foo.add bar
#   foo.getSum
# end

TC = com.tc.object.bytecode.ManagerUtil

module DSO

  # The different lock types
  WRITE_LOCK = com.tc.object.bytecode.Manager::LOCK_TYPE_WRITE
  READ_LOCK = com.tc.object.bytecode.Manager::LOCK_TYPE_READ
  CONCURRENT_LOCK = com.tc.object.bytecode.Manager::LOCK_TYPE_CONCURRENT

  # Creates a Terracotta shared root, 'name' is the name of the root
  # (can be anything that uniquily defines the root), 'object' is an
  # instance of the object to be shared. If the root the given name
  # already exists it simply returns it.
  def DSO.createRoot(name, object)
    guardWithNamedLock name, WRITE_LOCK do
      TC.lookupOrCreateRoot name, object
    end
  end

  # Creates a transaction and guards the object (passed in as the
  # 'object' argument) during the execution of the block passed into
  # the method. Similar to Java's synchronized(object) {...} blocks.
  # Garantuees that the critical section is maintained correctly across
  # the cluster. The type of the lock can be one of: DSO:WRITE_LOCK,
  # DSO::READ_LOCK or DSO::CONCURRENT_LOCK (default is DSO:WRITE_LOCK).
  def DSO.guard(object, type = WRITE_LOCK)
    TC.monitorEnter object, type
    begin
      yield
    ensure
      TC.monitorExit object
    end
  end

  # Creates a transaction and guards the critical section using a virtual
  # so called 'named lock. It is held during the execution of the block
  # passed into the method. Garantuees that the critical section is
  # maintained correctly across the cluster. The type of the lock can
  # be one of: DSO:WRITE_LOCK, DSO::READ_LOCK or DSO::CONCURRENT_LOCK
  # (default is DSO:WRITE_LOCK)8.
  def DSO.guardWithNamedLock(name, type = WRITE_LOCK)
    TC.beginLock name, type
    begin
      yield
    ensure
      TC.commitLock name
    end
  end

  # Dispatches a Distributed Method Call (DMI). Ensures that the
  # particular method will be invoked on all nodes in the cluster.
  def DSO.dmi(object, methodName, arguments)
    TC.distributedMethodCall object, methodName, arguments
  end
end

Usage

Copy this code in the section above and paste it into a JRuby file, f.e. call it 'terracotta.rb'. Then import the 'terracotta.rb' module into your application. Make sure that you have the Terracotta jar 'tc.jar' on the classpath.

Then you can use it like this:

# create a Terracotta root
@messages = DSO.createRoot java.util.ArrayList.new

# update the shared root in a transaction
DSO.guard @messages do
  @messages.add msg
end

For example here is an example that shows a very simple Chat application that can be used by multiple users, all on different machines:

require 'java'
load 'terracotta.rb'

class Chatter
  def initialize
    @name = ARGV[0]
    @messages = DSO.createRoot "chatter", java.util.ArrayList.new
    puts "— Hi #{@name}. Welcome to Chatter. Press Enter to refresh —"
  end

  def run
    while true do
      print "Enter Text>>"
      text = STDIN.gets.chomp
      if text.length > 0 then
        DSO.guard @messages do
          @messages.add "[#{Time.now} — #{@name}] #{text}"
          puts @messages
        end
      else
        puts @messages
     end
    end
  end
end

Chatter.new.run

For more details on how to use it (enabling Open Terracotta, samples etc.) read this blog post: http://jonasboner.com/2007/02/05/clustering-jruby-with-open-terracotta/

Happy hacking.

Powered by Atlassian Confluence, the Enterprise Wiki. (Version: 2.5.5 Build:#811 Jul 25, 2007) - Bug/feature request - Contact Administrators