jruby 6 years in production
DESCRIPTION
Insight from using JRuby in production for six years integrating with Java services, such as Spring.TRANSCRIPT
![Page 1: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/1.jpg)
JRubyInsights from Six Years in Production
Mark Menard Enable Labs
![Page 2: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/2.jpg)
Enable Labs @mark_menard!2
How do you get Ruby into a Java shop?
How do you get Ruby into a legacy Java app?
How do you move past Java to Ruby?
![Page 3: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/3.jpg)
Enable Labs @mark_menard!3
Who are you?
![Page 4: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/4.jpg)
Enable Labs @mark_menard!4
You love Ruby!
![Page 5: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/5.jpg)
Enable Labs @mark_menard!5
You work in a Java shop.
![Page 6: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/6.jpg)
Enable Labs @mark_menard!6
You have a client who uses Java.
![Page 7: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/7.jpg)
Enable Labs @mark_menard!7
You have to work on
something that uses Java.
![Page 8: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/8.jpg)
Enable Labs @mark_menard!8
You have a project that could benefit from true concurrency.
![Page 9: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/9.jpg)
Enable Labs @mark_menard!9
Why?
![Page 10: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/10.jpg)
Enable Labs @mark_menard!10
So... what is JRuby anyway?
![Page 11: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/11.jpg)
Enable Labs @mark_menard!11
What’s different?
But I use MRI!
![Page 12: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/12.jpg)
Enable Labs @mark_menard!12
• 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.
To Summarize
![Page 13: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/13.jpg)
Enable Labs @mark_menard!13
What can JRuby do for you?
![Page 14: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/14.jpg)
Enable Labs @mark_menard!14
Parallelism in JRuby
![Page 15: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/15.jpg)
Enable Labs @mark_menard!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.
![Page 16: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/16.jpg)
Enable Labs @mark_menard!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
![Page 17: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/17.jpg)
Enable Labs @mark_menard!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)
![Page 18: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/18.jpg)
Enable Labs @mark_menard!18
Using Java Libraries
![Page 19: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/19.jpg)
Enable Labs @mark_menard!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
![Page 20: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/20.jpg)
Enable Labs @mark_menard!20
require 'itext' Itext::Document.create("document.pdf") do p "Hello from JRuby" end
![Page 21: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/21.jpg)
Enable Labs @mark_menard!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
![Page 22: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/22.jpg)
Enable Labs @mark_menard!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
![Page 23: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/23.jpg)
Enable Labs @mark_menard!23
require 'itext' Itext::Document.create("document.pdf") do p "Hello from JRuby" end
![Page 24: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/24.jpg)
Enable Labs @mark_menard!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
![Page 25: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/25.jpg)
Enable Labs @mark_menard!25
Case Studies
![Page 26: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/26.jpg)
Enable Labs @mark_menard!26
Case Study 1 !
What the Client Wanted !
1Q2008
• New functionality that was predominantly orthogonal to their existing app.
• Single Signon • Faster Development Times • Some integration at the data
level.
![Page 27: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/27.jpg)
Enable Labs @mark_menard!27
Case Study 1 !
The Technical Environment
• Struts 2 • Groovy 1.0 • Jetty Running Un-war'ed • Spring Dependency Injection -
XML Hell • Struts 2 - More XML Hell !
• Mostly Continuous Deployment
![Page 28: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/28.jpg)
Enable Labs @mark_menard!28
Case Study 1 !
What We Used
• JRuby 1.0 • Rails 2.1 • ERB
![Page 29: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/29.jpg)
Enable Labs @mark_menard!29
Case Study 1 !
What Made it Work
Java Integration
![Page 30: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/30.jpg)
Enable Labs @mark_menard!30
Case Study 1 !
The Challenges
• Integrating the Signin Process • Accessing the Spring Context • Reusing Existing Permission
System • Deployment • Gem Management
![Page 31: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/31.jpg)
Enable Labs @mark_menard!31
Case Study 1 !
Integrating the Signin Process
• 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.
![Page 32: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/32.jpg)
Enable Labs @mark_menard!32
Case Study 1 !
Accessing the Spring Context
• Create a Java object that holds a 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.
![Page 33: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/33.jpg)
Enable Labs @mark_menard!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; } }
![Page 34: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/34.jpg)
Enable Labs @mark_menard!34
module SpringSupport def get_spring_context @context ||= Java::lib.SpringApplicationContextFinder.getContext() end end
![Page 35: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/35.jpg)
Enable Labs @mark_menard!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")
![Page 36: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/36.jpg)
Enable Labs @mark_menard!36
Case Study 1 !
Reusing Existing Permission System
• Permission system written in Java/Groovy.
• Still needed to be accessible from Java/Groovy.
• Did not want to maintain two versions of the permission system.
![Page 37: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/37.jpg)
Enable Labs @mark_menard!37
Case Study 1 !
Reusing Existing Permission System !
Solution
• Use the Spring implementation of permissions.
• Check permissions in a before_filter.
• Use the SpringSupport to get access to the security manager.
![Page 38: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/38.jpg)
Enable Labs @mark_menard!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
![Page 39: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/39.jpg)
Enable Labs @mark_menard!39
Case Study 1 !
Deployment and Gem Management
• 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.
![Page 40: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/40.jpg)
Enable Labs @mark_menard!40
Case Study 1 !
Success
• Completed work in about 3 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.
![Page 41: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/41.jpg)
Enable Labs @mark_menard!41
Case Study 1 !
Why did it succeed?
• Minimal integration with existing application.
• Highly compartmentalized. • Focused feature set with stable
requirements. • Java integration worked.
![Page 42: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/42.jpg)
Enable Labs @mark_menard!42
Case Study 2
And Steve said, “let there be iPhone.”
![Page 43: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/43.jpg)
Enable Labs @mark_menard!43
Case Study 2 !
iPhone
• 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.
![Page 44: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/44.jpg)
Enable Labs @mark_menard!44
Case Study 2 !
iPhone
• 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.
Rails to the Rescue !
A rip roaring success
![Page 45: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/45.jpg)
Enable Labs @mark_menard!45
Case Study 3 !
Porting to Ruby First Attempt !
A Study in Over Enthusiasm
This JRuby is Awesome! Let’s Port the App!
![Page 46: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/46.jpg)
Enable Labs @mark_menard!46
Case Study 3 !
Porting to Ruby First Attempt
• 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
Let’s talk about the brownfield.
![Page 47: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/47.jpg)
Enable Labs @mark_menard!47
Case Study 3
Current Architecture
![Page 48: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/48.jpg)
Enable Labs @mark_menard!48
Case Study 3 !
Porting to Ruby First Attempt
• Ruby classes can implement Java interfaces.
![Page 49: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/49.jpg)
Enable Labs @mark_menard!49
public interface Person { public String getName (); public void setName (String name); }
class Person include Java::Person !
attr_accessor :name end
![Page 50: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/50.jpg)
Enable Labs @mark_menard!50
Case Study 3 !
Porting to Ruby First Attempt
• Ruby classes can implement Java interfaces.
• Plug a Ruby/Rails environment into Spring to manufacture Ruby “beans”.
![Page 51: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/51.jpg)
Enable Labs @mark_menard!51
def getInventoryManager () { log.debug "[ RailsFactory.groovy ] : Instantiating Integration::InventoryManager" eval "Integration::InventoryManager.new\n" }
![Page 52: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/52.jpg)
Enable Labs @mark_menard!52
Case Study 3 !
Porting to Ruby First Attempt
• Ruby classes can implement Java 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.
![Page 53: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/53.jpg)
Enable Labs @mark_menard!53
Case Study 3 !
Porting to Ruby First Attempt
• 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.
Rationale
![Page 54: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/54.jpg)
Enable Labs @mark_menard!54
Case Study 3 !Porting to Ruby First Attempt
• Technical success, business failure. • Did not take full advantage of our
Ruby tools. • There was no driving business value
in doing it.
Outcome
![Page 55: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/55.jpg)
Enable Labs @mark_menard!55
Case Study 4 !
Porting to Rails Part 2
Ah.... sweet incremental success... ...mostly.
![Page 56: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/56.jpg)
Enable Labs @mark_menard!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.
Case Study 4 !
Porting to Rails Part 2
![Page 57: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/57.jpg)
Enable Labs @mark_menard!57
Case Study 4 !
Porting to Rails Part 2
The God Object in the Closet
![Page 58: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/58.jpg)
Enable Labs @mark_menard!58
Case Study 4 Porting to Rails Part 2The
God
Obj
ect i
n th
e Cl
oset 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.
![Page 59: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/59.jpg)
Enable Labs @mark_menard!59
JRuby works today.
Java integration lets you play where MRI just can’t go.
It’s just Ruby, with Java JVM super powers!
![Page 60: JRuby 6 Years in Production](https://reader033.vdocuments.net/reader033/viewer/2022051612/54bc892e4a79599d448b4577/html5/thumbnails/60.jpg)
Enable Labs @mark_menard!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