jruby 6 years in production

61
JRuby Insights from Six Years in Production Mark Menard Enable Labs

Upload: mark-menard

Post on 19-Jan-2015

863 views

Category:

Technology


0 download

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

JRubyInsights from Six Years in Production

Mark Menard Enable Labs

Page 2: JRuby 6 Years in Production

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

Enable Labs @mark_menard!3

Who are you?

Page 4: JRuby 6 Years in Production

Enable Labs @mark_menard!4

You love Ruby!

Page 5: JRuby 6 Years in Production

Enable Labs @mark_menard!5

You work in a Java shop.

Page 6: JRuby 6 Years in Production

Enable Labs @mark_menard!6

You have a client who uses Java.

Page 7: JRuby 6 Years in Production

Enable Labs @mark_menard!7

You have to work on

something that uses Java.

Page 8: JRuby 6 Years in Production

Enable Labs @mark_menard!8

You have a project that could benefit from true concurrency.

Page 9: JRuby 6 Years in Production

Enable Labs @mark_menard!9

Why?

Page 10: JRuby 6 Years in Production

Enable Labs @mark_menard!10

So... what is JRuby anyway?

Page 11: JRuby 6 Years in Production

Enable Labs @mark_menard!11

What’s different?

But I use MRI!

Page 12: JRuby 6 Years in Production

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

Enable Labs @mark_menard!13

What can JRuby do for you?

Page 14: JRuby 6 Years in Production

Enable Labs @mark_menard!14

Parallelism in JRuby

Page 15: JRuby 6 Years in Production

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

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

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

Enable Labs @mark_menard!18

Using Java Libraries

Page 19: JRuby 6 Years in Production

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

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

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

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

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

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

Enable Labs @mark_menard!25

Case Studies

Page 26: JRuby 6 Years in Production

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

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

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

Enable Labs @mark_menard!29

Case Study 1 !

What Made it Work

Java Integration

Page 30: JRuby 6 Years in Production

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

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

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

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

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

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

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

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

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

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

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

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

Enable Labs @mark_menard!42

Case Study 2

And Steve said, “let there be iPhone.”

Page 43: JRuby 6 Years in Production

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

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

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

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

Enable Labs @mark_menard!47

Case Study 3

Current Architecture

Page 48: JRuby 6 Years in Production

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

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

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

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

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

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

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

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

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

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

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

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

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

Page 61: JRuby 6 Years in Production

http://www.enablelabs.com/

[email protected]

866-895-8189

Enable Labs@mark_menard