Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Broken 1.9-mode deploys due to load path modifications #89

Closed
headius opened this issue Jan 31, 2012 · 21 comments
Closed

Broken 1.9-mode deploys due to load path modifications #89

headius opened this issue Jan 31, 2012 · 21 comments
Assignees

Comments

@headius
Copy link
Member

headius commented Jan 31, 2012

There appears to be a problem with recent jruby-rack that's causing a complete failure to deploy 1.9-mode applications. The full output is here: https://gist.github.com/1710589

The problem happens during boot, when the 'yaml' library is loaded. It attempts to require 'psych', which should load lib/ruby/1.9/psych.rb and related files. However, because of an apparent load path misconfiguration, it only finds the internal 'psych.jar' extension (which doesn't exist as a jar...we just use that name...it's PsychLibrary in JRuby).

I added some writes to a file to show loaded features and load path at the point of failure (inside yaml.rb). At that point, loaded features ($") contains psych.jar but none of the psych .rb files, which define the "to_yaml" method in question. The load path contains the following four stdlib-related entries:

classpath:/META-INF/jruby.home/lib/ruby/site_ruby/1.9
classpath:/META-INF/jruby.home/lib/ruby/site_ruby/shared
classpath:/META-INF/jruby.home/lib/ruby/site_ruby/1.8
classpath:/META-INF/jruby.home/lib/ruby/1.9

I believe this is the source of the problem. These paths do not appear to be handled as expected in JRuby. If I make a similar modification at the command line, I get a similar failure:


jruby-1.6.6@debug ~/projects/jruby/debug/war2/WEB-INF/tmp $ java -Djruby.debug.loadService=true -cp /Users/headius/projects/jruby/lib/ruby/gems/shared/gems/jruby-jars-1.6.5.1/lib/jruby-core-1.6.5.1.jar:/Users/headius/projects/jruby/lib/ruby/gems/shared/gems/jruby-jars-1.6.5.1/lib/jruby-stdlib-1.6.5.1.jar org.jruby.Main --1.9 --disable-gems -e "\$:.replace(['classpath:META-INF/jruby.home/lib/ruby/site_ruby/1.9', 'classpath:META-INF/jruby.home/lib/ruby/1.9']); require 'psych'; p \$\""
LoadService: trying builtinLib: thread.jar
LoadService: found builtinLib: thread.jar
LoadService: trying builtinLib: enumerator.class
LoadService: trying builtinLib: enumerator.rb
LoadService: trying resourceFromJarURLWithLoadPath: /Users/headius/projects/jruby/lib/ruby/gems/shared/gems/jruby-jars-1.6.5.1/lib/jruby-stdlib-1.6.5.1.jar!/META-INF/jruby.home/lib/ruby/site_ruby/1.9/enumerator.class
LoadService: trying resourceFromJarURLWithLoadPath: /Users/headius/projects/jruby/lib/ruby/gems/shared/gems/jruby-jars-1.6.5.1/lib/jruby-stdlib-1.6.5.1.jar!/META-INF/jruby.home/lib/ruby/site_ruby/1.9/enumerator.rb
LoadService: trying resourceFromJarURLWithLoadPath: /Users/headius/projects/jruby/lib/ruby/gems/shared/gems/jruby-jars-1.6.5.1/lib/jruby-stdlib-1.6.5.1.jar!/META-INF/jruby.home/lib/ruby/site_ruby/shared/enumerator.class
LoadService: trying resourceFromJarURLWithLoadPath: /Users/headius/projects/jruby/lib/ruby/gems/shared/gems/jruby-jars-1.6.5.1/lib/jruby-stdlib-1.6.5.1.jar!/META-INF/jruby.home/lib/ruby/site_ruby/shared/enumerator.rb
LoadService: trying resourceFromJarURLWithLoadPath: /Users/headius/projects/jruby/lib/ruby/gems/shared/gems/jruby-jars-1.6.5.1/lib/jruby-stdlib-1.6.5.1.jar!/META-INF/jruby.home/lib/ruby/site_ruby/1.8/enumerator.class
LoadService: trying resourceFromJarURLWithLoadPath: /Users/headius/projects/jruby/lib/ruby/gems/shared/gems/jruby-jars-1.6.5.1/lib/jruby-stdlib-1.6.5.1.jar!/META-INF/jruby.home/lib/ruby/site_ruby/1.8/enumerator.rb
LoadService: trying resourceFromJarURLWithLoadPath: /Users/headius/projects/jruby/lib/ruby/gems/shared/gems/jruby-jars-1.6.5.1/lib/jruby-stdlib-1.6.5.1.jar!/META-INF/jruby.home/lib/ruby/1.9/enumerator.class
LoadService: trying resourceFromJarURLWithLoadPath: /Users/headius/projects/jruby/lib/ruby/gems/shared/gems/jruby-jars-1.6.5.1/lib/jruby-stdlib-1.6.5.1.jar!/META-INF/jruby.home/lib/ruby/1.9/enumerator.rb
LoadService: trying builtinLib: enumerator.jar
LoadService: found builtinLib: enumerator.jar
LoadService: trying builtinLib: generator_internal.class
LoadService: trying builtinLib: generator_internal.rb
LoadService: found builtinLib: generator_internal.rb
LoadService: trying builtinLib: thread.class
LoadService: trying builtinLib: thread.rb
LoadService: found builtinLib: thread.rb
LoadService: trying builtinLib: psych.class
LoadService: trying builtinLib: psych.rb
LoadService: trying resourceFromLoadPath: '/Users/headius/projects/jruby/debug/war2/WEB-INF/tmp/classpath:META-INF/jruby.home/lib/ruby/site_ruby/1.9/psych.class' false false
LoadService: trying resourceFromLoadPath: '/Users/headius/projects/jruby/debug/war2/WEB-INF/tmp/classpath:META-INF/jruby.home/lib/ruby/site_ruby/1.9/psych.rb' false false
LoadService: trying resourceFromLoadPath: '/Users/headius/projects/jruby/debug/war2/WEB-INF/tmp/classpath:META-INF/jruby.home/lib/ruby/1.9/psych.class' false false
LoadService: trying resourceFromLoadPath: '/Users/headius/projects/jruby/debug/war2/WEB-INF/tmp/classpath:META-INF/jruby.home/lib/ruby/1.9/psych.rb' false false
LoadService: trying builtinLib: psych.jar
LoadService: found builtinLib: psych.jar
["thread.jar", "enumerator.jar", "generator_internal.rb", "thread.rb", "psych.jar"]

Notice that the classpath: paths are getting prefixed with my current dir, which makes them fail to resolve any of the jarred-up files.

Reproducing this issue should be easy. I tested on Glassfish, and a user that initiated the JRuby bug (http://jira.codehaus.org/browse/JRUBY-6397) is using JBoss.

  • Generate a new Rails app
  • Configure warbler to deploy in 1.9 mode
  • Deploy and hit the app

The app will fail to boot with the error reported here.

This is a pretty critical issue, since it means nobody can deploy WARed up applications in 1.9 mode right now. I'm assigning @kares to look at it since he's had most of the recent commits, some of them load path-related.

Help me Karol Bucek...you're my only hope!

@ghost ghost assigned kares Jan 31, 2012
@dobbs
Copy link

dobbs commented Jan 31, 2012

I tried backing off jruby-rack versions in small steps as far back as 1.0.10 and still no sign of to_yaml.

Removing 1.9 syntax from my code and deploying without the 1.9 flags has resolved my immediate deploy issues.

So deploy works fine in ruby 1.8 mode and doesn't work at all in ruby 1.9 mode.

@dobbs
Copy link

dobbs commented Jan 31, 2012

this conversation from #jruby today may also be helpful:
http://logs.jruby.org/jruby/2012-01-31.html

18:17:18 dobbs: so perhaps this never worked with 1.9 mode
18:17:22 I have a suspicion
18:17:36 1.9 mode does not include "." in its load path
18:17:45 perhaps that's acting as a catchall in 1.8 mode
18:17:54 you might try adding $: << '.' to the top of boot.rb
...
18:47:33 headius: adding $: << '.' to the top of boot.rb didn't fix it. The server log looks the same.

@trajar
Copy link
Contributor

trajar commented Feb 1, 2012

I explicitly put "require 'yaml.rb'" in my boot.rb .. It seemed to work on my 3.2 app in 1.9 mode.

@kares
Copy link
Member

kares commented Feb 1, 2012

nice one !

@headius I'm not sure I understand why's there :

LoadService: trying resourceFromJarURLWithLoadPath: /Users/headius/projects/jruby/lib/ruby/gems/shared/gems/jruby-jars-1.6.5.1/lib/jruby-stdlib-1.6.5.1.jar!/META-INF/jruby.home/lib/ruby/site_ruby/1.9/enumerator.rb

but then for psych.rb there's "only" :

LoadService: trying resourceFromLoadPath: '/Users/headius/projects/jruby/debug/war2/WEB-INF/tmp/classpath:META-INF/jruby.home/lib/ruby/site_ruby/1.9/psych.rb'

isn't the load service deterministically trying the same path for each .rb ?

@trajar I'm guessing you've put that in rails/boot.rb also are you deploying as a .war ?

I'm sorry I only had a quick look and try, unfortunately just about when this got reported I got sick :(
I was actually in the middle of taking case of some other reported issues. I'll try to look closer ASAP ...

@trajar
Copy link
Contributor

trajar commented Feb 2, 2012

Yeah I'm deploying inside a war (actually technically custom war-based OSGi container). I had to do the same thing for 'digest.rb' in order to get all the MD5 goodness. I spend an afternoon trying to figure it out, and it seems that if you are loading something that might be custom jar or jruby library, that gets preference, and the .rb gets skipped.

@kares
Copy link
Member

kares commented Feb 6, 2012

confirming that putting a require 'psych.rb' in [RAILS_ROOT]/boot.rb before require 'bundler/setup'
serves as a workaround and the application seems to successfully boot when deployed as .war
verified this on JBoss 7

@kares
Copy link
Member

kares commented Feb 7, 2012

after more digging in - I debugged the load service while deploying a .war in 1.9 mode onto JBoss - I am starting to think the most elegant solution, that would avoid similar "surprises" for the the future as well, would be to adjust the load path on JRuby's side (when dealing with classpath:/ entries).

first of all let's clarify the title: there's no $LOAD_PATH-modifications happening on our side, in this case !
on JBoss with JRuby home 'classpath:/META-INF/jruby.home' here's what the load path looks like :

"classpath:/META-INF/jruby.home/lib/ruby/site_ruby/1.9"
"classpath:/META-INF/jruby.home/lib/ruby/site_ruby/shared"
"classpath:/META-INF/jruby.home/lib/ruby/site_ruby/1.8"
"classpath:/META-INF/jruby.home/lib/ruby/1.9"

we would modify the $LOAD_PATH if jruby.home == /tmp since than it would most likely contain non-sense such as :

"/tmp/lib/ruby/site_ruby/1.9"
...

but this is not the problem here ...

I'm certainly no expert on what edge cases the org.jruby.runtime.load.LoadService is capable of handling.
Suppose our case, we have a rails.war and stdlib is located within a jar at WEB-INF/lib/jruby-stdlib-VERSION.jar.

Recall the require 'psych' loading logic :

LoadService: trying builtinLib: psych.class
LoadService: trying builtinLib: psych.rb
LoadService: trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/classpath:/META-INF/jruby.home/lib/ruby/site_ruby/1.9/psych.class' false false
LoadService: trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/classpath:/META-INF/jruby.home/lib/ruby/site_ruby/1.9/psych.rb' false false
LoadService: trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/classpath:/META-INF/jruby.home/lib/ruby/site_ruby/shared/psych.class' false false
LoadService: trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/classpath:/META-INF/jruby.home/lib/ruby/site_ruby/shared/psych.rb' false false
LoadService: trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/gems/gems/bundler-1.0.21/lib/psych.class' false false
LoadService: trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/gems/gems/bundler-1.0.21/lib/psych.rb' false false
LoadService: trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/classpath:/META-INF/jruby.home/lib/ruby/site_ruby/1.8/psych.class' false false
LoadService: trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/classpath:/META-INF/jruby.home/lib/ruby/site_ruby/1.8/psych.rb' false false
LoadService: trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/classpath:/META-INF/jruby.home/lib/ruby/1.9/psych.class' false false
LoadService: trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/classpath:/META-INF/jruby.home/lib/ruby/1.9/psych.rb' false false
LoadService: trying builtinLib: psych.jar
LoadService: found builtinLib: psych.jar

or even better the require 'yaml' debugs :

trying builtinLib: yaml.class
trying builtinLib: yaml.rb
trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/classpath:/META-INF/jruby.home/lib/ruby/site_ruby/1.9/yaml.class' false false
trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/classpath:/META-INF/jruby.home/lib/ruby/site_ruby/1.9/yaml.rb' false false
trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/classpath:/META-INF/jruby.home/lib/ruby/site_ruby/shared/yaml.class' false false
trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/classpath:/META-INF/jruby.home/lib/ruby/site_ruby/shared/yaml.rb' false false
trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/gems/gems/bundler-1.0.21/lib/yaml.class' false false
trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/gems/gems/bundler-1.0.21/lib/yaml.rb' false false
trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/classpath:/META-INF/jruby.home/lib/ruby/site_ruby/1.8/yaml.class' false false
trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/classpath:/META-INF/jruby.home/lib/ruby/site_ruby/1.8/yaml.rb' false false
trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/classpath:/META-INF/jruby.home/lib/ruby/1.9/yaml.class' false false
trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/classpath:/META-INF/jruby.home/lib/ruby/1.9/yaml.rb' false false
trying builtinLib: yaml.jar
trying builtinLib: yaml.so
trying builtinLib: yaml.bundle
trying builtinLib: yaml.dll
trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/classpath:/META-INF/jruby.home/lib/ruby/site_ruby/1.9/yaml.jar' false false
trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/classpath:/META-INF/jruby.home/lib/ruby/site_ruby/1.9/yaml.so' false false
trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/classpath:/META-INF/jruby.home/lib/ruby/site_ruby/1.9/yaml.bundle' false false
trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/classpath:/META-INF/jruby.home/lib/ruby/site_ruby/1.9/yaml.dll' false false
trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/classpath:/META-INF/jruby.home/lib/ruby/site_ruby/shared/yaml.jar' false false
trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/classpath:/META-INF/jruby.home/lib/ruby/site_ruby/shared/yaml.so' false false
trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/classpath:/META-INF/jruby.home/lib/ruby/site_ruby/shared/yaml.bundle' false false
trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/classpath:/META-INF/jruby.home/lib/ruby/site_ruby/shared/yaml.dll' false false
trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/gems/gems/bundler-1.0.21/lib/yaml.jar' false false
trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/gems/gems/bundler-1.0.21/lib/yaml.so' false false
trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/gems/gems/bundler-1.0.21/lib/yaml.bundle' false false
trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/gems/gems/bundler-1.0.21/lib/yaml.dll' false false
trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/classpath:/META-INF/jruby.home/lib/ruby/site_ruby/1.8/yaml.jar' false false
trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/classpath:/META-INF/jruby.home/lib/ruby/site_ruby/1.8/yaml.so' false false
trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/classpath:/META-INF/jruby.home/lib/ruby/site_ruby/1.8/yaml.bundle' false false
trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/classpath:/META-INF/jruby.home/lib/ruby/site_ruby/1.8/yaml.dll' false false
trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/classpath:/META-INF/jruby.home/lib/ruby/1.9/yaml.jar' false false
trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/classpath:/META-INF/jruby.home/lib/ruby/1.9/yaml.so' false false
trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/classpath:/META-INF/jruby.home/lib/ruby/1.9/yaml.bundle' false false
trying resourceFromLoadPath: '/opt/server/jboss-as-7.1.0.CR1b/standalone/tmp/vfs/temp216f26790480917e/rails31.war-97a876e1d5956452/WEB-INF/classpath:/META-INF/jruby.home/lib/ruby/1.9/yaml.dll' false false
trying fileInClasspath: META-INF/jruby.home/lib/ruby/site_ruby/1.9/yaml.class
trying fileInClasspath: META-INF/jruby.home/lib/ruby/site_ruby/shared/yaml.class
trying fileInClasspath: META-INF/jruby.home/lib/ruby/site_ruby/1.8/yaml.class
trying fileInClasspath: META-INF/jruby.home/lib/ruby/1.9/yaml.class
trying fileInClasspath: yaml.class
trying fileInClasspath: META-INF/jruby.home/lib/ruby/site_ruby/1.9/yaml.rb
trying fileInClasspath: META-INF/jruby.home/lib/ruby/site_ruby/shared/yaml.rb
trying fileInClasspath: META-INF/jruby.home/lib/ruby/site_ruby/1.8/yaml.rb
trying fileInClasspath: META-INF/jruby.home/lib/ruby/1.9/yaml.rb

if the resourceFromLoadPath was smart enough to detect a path entry starting with classpath:/ instead of expanding it from CWD it would certainly be for good and solve this issue, I even think it would do some minor performance boost for cases like these.

But most importantly if say I append $LOAD_PATH << 'classpath:/META-INF/foo' and then require 'bar' it would try and resolve a class path file classpath:/META-INF/foo/bar.rb early on and I won't accidentaly end up with a builtin library bar.jar if there's one ...

Solving this on our side seems to be only possible with a $LOAD_PATH modification hack :
Detect a /jruby-stdlib.?.jar/ in *WEB-INF/lib and modify the $LOAD_PATH to something like jar:/jruby-stdlib.jar/!/META-INF/jruby.home/lib/ruby/1.9 (so the load service picks it up during resourceFromLoadPath).

But since this seems to be only necessary for a few jar conflicting cases so far (yaml and digest) I would rather do a workaround of preloading these libraries with require 'yaml.rb' if it's going to be resolved on JRuby's side ...

Any suggestions are welcome ...

@headius
Copy link
Member Author

headius commented Feb 7, 2012

I think you may be right about modifying JRuby to recognize load path entries starting with classpath:/ and resolve them as normal classloader resources. I'll look into that today.

@headius
Copy link
Member Author

headius commented Feb 7, 2012

Here's a first stab at a patch: https://gist.github.com/1760124

I've confirmed that this resolves issues at the command line when the load path contains only classpath:/ entries:

before:

system ~/projects/jruby $ java -Djruby.home=/tmp -cp .:lib/jruby.jar org.jruby.Main -e '$:.replace(["classpath:/lib/ruby/1.9"]); require "digest"; p $"'
["thread.rb", "thread.jar", "digest.jar"]

after:

system ~/projects/jruby $ java -Djruby.home=/tmp -cp .:lib/jruby.jar org.jruby.Main -e '$:.replace(["classpath:/lib/ruby/1.9"]); require "digest"; p $"'
["thread.rb", "thread.jar", "digest.jar", "/Users/headius/projects/jruby/lib/ruby/1.9/digest.rb"]

And a similar result for the psych libraries.

I'm not sure where the classpath:/ entries in load path are coming from, however. They appear to be built up like normal, with site_lib and 1.9 entries, which makes me think somewhere jruby.home is getting set up as a classpath:/ path, like classpath:/META-INF/jruby.home. I'd like to know exactly how that's happening, but this patch appears to make JRuby honor classpath:/ load path entries correctly.

@kares
Copy link
Member

kares commented Feb 7, 2012

@headius looks great but since I commented I was thinking - what if the path does not have the "classpath:" prefix
For some containers this might be true and since everything else is about the same (stdlib in a WEB-INF/lib jar) it will once again fail (the load service log would have look exactly the same since in the end it tries loading the resource from CP no matter if there's a classpath: prefix - maybe the logic should be changed to check the CP "earlier")

@headius
Copy link
Member Author

headius commented Feb 7, 2012

Hmm yes, that could be. I thought it should be looking for classloader resources before builtins, but perhaps I'm mistaken.

@kares
Copy link
Member

kares commented Feb 7, 2012

we might need to do something like this https://gist.github.com/1760422 (based on jruby-1_6_5)
I've tested with LoadService recompiled and the .war started working in 1.9 mode as expected ...

@headius
Copy link
Member Author

headius commented Feb 7, 2012

Ah yes.

So that code is part of findLibraryWithoutCWD, called from NormalSearcher which is the first real search done. And I remember why we did it this way: because that's the order MRI seemed to find extensions at some point in the past. However, I think MRI's ordering can be explained by looking at its load path:

system ~/projects/jruby $ rvm 1.9.3 do ruby -e 'puts $:'
/Users/headius/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/site_ruby/1.9.1
/Users/headius/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/site_ruby/1.9.1/x86_64-darwin10.8.0
/Users/headius/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/site_ruby
/Users/headius/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/vendor_ruby/1.9.1
/Users/headius/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/vendor_ruby/1.9.1/x86_64-darwin10.8.0
/Users/headius/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/vendor_ruby
/Users/headius/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/1.9.1
/Users/headius/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/1.9.1/x86_64-darwin10.8.0

Each normal lib entry (1.9.1 dirs) is followed by a platform-specific dir. When loading psych, the .bundle (OS X) comes from one of these:

system ~/projects/jruby $ rvm 1.9.3 do ruby -rpsych -e 'puts $"' | grep psych.bundle
/Users/headius/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/1.9.1/x86_64-darwin10.8.0/psych.bundle

So our searching for builtins first does not quite match MRI. We should search builtins as though they're a load path entry after each source-related entry.

For 1.6.7 we want a minimally-invasive patch, though. I wonder if my patch above is good enough for 1.6.7?

@headius
Copy link
Member Author

headius commented Feb 7, 2012

Ok, so here's an updated summary.

MRI sets up its load path so that its own source and ext dirs are at the front of the load path. This allows it to ensure the "built in" libraries get a chance to load before user-added load path entries.

Our case is set up a bit differently. Since there's no "load path" entry for built in extensions (they're just special names we know are part of JRuby), we check for built-in extensions before searching load path. Now the obvious idea would be to move built-in ext searching after load path searching, but this is not right for a couple reasons:

  • it would allow user-provided files to load before our own extensions, which could cause our extensions to fail
  • it still doesn't solve this bug

The reason it doesn't solve this bug is because our classloader searching always comes after normal filesystem-based load path searching. As a result, the examples in this bug (psych and digest) end up finding the ".jar" built-in ext before ever trying to search classloader.

This is further aggravated because under some deployments, the load path is getting filled with classpath:/ entries instead of filesystem entries, since there's no searchable filesystem paths. In that case, built-in exts end up getting found before any .rb files from load path, which ultimately causes psych.jar and digest.jar exts to load instead of psych.rb and digest.rb.

We might be able to fix this by moving our built-in ext search after classloader searching, but that has even more chance of loading a user-provided ".jar" instead of our built-in ext ".jar".

I'm going to ponder this a bit more and try a few changes.

@headius
Copy link
Member Author

headius commented Feb 7, 2012

FWIW, I think my fix to make classpath:/ load path entries work properly does represent a good fix, because:

  • It causes a classloader search to happen as part of the normal load path sequence, ahead of built-in ext searching.
  • It is minimally invasive, and only fixes classpath:/ load path entries that were not searched in the correct way before.

@kares: I think your point about bare classloader resource loads is valid, but perhaps the real solution is for us to push for classloader resources to either be on load path (with classpath:/ entries) or to have a full classpath:/ URL for there resource itself. That would allow us to always recognize that it's supposed to come from classloader, rather than hoping the classloader phase will get lucky and pick it up.

@headius
Copy link
Member Author

headius commented Feb 7, 2012

Ok, I think I know where the classpath: load path entries come from now too.

If no jruby.home is set, the following code is eventually called in JRuby to determine it (called from RubyInstanceConfig.calculateJRubyHome):


    public static String findFromJar(Object instance) {
        URL resource = instance.getClass().getResource("/META-INF/jruby.home");
        if (resource == null) {
            return null;
        }

        String location = null;
        if (resource.getProtocol().equals("jar")) {
            location = getPath(resource);
            if (!location.startsWith("file:")) {
                // for remote-sourced classpath resources, just use classpath:
                location = "classpath:/META-INF/jruby.home";
            }
        } else {
            location = "classpath:/META-INF/jruby.home";
        }

        // Trim trailing slash. It confuses OSGi containers...
        if (location.endsWith("/")) {
            location = location.substring(0, location.length() - 1);
        }

        return location;
    }

The logic here basically looks for META-INF/jruby.home as a classloader resource. If it finds it, it checks the format of the URL. If the URL does not start with jar:file:, it ends up using classpath:/META-INF/jruby.home as jruby.home, and then builds up load paths from there.

So there's a couple possibilities here:

  • jruby.rack may never eventually set a jruby.home in some environments.
  • jruby may see that a jruby.home is set, but reject it and fall back on this logic.

Looking at jruby-rack, I can only see a few places where it attempts to set up jruby.home:

  • In DefaultRackApplicationFactory.createDefaultConfig, where it attempts to get /META-INF/jruby.home as a classloader resource; if it starts with jar it sets it, but otherwise it does not.
  • In booter.rb method adjust_load_path, where it attemps to replace tmpdir-based load path entries with bare META-INF entries, otherwise adding the bare META-INF paths to load path.

My current theory is that jruby.home is not being set, and for whatever reason, the URL provided for the "META-INF/jruby.home" resource resolves to a URL that does not start with jar:file:.

If it resolved to a jar:file: URL, then we'd add jar:file: URLs to load path which are currently handled right. In this case, however, we add classpath: entries, which were not handled properly. This again leads me to conclude that my fix is probably good enough for JRuby 1.6.7, since either jar:file URLs or classpath: URLs in load path will resolve properly.

There is still one mystery for me: why this only fails to work in 1.9 mode.

@kares
Copy link
Member

kares commented Feb 8, 2012

my attempt was a quick shoot - I sure was aware that I probably broke something by changing the loading order.

your fix seems perfectly valid and would solve the $LOAD_PATH << classpath:// user scenario, I expect it to work on JBoss/Tomcat but I'm in doubt for WebLogic/WebSphere. I will perform a .war deploy with some servers later and report back. I also agree on jar:file://... entries (after looking at LoadService I realized it would have solved this issue) if it worked correctly and we got these from various servers but unfortunately I;m not sure we will and I did not wanted jruby-rack to know too much about "warbler specifics" (there's a jruby-stdlib-1.6.6.jar somewhere in WEB-INF/lib) as a workaround.
anyway, I guess we'll figure out something eventually after looking at how it reacts in those servers.

@headius
Copy link
Member Author

headius commented Feb 8, 2012

Ok, I will mark this closed. I have pushed the classpath: fix to JRuby master and 1.6 branches, and kicked off a dist build of 1.6. It should be possible to use that to test jruby-rack for deployment.

@headius headius closed this as completed Feb 8, 2012
@kares
Copy link
Member

kares commented Feb 9, 2012

@headius sorry for the delay, I've verified this to be working with jruby-1.6.7.dev gem on JBoss 7.1, Tomcat 6.0 as well as WebLogic 10.3.5 all of which seems to be using classpath: entries by default. unfortunately it's still the same failure on WebSphere but that seems to be caused by an incorrect jruby.home detection and I'll try to modify those $LOAD_PATH entries to be cp prefixed as well ... thanks for your help !

kares added a commit to kares/jruby-rack that referenced this issue Feb 9, 2012
…ers where jruby.home is detected as /tmp - verified that this works in 1.9 mode on WebSphere 8 with the JRuby 1.6.7 LoadService fix (related to jruby#89)
@headius
Copy link
Member Author

headius commented Feb 9, 2012

Thanks so much for looking into this, especially since it wasn't ultimately a jruby-rack bug! If there's anything else you think we need to do in JRuby for e.g. WebSphere, let me know ASAP.

@kares
Copy link
Member

kares commented Feb 9, 2012

NP, well there's the "obvious" better jruby.home detection - I've just tried shooting some alternative getResource Java code at it but seems it always returns null - a mystery for me why later on classLoader.getResource from LoadService works.But there's a workaround already for that, thus I do not think it's critical although would be nice. I'll open a pull if I eventually get something working.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants