Terracotta for JRuby
Overview
This project provides integration between Open Terracotta and JRuby
.
There are three ways this integration can be made:
- Create a JRuby API that makes use of Open Terracotta.
- Transparently hook into the JRuby interpreter.
- 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.