STANDING ON
                    THE SHOULDERS
                      OF GIANTS
                      WITH JRUBY


måndag 19 mars 12
Theo
                    @iconara
måndag 19 mars 12
Chief Architect at




måndag 19 mars 12
måndag 19 mars 12
ruby




måndag 19 mars 12
TL;DR
              Most of you are Java developers
              Writing Java is tedious
              There’s truckloads of great Java libraries
              The JVM is awesome
              Ruby is awesome
              ∴ JRuby FTW

måndag 19 mars 12
just


                     I’M NOT HERE TO
                    MAKE FUN OF JAVA



måndag 19 mars 12
JRuby vs. vanilla Ruby
              Real threads
              A working GC
              Every Java, Scala and Ruby library ever made
              All the JVM awesomeness, JIT, the lot




måndag 19 mars 12
JRuby vs. Java
              It doesn’t make you want to stab yourself




måndag 19 mars 12
måndag 19 mars 12
JRUBY ♥ JAVA
                    A WHIRLWIND TOUR



måndag 19 mars 12
JRuby ♥ Java
              stuff = TreeMap.new
              stuff['windmill'] = 'rocket'
              stuff['pirate'] = 'bees'
              stuff.each do |something|
                # redacted
              end




måndag 19 mars 12
JRuby ♥ Java
              require 'java'
              require 'rabbitmq-client.jar'

              import 'com.rabbitmq.client.ConnectionFactory'

              factory = ConnectionFactory.new()
              factory.setUri('amqp://localhost:5672/')
              connection = factory.newConnection()




måndag 19 mars 12
JRuby ♥ Java
              require 'java'
              require 'rabbitmq-client.jar'

              import 'com.rabbitmq.client.ConnectionFactory'

              factory = ConnectionFactory.new
              factory.uri = 'amqp://localhost:5672/'
              connection = factory.new_connection




måndag 19 mars 12
JRuby ♥ Java
              class Worker < Thread
                def run
                  puts 'Hard work, no play'
                end
              end

              # or

              class Worker
                include Runnable

                def run
                  puts 'Hard work, no play'
                end
              end




måndag 19 mars 12
JRuby ♥ Java
              pool = Executors.new_fixed_thread_pool(3)

              memes = LinkedBlockingQueue.new

              10.times do
                pool.submit do
                  open('http://api.autome.me/text').read.each_line do |meme|
                    memes << meme
                  end
                end
              end

              pool.shutdown
              pool.await_termination(3, TimeUnit::DAYS)

              memes.each { |m| puts(m) }




måndag 19 mars 12
JRUBY FOR
                    REPRESSED JAVA
                     DEVELOPERS


måndag 19 mars 12
Made up fact
              84% of Java devs don’t write tests because it’s
              way too much extra code to type.




måndag 19 mars 12
Testing
              public class Person {
                public final String firstName;
                public final String lastName;

                    public Person(String firstName, String lastName) {
                      this.firstName = firstName;
                      this.lastName = lastName;
                    }

                    public String getFullName() {
                      return String.format("%s %s", firstName, lastName);
                    }

                    // omg I’m already bored
              }




måndag 19 mars 12
Testing
              class TestPerson < Test::Unit::TestCase
                def setup
                  @person = Person.new('James', 'Gosling')
                end

                def test_full_name
                  assert_equal('James Gosling', @person.full_name)
                end
              end

              # this is TestUnit, it’s part of the stdlib




måndag 19 mars 12
Testing
              describe Person do
                describe '#full_name' do
                  before do
                    @person = Person.new('James', 'Gosling')
                  end

                  it 'is the first name and the last name with a space in-between' do
                    @person.full_name.should == 'James Gosling'
                  end
                end
              end

              # this is RSpec, read more at rspec.info




måndag 19 mars 12
Made up fact
              Ant was designed by the same people who
              came up with the QWERTY layout.




måndag 19 mars 12
Automation
               <?xml version="1.0"?>
               <project name="MyProject" default="dist" basedir=".">
                   <description>
                       simple example build file
                   </description>
                   <!-- set global properties for this build -->
                   <property name="src" location="src"/>
                   <property name="build" location="build"/>
                   <property name="dist" location="dist"/>
                   <target name="init">
                       <!-- Create the time stamp -->
                       <tstamp/>
                       <!-- Create the build directory structure used by compile -->
                       <mkdir dir="${build}"/>
                   </target>
                   <target name="compile" depends="init" description="compile the source ">
                       <!-- Compile the java code from ${src} into ${build} -->
                       <javac srcdir="${src}" destdir="${build}"/>
                   </target>
                   <target name="dist" depends="compile" description="generate the distribution">
                       <!-- Create the distribution directory -->
                       <mkdir dir="${dist}/lib"/>
                       <!-- And don’t get me started on Maven, $%&@*! -->
                       <!-- Put everything in ${build} into the MyProject-${DSTAMP}.jar file -->
måndag 19 mars 12
Automation
           require 'ant'

           src_dir = 'src'
           build_dir = 'build'
           dist_dir = 'dist'
           timestamp = Time.now

           task :init do
             mkdir_p build_dir
           end

           task :compile => :init do
             ant.javac :srcdir => src_dir, :destdir => build_dir
           end

           task :dist => :compile do
             mkdir_p "#{dist_dir}/lib"
             ant.jar :jarfile => "#{dist_dir}/lib/MyProject-#{timestamp}.jar",
                     :basedir => build_dir
           end

               task :clean do
                  rm_rf build_dir
                  rm_rf dist_dir
måndag 19 mars 12
Made up fact
              The average number of lines in a Java web
              application is somewhere around 100 000




måndag 19 mars 12
Don’t make it so hard
              get '/' do
                erb :index
              end

              post '/register' do
                stuff.save(params[:name], params[:email], params[:shoe_size])
                redirect '/thanks'
              end

              get '/thanks' do
                @name = params[:name]
                erb :thanks
              end

              # this is Sinatra, read more at sinatrarb.com




måndag 19 mars 12
Pack it up in a
              WAR and ship it
              $ warbler war

              # warbler can be found at github.com/jruby/warbler
              # also check out torquebox.org, and github.com/trinidad/trinidad




måndag 19 mars 12
Make a console
              $ hbase shell

              HBase Shell; enter 'help<RETURN>' for list of supported commands.
              Type "exit<RETURN>" to leave the HBase Shell
              Version 0.90.4-cdh3u2, r, Thu Oct 13 20:32:26 PDT 2011

              hbase(main):001:0>   create 'test', 'cf'
              0 row(s) in 1.2200   seconds
              hbase(main):002:0>   put 'test', 'row1', 'cf:a', 'value1'
              0 row(s) in 0.0560   seconds
              hbase(main):003:0>   put 'test', 'row2', 'cf:b', 'value2'
              0 row(s) in 0.0370   seconds




måndag 19 mars 12
Monitoring
              require 'jmx'

              client = JMX.connect(:port => 7199)

              storage_service = client['org.apache.cassandra.db:type=StorageService']
              storage_service.keyspaces.each do |keyspace|
                puts keyspace
              end

              memory_mbean = client['java.lang:type=Memory']
              puts memory_mbean.heap_memory_usage.used

              memory_mbean.gc




måndag 19 mars 12
Configuration
              conf = configuration do
                base_keys :api_key, :date

                    dimension   :path
                    dimension   :section
                    dimension   :country
                    dimension   :section, :country

                metric :pageviews
                metric :reach, :user_id, :type => :unique
                metric :clicks, :click?, :type => :predicate
              end

              counters = conf.build!




måndag 19 mars 12
SNEAK SOME RUBY INTO THAT
                      ENTERPRISE APPLICATION



måndag 19 mars 12
STANDING ON
                    THE SHOULDERS
                      OF GIANTS
                      WITH JRUBY


måndag 19 mars 12
LET’S STACK SOME
                     ABSTRACTIONS ON
                    TOP OF THEM THERE
                      ABSTRACTIONS


måndag 19 mars 12
Our stack: RabbitMQ
              We use the Java driver, with a JRuby interface
              we call HotBunnies
              github.com/ruby-amqp/hot_bunnies




måndag 19 mars 12
Our stack: RabbitMQ
              require 'hot_bunnies'

              connection = HotBunnies.connect(:host => 'localhost')
              channel = connection.create_channel
              queue = channel.queue('test_queue')
              queue.bind('test_exch', :routing_key => 'hi')

              subscription = queue.subscribe(:ack => true, :blocking => false) do |headers, msg|
                # do awesome stuff
              end




måndag 19 mars 12
Our stack: MongoDB
              Go listen to David’s talk tomorrow
              We use the Ruby driver, because we wouldn’t
              get anything done otherwise




måndag 19 mars 12
Our stack: MongoDB
              BasicDBObject doc = new BasicDBObject();
              doc.put("name", "MongoDB");
              doc.put("type", "database");
              doc.put("count", 1);

              BasicDBObject info = new BasicDBObject();
              info.put("x", 203);
              info.put("y", 102);

              doc.put("info", info);

              coll.insert(doc);

              vs.

              coll.insert(
                'name' => 'MongoDB',
                'type' => 'database',
                'count' => 1,
                'info' => {'x' => 203, 'y' => 102}
              )


måndag 19 mars 12
Our stack: Cassandra
              We use a JRuby wrapper on top of Pelops
              github.com/iconara/eurydice




måndag 19 mars 12
Our stack: Cassandra
              Mutator mutator = Pelops.createMutator(pool);
              Column nameColumn = mutator.newColumn("name", "Dan");
              Column ageColumn = mutator.newColumn("age", Bytes.fromInt(33));
              List<Column> columns = mutator.newColumnList(nameColumn, ageColumn);
              mutator.writeColumns(columnFamily, rowKey, columns);
              mutator.execute(ConsistencyLevel.ONE);



              vs.



              columns = {'name' => 'Dan', 'age' => 33}
              column_family.update(row_key, columns, :consistency_level => :one)




måndag 19 mars 12
Our stack: Akka
              Scala is nice, but we’ve got better things to do
              than to wait for code to compile
              Akka is awesome, so we created Mikka
              github.com/iconara/mikka




måndag 19 mars 12
Our stack: Akka
              class Ping < Mikka::Actor
                def pre_start
                  @pong = context.actor_of(Pong)
                  @pong << :ping
                end

                def receive(message)
                  context.reply(:ping)
                end
              end

              class Pong < Mikka::Actor
                def receive(message)
                  context.reply(:pong)
                end
              end

              ping = Mikka.actor_of(Ping).start




måndag 19 mars 12
Our stack: numbers
              We process hundreds of millions of messages
              per day, using less than 20K lines of JRuby




måndag 19 mars 12
twitter.com/iconara
                    architecturalatrocities.com
                           burtcorp.com



måndag 19 mars 12

Standing on the shoulders of giants with JRuby

  • 1.
    STANDING ON THE SHOULDERS OF GIANTS WITH JRUBY måndag 19 mars 12
  • 2.
    Theo @iconara måndag 19 mars 12
  • 3.
  • 4.
  • 5.
  • 6.
    TL;DR Most of you are Java developers Writing Java is tedious There’s truckloads of great Java libraries The JVM is awesome Ruby is awesome ∴ JRuby FTW måndag 19 mars 12
  • 7.
    just I’M NOT HERE TO MAKE FUN OF JAVA måndag 19 mars 12
  • 8.
    JRuby vs. vanillaRuby Real threads A working GC Every Java, Scala and Ruby library ever made All the JVM awesomeness, JIT, the lot måndag 19 mars 12
  • 9.
    JRuby vs. Java It doesn’t make you want to stab yourself måndag 19 mars 12
  • 10.
  • 11.
    JRUBY ♥ JAVA A WHIRLWIND TOUR måndag 19 mars 12
  • 12.
    JRuby ♥ Java stuff = TreeMap.new stuff['windmill'] = 'rocket' stuff['pirate'] = 'bees' stuff.each do |something| # redacted end måndag 19 mars 12
  • 13.
    JRuby ♥ Java require 'java' require 'rabbitmq-client.jar' import 'com.rabbitmq.client.ConnectionFactory' factory = ConnectionFactory.new() factory.setUri('amqp://localhost:5672/') connection = factory.newConnection() måndag 19 mars 12
  • 14.
    JRuby ♥ Java require 'java' require 'rabbitmq-client.jar' import 'com.rabbitmq.client.ConnectionFactory' factory = ConnectionFactory.new factory.uri = 'amqp://localhost:5672/' connection = factory.new_connection måndag 19 mars 12
  • 15.
    JRuby ♥ Java class Worker < Thread def run puts 'Hard work, no play' end end # or class Worker include Runnable def run puts 'Hard work, no play' end end måndag 19 mars 12
  • 16.
    JRuby ♥ Java pool = Executors.new_fixed_thread_pool(3) memes = LinkedBlockingQueue.new 10.times do pool.submit do open('http://api.autome.me/text').read.each_line do |meme| memes << meme end end end pool.shutdown pool.await_termination(3, TimeUnit::DAYS) memes.each { |m| puts(m) } måndag 19 mars 12
  • 17.
    JRUBY FOR REPRESSED JAVA DEVELOPERS måndag 19 mars 12
  • 18.
    Made up fact 84% of Java devs don’t write tests because it’s way too much extra code to type. måndag 19 mars 12
  • 19.
    Testing public class Person { public final String firstName; public final String lastName; public Person(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public String getFullName() { return String.format("%s %s", firstName, lastName); } // omg I’m already bored } måndag 19 mars 12
  • 20.
    Testing class TestPerson < Test::Unit::TestCase def setup @person = Person.new('James', 'Gosling') end def test_full_name assert_equal('James Gosling', @person.full_name) end end # this is TestUnit, it’s part of the stdlib måndag 19 mars 12
  • 21.
    Testing describe Person do describe '#full_name' do before do @person = Person.new('James', 'Gosling') end it 'is the first name and the last name with a space in-between' do @person.full_name.should == 'James Gosling' end end end # this is RSpec, read more at rspec.info måndag 19 mars 12
  • 22.
    Made up fact Ant was designed by the same people who came up with the QWERTY layout. måndag 19 mars 12
  • 23.
    Automation <?xml version="1.0"?> <project name="MyProject" default="dist" basedir="."> <description> simple example build file </description> <!-- set global properties for this build --> <property name="src" location="src"/> <property name="build" location="build"/> <property name="dist" location="dist"/> <target name="init"> <!-- Create the time stamp --> <tstamp/> <!-- Create the build directory structure used by compile --> <mkdir dir="${build}"/> </target> <target name="compile" depends="init" description="compile the source "> <!-- Compile the java code from ${src} into ${build} --> <javac srcdir="${src}" destdir="${build}"/> </target> <target name="dist" depends="compile" description="generate the distribution"> <!-- Create the distribution directory --> <mkdir dir="${dist}/lib"/> <!-- And don’t get me started on Maven, $%&@*! --> <!-- Put everything in ${build} into the MyProject-${DSTAMP}.jar file --> måndag 19 mars 12
  • 24.
    Automation require 'ant' src_dir = 'src' build_dir = 'build' dist_dir = 'dist' timestamp = Time.now task :init do mkdir_p build_dir end task :compile => :init do ant.javac :srcdir => src_dir, :destdir => build_dir end task :dist => :compile do mkdir_p "#{dist_dir}/lib" ant.jar :jarfile => "#{dist_dir}/lib/MyProject-#{timestamp}.jar", :basedir => build_dir end task :clean do rm_rf build_dir rm_rf dist_dir måndag 19 mars 12
  • 25.
    Made up fact The average number of lines in a Java web application is somewhere around 100 000 måndag 19 mars 12
  • 26.
    Don’t make itso hard get '/' do erb :index end post '/register' do stuff.save(params[:name], params[:email], params[:shoe_size]) redirect '/thanks' end get '/thanks' do @name = params[:name] erb :thanks end # this is Sinatra, read more at sinatrarb.com måndag 19 mars 12
  • 27.
    Pack it upin a WAR and ship it $ warbler war # warbler can be found at github.com/jruby/warbler # also check out torquebox.org, and github.com/trinidad/trinidad måndag 19 mars 12
  • 28.
    Make a console $ hbase shell HBase Shell; enter 'help<RETURN>' for list of supported commands. Type "exit<RETURN>" to leave the HBase Shell Version 0.90.4-cdh3u2, r, Thu Oct 13 20:32:26 PDT 2011 hbase(main):001:0> create 'test', 'cf' 0 row(s) in 1.2200 seconds hbase(main):002:0> put 'test', 'row1', 'cf:a', 'value1' 0 row(s) in 0.0560 seconds hbase(main):003:0> put 'test', 'row2', 'cf:b', 'value2' 0 row(s) in 0.0370 seconds måndag 19 mars 12
  • 29.
    Monitoring require 'jmx' client = JMX.connect(:port => 7199) storage_service = client['org.apache.cassandra.db:type=StorageService'] storage_service.keyspaces.each do |keyspace| puts keyspace end memory_mbean = client['java.lang:type=Memory'] puts memory_mbean.heap_memory_usage.used memory_mbean.gc måndag 19 mars 12
  • 30.
    Configuration conf = configuration do base_keys :api_key, :date dimension :path dimension :section dimension :country dimension :section, :country metric :pageviews metric :reach, :user_id, :type => :unique metric :clicks, :click?, :type => :predicate end counters = conf.build! måndag 19 mars 12
  • 31.
    SNEAK SOME RUBYINTO THAT ENTERPRISE APPLICATION måndag 19 mars 12
  • 32.
    STANDING ON THE SHOULDERS OF GIANTS WITH JRUBY måndag 19 mars 12
  • 33.
    LET’S STACK SOME ABSTRACTIONS ON TOP OF THEM THERE ABSTRACTIONS måndag 19 mars 12
  • 34.
    Our stack: RabbitMQ We use the Java driver, with a JRuby interface we call HotBunnies github.com/ruby-amqp/hot_bunnies måndag 19 mars 12
  • 35.
    Our stack: RabbitMQ require 'hot_bunnies' connection = HotBunnies.connect(:host => 'localhost') channel = connection.create_channel queue = channel.queue('test_queue') queue.bind('test_exch', :routing_key => 'hi') subscription = queue.subscribe(:ack => true, :blocking => false) do |headers, msg| # do awesome stuff end måndag 19 mars 12
  • 36.
    Our stack: MongoDB Go listen to David’s talk tomorrow We use the Ruby driver, because we wouldn’t get anything done otherwise måndag 19 mars 12
  • 37.
    Our stack: MongoDB BasicDBObject doc = new BasicDBObject(); doc.put("name", "MongoDB"); doc.put("type", "database"); doc.put("count", 1); BasicDBObject info = new BasicDBObject(); info.put("x", 203); info.put("y", 102); doc.put("info", info); coll.insert(doc); vs. coll.insert( 'name' => 'MongoDB', 'type' => 'database', 'count' => 1, 'info' => {'x' => 203, 'y' => 102} ) måndag 19 mars 12
  • 38.
    Our stack: Cassandra We use a JRuby wrapper on top of Pelops github.com/iconara/eurydice måndag 19 mars 12
  • 39.
    Our stack: Cassandra Mutator mutator = Pelops.createMutator(pool); Column nameColumn = mutator.newColumn("name", "Dan"); Column ageColumn = mutator.newColumn("age", Bytes.fromInt(33)); List<Column> columns = mutator.newColumnList(nameColumn, ageColumn); mutator.writeColumns(columnFamily, rowKey, columns); mutator.execute(ConsistencyLevel.ONE); vs. columns = {'name' => 'Dan', 'age' => 33} column_family.update(row_key, columns, :consistency_level => :one) måndag 19 mars 12
  • 40.
    Our stack: Akka Scala is nice, but we’ve got better things to do than to wait for code to compile Akka is awesome, so we created Mikka github.com/iconara/mikka måndag 19 mars 12
  • 41.
    Our stack: Akka class Ping < Mikka::Actor def pre_start @pong = context.actor_of(Pong) @pong << :ping end def receive(message) context.reply(:ping) end end class Pong < Mikka::Actor def receive(message) context.reply(:pong) end end ping = Mikka.actor_of(Ping).start måndag 19 mars 12
  • 42.
    Our stack: numbers We process hundreds of millions of messages per day, using less than 20K lines of JRuby måndag 19 mars 12
  • 43.
    twitter.com/iconara architecturalatrocities.com burtcorp.com måndag 19 mars 12