Your SlideShare is downloading. ×
0
JRuby
Insights from Six Years in Production
Mark Menard	

Enable Labs
How do you get Ruby into a Java shop?

How do you move past Java to Ruby?

How do you get Ruby into a legacy Java app?

En...
Who are you?
Enable Labs

!3

@mark_menard
You love Ruby!

Enable Labs

!4

@mark_menard
You work in a Java shop.
Enable Labs

!5

@mark_menard
You have a client who
uses Java.

Enable Labs

!6

@mark_menard
You have to
work on
something
that uses Java.
Enable Labs

!7

@mark_menard
You have a
project that
could benefit
from true
concurrency.
Enable Labs

!8

@mark_menard
Why?

Enable Labs

!9

@mark_menard
So... what is JRuby anyway?

Enable Labs

!10

@mark_menard
But I use MRI!
What’s different?
Enable Labs

!11

@mark_menard
To Summarize
•
•
•
•
•
•
Enable Labs

A JVM is required.	

Start up is a little slower.	

Prefer pure Ruby gems, or gems t...
What can JRuby do for you?

Enable Labs

!13

@mark_menard
Parallelism in JRuby

Enable Labs

!14

@mark_menard
my_thread = Thread.new do
(1..100_000).each { |i| i * 2 }
end
!

# Do some more work.
!

my_thread.join # Wait for my_thre...
require 'peach'
require 'benchmark'
!

overall_results = Benchmark.measure do
(1..100_000).peach(8) do |i|
1000.times do |...
$ rvm use 2.0
Using /Users/mark/.rvm/gems/ruby-2.0.0-p247
$ ruby peach_example.rb
41.250000 0.060000 41.310000 ( 41.302095...
Using Java Libraries

Enable Labs

!18

@mark_menard
require 'java'
require "#{File.expand_path(File.dirname(__FILE__))}/itextpdf-5.4.4.jar"
!
# Make Java classes top level co...
require 'itext'
Itext::Document.create("document.pdf") do
p "Hello from JRuby"
end

Enable Labs

!20

@mark_menard
require 'java'
require "#{File.expand_path(File.dirname(__FILE__))}/itextpdf-5.4.4.jar"
!

module Itext
# Namespace the Ja...
module Itext
# Re-open the *Java* Document class.
class Document
def self.create (filename, &block)
document = self.new
pd...
require 'itext'
Itext::Document.create("document.pdf") do
p "Hello from JRuby"
end

Enable Labs

!23

@mark_menard
require 'java'
!

frame = javax.swing.JFrame.new
frame.set_default_close_operation javax.swing.JFrame::EXIT_ON_CLOSE
frame...
Case Studies

Enable Labs

!25

@mark_menard
Case Study 1
!

What the Client
Wanted
!

1Q2008

Enable Labs

• New functionality that was
predominantly orthogonal
to th...
• Struts 2
• Groovy 1.0
• Jetty Running Un-war'ed
• Spring Dependency Injection XML Hell
• Struts 2 - More XML Hell

Case ...
Case Study 1
!

What We Used

• JRuby 1.0
• Rails 2.1
• ERB
Enable Labs

!28

@mark_menard
Case Study 1
!

What Made it
Work

Java Integration

Enable Labs

!29

@mark_menard
Case Study 1
!

The Challenges

Enable Labs

• Integrating the Signin Process
• Accessing the Spring Context
• Reusing Exi...
• Initiate all signins on the Rails
side of the application.
• On success setup the HTTP
session for both the Java and
Rai...
• Create a Java object that holds a

Case Study 1
!

Accessing the
Spring Context

Enable Labs

static reference to the Sp...
public class SpringApplicationContextFinder implements ApplicationContextAware {

!
!

!

}

private static ApplicationCon...
module SpringSupport
def get_spring_context
@context ||= Java::lib.SpringApplicationContextFinder.getContext()
end
end

En...
class SomeObject
include SpringSupport
!

spring_dependency :some_spring_service
!

def do_something (arg)
!

#@some_sprin...
• Permission system written in

Case Study 1
!

Reusing Existing
Permission
System

Enable Labs

Java/Groovy.
• Still need...
• Use the Spring implementation

Case Study 1
!

Reusing Existing
Permission
System

of permissions.
• Check permissions i...
class CheckSecurityAccessService < Struct.new(:url, :user)
include SpringSupport
spring_dependency :security_manager
!

de...
Case Study 1
!

Deployment and
Gem Management

Enable Labs

• App used Jetty un-war’ed.
• Warbler didn’t apply.
• Layout R...
• Completed work in about 3

Case Study 1
!

Success

Enable Labs

months.
• Much better test coverage.
• This module is s...
• Minimal integration with existing
application.
• Highly compartmentalized.
• Focused feature set with stable
requirement...
Case Study 2

And Steve said, “let
there be iPhone.”

Enable Labs

!42

@mark_menard
• Client wants about 10 screens
available in the browser on the
iPhone.
• He has a trip in two weeks and
wants it working ...
• Working screens inside of a week.
• Ready for his trip in two weeks.
• Went on to be used by the field sales staff for se...
Case Study 3
!

Porting to Ruby
First Attempt
!

A Study in Over
Enthusiasm

This JRuby is Awesome!
Let’s Port the App!
En...
Let’s talk
about the
brownfield.

• 388 Views
• 272 Struts 2 Actions
• 30 Spring Service Beans
• 81 Data Access Objects
• ...
Case Study 3
Current
Architecture

Enable Labs

!47

@mark_menard
• Ruby classes can implement Java
interfaces.

Case Study 3
!

Porting to Ruby
First Attempt
Enable Labs

!48

@mark_menar...
public interface Person {
public String getName ();
public void setName (String name);
}
class Person
include Java::Person...
• Ruby classes can implement Java
interfaces.
• Plug a Ruby/Rails environment
into Spring to manufacture Ruby
“beans”.

Ca...
def getInventoryManager () {
log.debug "[ RailsFactory.groovy ] : Instantiating Integration::InventoryManager"
eval "Integ...
• Ruby classes can implement Java

Case Study 3
!

Porting to Ruby
First Attempt
Enable Labs

interfaces.
• Plug a Ruby/Ra...
Rationale
Case Study 3
!

Porting to Ruby
First Attempt

Enable Labs

• No need to mess with the user
experience. The view...
Outcome
• Technical success, business failure.
• Did not take full advantage of our
Ruby tools.
• There was no driving bus...
Case Study 4
!

Porting to Rails
Part 2

Ah.... sweet incremental
success... ...mostly.

Enable Labs

!55

@mark_menard
• Do all new work in JRuby.
• Find silos of existing functionality.
• Wait for significant changes in
requirements for the...
Case Study 4
!

Porting to Rails
Part 2

The God Object in the Closet
Enable Labs

!57

@mark_menard
The God Object in the Closet

The Strategy
!

Make the God object a web service.
Implement it in Rails.
Translate the exis...
JRuby works today.
Java integration lets you
play where MRI just can’t go.
It’s just Ruby, with Java
JVM super powers!
Ena...
Photo credits
http://www.flickr.com/photos/usfwsnortheast/5655240564/
http://www.flickr.com/photos/falcon1961/3304306800/
...
Enable Labs
@mark_menard
http://www.enablelabs.com/
info@enablelabs.com
866-895-8189
Upcoming SlideShare
Loading in...5
×

JRuby 6 Years in Production

985

Published on

Insight from using JRuby in production for six years integrating with Java services, such as Spring.

Published in: Technology, Education
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
985
On Slideshare
0
From Embeds
0
Number of Embeds
3
Actions
Shares
0
Downloads
8
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Transcript of "JRuby 6 Years in Production"

  1. 1. JRuby Insights from Six Years in Production Mark Menard Enable Labs
  2. 2. How do you get Ruby into a Java shop? How do you move past Java to Ruby? How do you get Ruby into a legacy Java app? Enable Labs !2 @mark_menard
  3. 3. Who are you? Enable Labs !3 @mark_menard
  4. 4. You love Ruby! Enable Labs !4 @mark_menard
  5. 5. You work in a Java shop. Enable Labs !5 @mark_menard
  6. 6. You have a client who uses Java. Enable Labs !6 @mark_menard
  7. 7. You have to work on something that uses Java. Enable Labs !7 @mark_menard
  8. 8. You have a project that could benefit from true concurrency. Enable Labs !8 @mark_menard
  9. 9. Why? Enable Labs !9 @mark_menard
  10. 10. So... what is JRuby anyway? Enable Labs !10 @mark_menard
  11. 11. But I use MRI! What’s different? Enable Labs !11 @mark_menard
  12. 12. To Summarize • • • • • • Enable Labs A JVM is required. Start up is a little slower. Prefer pure Ruby gems, or gems that have JRuby versions. Try to avoid gems with C-extensions. No continuations. No fork() Regular expressions use 1.9 semantics even in 1.8 mode. • !12 @mark_menard
  13. 13. What can JRuby do for you? Enable Labs !13 @mark_menard
  14. 14. Parallelism in JRuby Enable Labs !14 @mark_menard
  15. 15. my_thread = Thread.new do (1..100_000).each { |i| i * 2 } end ! # Do some more work. ! my_thread.join # Wait for my_thread to finish. Enable Labs !15 @mark_menard
  16. 16. require 'peach' require 'benchmark' ! overall_results = Benchmark.measure do (1..100_000).peach(8) do |i| 1000.times do |n| a, b = 0, 1 a, b = b, a+b end end end puts overall_results Enable Labs !16 @mark_menard
  17. 17. $ rvm use 2.0 Using /Users/mark/.rvm/gems/ruby-2.0.0-p247 $ ruby peach_example.rb 41.250000 0.060000 41.310000 ( 41.302095) $ rvm use jruby Using /Users/mark/.rvm/gems/jruby-1.7.5 $ ruby peach_example.rb 9.960000 0.170000 10.130000 ( 1.437000) Enable Labs !17 @mark_menard
  18. 18. Using Java Libraries Enable Labs !18 @mark_menard
  19. 19. require 'java' require "#{File.expand_path(File.dirname(__FILE__))}/itextpdf-5.4.4.jar" ! # Make Java classes top level constants. java_import com.itextpdf.text.Document Document.__persistent__ = true java_import com.itextpdf.text.pdf.PdfWriter java_import java.io.FileOutputStream java_import com.itextpdf.text.Paragraph document = Document.new pdfWriter = PdfWriter.get_instance(document, FileOutputStream.new("document.pdf")) document.open document.add(Paragraph.new("Hello JRuby")) document.close Enable Labs !19 @mark_menard
  20. 20. require 'itext' Itext::Document.create("document.pdf") do p "Hello from JRuby" end Enable Labs !20 @mark_menard
  21. 21. require 'java' require "#{File.expand_path(File.dirname(__FILE__))}/itextpdf-5.4.4.jar" ! module Itext # Namespace the Java classes for convenience. java_import com.itextpdf.text.Document Document.__persistent__ = true java_import com.itextpdf.text.pdf.PdfWriter java_import java.io.FileOutputStream java_import com.itextpdf.text.Paragraph end Enable Labs !21 @mark_menard
  22. 22. module Itext # Re-open the *Java* Document class. class Document def self.create (filename, &block) document = self.new pdf_writer = PdfWriter.get_instance(document, FileOutputStream.new(filename)) document.open document.instance_eval(&block) document.close end ! def paragraph (content) add(Paragraph.new(content)) end alias_method :p, :paragraph end end Enable Labs !22 @mark_menard
  23. 23. require 'itext' Itext::Document.create("document.pdf") do p "Hello from JRuby" end Enable Labs !23 @mark_menard
  24. 24. require 'java' ! frame = javax.swing.JFrame.new frame.set_default_close_operation javax.swing.JFrame::EXIT_ON_CLOSE frame.set_size(300, 200) frame.get_content_pane.add javax.swing.JLabel.new('Hello world!') frame.set_visible true Enable Labs !24 @mark_menard
  25. 25. Case Studies Enable Labs !25 @mark_menard
  26. 26. Case Study 1 ! What the Client Wanted ! 1Q2008 Enable Labs • New functionality that was predominantly orthogonal to their existing app. • Single Signon • Faster Development Times • Some integration at the data level. !26 @mark_menard
  27. 27. • Struts 2 • Groovy 1.0 • Jetty Running Un-war'ed • Spring Dependency Injection XML Hell • Struts 2 - More XML Hell Case Study 1 ! The Technical Environment ! • Mostly Continuous Deployment Enable Labs !27 @mark_menard
  28. 28. Case Study 1 ! What We Used • JRuby 1.0 • Rails 2.1 • ERB Enable Labs !28 @mark_menard
  29. 29. Case Study 1 ! What Made it Work Java Integration Enable Labs !29 @mark_menard
  30. 30. Case Study 1 ! The Challenges Enable Labs • Integrating the Signin Process • Accessing the Spring Context • Reusing Existing Permission System • Deployment • Gem Management !30 @mark_menard
  31. 31. • Initiate all signins on the Rails side of the application. • On success setup the HTTP session for both the Java and Rails sides of the app. • Also handle signout in Rails. Enable Labs !31 Case Study 1 ! Integrating the Signin Process @mark_menard
  32. 32. • Create a Java object that holds a Case Study 1 ! Accessing the Spring Context Enable Labs static reference to the Spring context, the SpringApplicationContextFinder. • The finder is initialized at startup with the reference to the context. • Make a Ruby DSL to access Spring. !32 @mark_menard
  33. 33. public class SpringApplicationContextFinder implements ApplicationContextAware { ! ! ! } private static ApplicationContext CONTEXT; /** * This method is called from within the ApplicationContext once it is * done starting up, it will stick a reference to itself into this bean. * @param context a reference to the ApplicationContext. */ public void setApplicationContext(ApplicationContext context) throws BeansException { CONTEXT = context; } /** * Return a reference to the Spring application context. * @return SpringApplicationContext */ public static Object getContext () { return CONTEXT; } Enable Labs !33 @mark_menard
  34. 34. module SpringSupport def get_spring_context @context ||= Java::lib.SpringApplicationContextFinder.getContext() end end Enable Labs !34 @mark_menard
  35. 35. class SomeObject include SpringSupport ! spring_dependency :some_spring_service ! def do_something (arg) ! #@some_spring_service <---- This is a Java object. ! some_spring_service.do_something(arg) end end ! result = SomeObject.new.do_something("abc") Enable Labs !35 @mark_menard
  36. 36. • Permission system written in Case Study 1 ! Reusing Existing Permission System Enable Labs Java/Groovy. • Still needed to be accessible from Java/Groovy. • Did not want to maintain two versions of the permission system. !36 @mark_menard
  37. 37. • Use the Spring implementation Case Study 1 ! Reusing Existing Permission System of permissions. • Check permissions in a before_filter. • Use the SpringSupport to get access to the security manager. ! Solution Enable Labs !37 @mark_menard
  38. 38. class CheckSecurityAccessService < Struct.new(:url, :user) include SpringSupport spring_dependency :security_manager ! def execute security_manager.check_security_access(build_vr_context) end alias_method :succeeded?, :execute ! private ! def build_vr_context VRContext.new(HashMap.new('url' => url, 'user' => user)) end ! end Enable Labs !38 @mark_menard
  39. 39. Case Study 1 ! Deployment and Gem Management Enable Labs • App used Jetty un-war’ed. • Warbler didn’t apply. • Layout Rails app in /WEB-INF • Used GoldSpike servlet to front Rails. (We have since updated to jruby-rack and a Servlet filter.) • Vendor EVERYTHING and check it into git. !39 @mark_menard
  40. 40. • Completed work in about 3 Case Study 1 ! Success Enable Labs months. • Much better test coverage. • This module is still orthogonal to the main app today. • Code has been very stable. • Code has been ported through multiple versions of Rails. • Almost all new functionality is done in Rails since 1Q2008. !40 @mark_menard
  41. 41. • Minimal integration with existing application. • Highly compartmentalized. • Focused feature set with stable requirements. • Java integration worked. Case Study 1 ! Why did it succeed? Enable Labs !41 @mark_menard
  42. 42. Case Study 2 And Steve said, “let there be iPhone.” Enable Labs !42 @mark_menard
  43. 43. • Client wants about 10 screens available in the browser on the iPhone. • He has a trip in two weeks and wants it working before he leaves. Case Study 2 ! iPhone Enable Labs !43 @mark_menard
  44. 44. • Working screens inside of a week. • Ready for his trip in two weeks. • Went on to be used by the field sales staff for several years. • Total cost far below the client’s expectation. Case Study 2 Rails to the Rescue ! ! iPhone A rip roaring success Enable Labs !44 @mark_menard
  45. 45. Case Study 3 ! Porting to Ruby First Attempt ! A Study in Over Enthusiasm This JRuby is Awesome! Let’s Port the App! Enable Labs !45 @mark_menard
  46. 46. Let’s talk about the brownfield. • 388 Views • 272 Struts 2 Actions • 30 Spring Service Beans • 81 Data Access Objects • 151 Hibernate/JPA Entities (Models) • Not Enough Tests • Primary implementation language is Groovy Enable Labs Case Study 3 ! Porting to Ruby First Attempt !46 @mark_menard
  47. 47. Case Study 3 Current Architecture Enable Labs !47 @mark_menard
  48. 48. • Ruby classes can implement Java interfaces. Case Study 3 ! Porting to Ruby First Attempt Enable Labs !48 @mark_menard
  49. 49. public interface Person { public String getName (); public void setName (String name); } class Person include Java::Person ! attr_accessor :name end Enable Labs !49 @mark_menard
  50. 50. • Ruby classes can implement Java interfaces. • Plug a Ruby/Rails environment into Spring to manufacture Ruby “beans”. Case Study 3 ! Porting to Ruby First Attempt Enable Labs !50 @mark_menard
  51. 51. def getInventoryManager () { log.debug "[ RailsFactory.groovy ] : Instantiating Integration::InventoryManager" eval "Integration::InventoryManager.newn" } Enable Labs !51 @mark_menard
  52. 52. • Ruby classes can implement Java Case Study 3 ! Porting to Ruby First Attempt Enable Labs interfaces. • Plug a Ruby/Rails environment into Spring to manufacture Ruby “beans”. • On a case-by-case basis port Java/Groovy Spring service beans to JRuby. • This was a bottom up port. !52 @mark_menard
  53. 53. Rationale Case Study 3 ! Porting to Ruby First Attempt Enable Labs • No need to mess with the user experience. The views and controllers won’t change. • Can do it incrementally. • Allows Java, Groovy and JRuby objects to just inter-play. • Should be transparent. !53 @mark_menard
  54. 54. Outcome • Technical success, business failure. • Did not take full advantage of our Ruby tools. • There was no driving business value in doing it. Case Study 3 ! Porting to Ruby First Attempt Enable Labs !54 @mark_menard
  55. 55. Case Study 4 ! Porting to Rails Part 2 Ah.... sweet incremental success... ...mostly. Enable Labs !55 @mark_menard
  56. 56. • Do all new work in JRuby. • Find silos of existing functionality. • Wait for significant changes in requirements for the silo. • Port one silo at a time. • Port the whole silo to JRuby. • Write lots of tests. • Find improvements to UI/UX that can be rolled in for justification. • Use SOA for non-user facing services. Enable Labs !56 Case Study 4 ! Porting to Rails Part 2 @mark_menard
  57. 57. Case Study 4 ! Porting to Rails Part 2 The God Object in the Closet Enable Labs !57 @mark_menard
  58. 58. The God Object in the Closet The Strategy ! Make the God object a web service. Implement it in Rails. Translate the existing Groovy code. Port test suite to RSpec. Refactor, refactor, refactor, refactor, refactor.... Review the spec with the client extensively. Case Study 4 Enable Labs Porting to Rails Part 2 !58 @mark_menard
  59. 59. JRuby works today. Java integration lets you play where MRI just can’t go. It’s just Ruby, with Java JVM super powers! Enable Labs !59 @mark_menard
  60. 60. Photo credits http://www.flickr.com/photos/usfwsnortheast/5655240564/ http://www.flickr.com/photos/falcon1961/3304306800/ ! Code Color Scheme Solarized Light ! Syntax Highlighting Tool http://www.andre-simon.de/doku/highlight/en/highlight.html Enable Labs !60 @mark_menard
  61. 61. Enable Labs @mark_menard http://www.enablelabs.com/ info@enablelabs.com 866-895-8189
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×