JRuby (please use github issues at http://bugs.jruby.org)
  1. JRuby (please use github issues at http://bugs.jruby.org)
  2. JRUBY-6731

Can't load class files in WARs compiled by warbler using jruby 1.7.0.preview1

    Details

    • Type: Bug Bug
    • Status: Resolved Resolved
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: JRuby 1.7.0.pre1
    • Fix Version/s: JRuby 1.7.0.pre2
    • Component/s: Rails WAR Deployment
    • Labels:
      None
    • Environment:
    • Number of attachments :
      3

      Description

      When trying to load classfiles generated by warbler I am getting the error below. The application loads fine if it is not compiled. It also was working in JRuby 1.6.7.2. I was able to duplicate this issue on both Windows and Linux.

      Jun 15, 2012 11:28:14 AM org.apache.catalina.core.ApplicationContext log
      SEVERE: Error: application initialization failed
      org.jruby.rack.RackInitializationException: unable to create shared application instance
      at org.jruby.rack.SharedRackApplicationFactory.init(SharedRackApplicationFactory.java:44)
      at org.jruby.rack.RackServletContextListener.contextInitialized(RackServletContextListener.java:48)
      at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4779)
      at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5273)
      at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
      at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1566)
      at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1556)
      at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
      at java.util.concurrent.FutureTask.run(FutureTask.java:166)
      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
      at java.lang.Thread.run(Thread.java:722)
      Caused by: org.jruby.rack.RackInitializationException: no such file to load – C:/myapp/apps/apache-tomcat/webapps/../../../webapps/myapp/WEB-INF/config/boot.class
      from org/jruby/RubyKernel.java:982:in `require'
      from C:/myapp/apps/apache-tomcat/webapps/../../../webapps/myapp/WEB-INF/config/boot.rb:1:in `(root)'
      from org/jruby/RubyKernel.java:982:in `require'
      from file:/C:/myapp/webapps/myapp/WEB-INF/lib/gems-gems-jruby-rack-1.1.6-lib-jruby-rack-1.1.6.jar!/jruby/rack/rails/environment3.rb:1:in `(root)'
      from file:/C:/myapp/webapps/myapp/WEB-INF/lib/gems-gems-jruby-rack-1.1.6-lib-jruby-rack-1.1.6.jar!/jruby/rack/rails/environment3.rb:21:in `load_environment'
      from file:/C:/myapp/webapps/myapp/WEB-INF/lib/gems-gems-jruby-rack-1.1.6-lib-jruby-rack-1.1.6.jar!/jruby/rack/rails_booter.rb:65:in `load_environment'

      at org.jruby.rack.DefaultRackApplicationFactory$4.init(DefaultRackApplicationFactory.java:218)
      at org.jruby.rack.DefaultRackApplicationFactory.getApplication(DefaultRackApplicationFactory.java:58)
      at org.jruby.rack.SharedRackApplicationFactory.init(SharedRackApplicationFactory.java:32)
      ... 11 more
      Caused by: org.jruby.exceptions.RaiseException: (LoadError) no such file to load – C:/myapp/apps/apache-tomcat/webapps/../../../webapps/myapp/WEB-INF/config/boot.class
      at org.jruby.RubyKernel.require(org/jruby/RubyKernel.java:982)
      at RUBY.(root)(C:/myapp/apps/apache-tomcat/webapps/../../../webapps/myapp/WEB-INF/config/boot.rb:1)
      at org.jruby.RubyKernel.require(org/jruby/RubyKernel.java:982)
      at RUBY.(root)(file:/C:/myapp/webapps/myapp/WEB-INF/lib/gems-gems-jruby-rack-1.1.6-lib-jruby-rack-1.1.6.jar!/jruby/rack/rails/environment3.rb:1)
      at RUBY.load_environment(file:/C:/myapp/webapps/myapp/WEB-INF/lib/gems-gems-jruby-rack-1.1.6-lib-jruby-rack-1.1.6.jar!/jruby/rack/rails/environment3.rb:21)
      at RUBY.load_environment(file:/C:/myapp/webapps/myapp/WEB-INF/lib/gems-gems-jruby-rack-1.1.6-lib-jruby-rack-1.1.6.jar!/jruby/rack/rails_booter.rb:65)

      1. boot.rb
        0.0 kB
        Scott Nelson
      2. Gemfile
        2 kB
        Scott Nelson
      3. Gemfile.lock
        4 kB
        Scott Nelson

        Activity

        Hide
        Charles Oliver Nutter added a comment -

        Well I'm confused about why this would have started failing in 1.7. There are no related changes I can see in the relevant code in JRuby (LoadService.java) during the post 1.6.7 dev cycle. I also don't see how it would have ever worked before.

        The logic currently works like this:

        • If require name has no extension, search for both source and ext (.rb, .class, and .dll/.so/.bundle).
        • If require name has a .rb extension, search for source forms (.rb and .class).
        • If require name has a .class extension, search for both source and ext using original name as base (e.g. require 'foo.class' will try 'foo.class.rb' and 'foo.class.class').

        This last bullet does not appear to have changed in a long time, and is the primary reason why this fails.

        There are a couple possible fixes:

        • Treat a require of a .class file as an exact filename; do not search for anything other than that filename.
        • Treat a require of a .class file as a source search; try .class and .rb in that order.

        I'm inclined to do the former, since I believe that most accurately reflects the intent. The .rb => .class search in the bullets above was intended to allow scripts that require .rb files to work unmodified when precompiling. I don't see a use case where you'd want an explicit require of precompiled code to load .rb files.

        HOWEVER...all that said, I still think warbler should be using load instead of require. Using require adds .class entries to "loaded features" which is a visible change, where precompilation is intended to be an invisible feature. I also see no harm in using load; what we want here is basically to take the guts out of a .rb file, stuff them in a .class file, and pretend nothing has changed. That's load, not require.

        I will try to raise this with the warbler folks again.

        Show
        Charles Oliver Nutter added a comment - Well I'm confused about why this would have started failing in 1.7. There are no related changes I can see in the relevant code in JRuby (LoadService.java) during the post 1.6.7 dev cycle. I also don't see how it would have ever worked before. The logic currently works like this: If require name has no extension, search for both source and ext (.rb, .class, and .dll/.so/.bundle). If require name has a .rb extension, search for source forms (.rb and .class). If require name has a .class extension, search for both source and ext using original name as base (e.g. require 'foo.class' will try 'foo.class.rb' and 'foo.class.class'). This last bullet does not appear to have changed in a long time, and is the primary reason why this fails. There are a couple possible fixes: Treat a require of a .class file as an exact filename; do not search for anything other than that filename. Treat a require of a .class file as a source search; try .class and .rb in that order. I'm inclined to do the former, since I believe that most accurately reflects the intent. The .rb => .class search in the bullets above was intended to allow scripts that require .rb files to work unmodified when precompiling. I don't see a use case where you'd want an explicit require of precompiled code to load .rb files. HOWEVER...all that said, I still think warbler should be using load instead of require. Using require adds .class entries to "loaded features" which is a visible change, where precompilation is intended to be an invisible feature. I also see no harm in using load; what we want here is basically to take the guts out of a .rb file, stuff them in a .class file, and pretend nothing has changed. That's load, not require. I will try to raise this with the warbler folks again.
        Hide
        Charles Oliver Nutter added a comment -

        Ok, I said my piece on the Warbler issue.

        I think @kares may have misunderstood what we were asking for, since he mentioned changing a require of boot.rb into a load of boot.rb. Hopefully my explanation will clear things up.

        I will proceed with the require fix.

        Show
        Charles Oliver Nutter added a comment - Ok, I said my piece on the Warbler issue. I think @kares may have misunderstood what we were asking for, since he mentioned changing a require of boot.rb into a load of boot.rb. Hopefully my explanation will clear things up. I will proceed with the require fix.
        Hide
        Charles Oliver Nutter added a comment -

        Fixed as described. Note that this should allow Warbler's current mechanism to function, but I still believe it should use load instead of require.

        commit 7c4238ab08c1df6f238e7b0b79d1bc7f07ebf1d6
        Author: Charles Oliver Nutter <headius@headius.com>
        Date:   Thu Jul 26 17:16:43 2012 -0500
        
            Fix JRUBY-6731
            
            Can't load class files in WARs compiled by warbler using jruby 1.7.0.preview1
            
            When requiring a file with an explicit .class extension, assume
            the .class filename is the only filename to be searched.
            
            Because a require of .class almost always will indicate that the
            user wants to load a specific precompiled Ruby file, there's no
            good reason (I know of) to search for anything else.
            
            The original logic did not work at all; require 'foo.class' would
            search for 'foo.class.rb' and 'foo.class.class' (in that order)
            but never 'foo.class'.
            
            The claim is that this worked in 1.6.7.2, but I could not
            reproduce that with a simple case. The logic involved does not
            appear to have changed in a substantial way since well before
            1.6.7.2, and in any case I believe the fix here is the right way
            for it to work.
        
        :100644 100644 bb7a9d6... 3c38f56... M	src/org/jruby/runtime/load/LoadService.java
        
        Show
        Charles Oliver Nutter added a comment - Fixed as described. Note that this should allow Warbler's current mechanism to function, but I still believe it should use load instead of require. commit 7c4238ab08c1df6f238e7b0b79d1bc7f07ebf1d6 Author: Charles Oliver Nutter <headius@headius.com> Date: Thu Jul 26 17:16:43 2012 -0500 Fix JRUBY-6731 Can't load class files in WARs compiled by warbler using jruby 1.7.0.preview1 When requiring a file with an explicit .class extension, assume the .class filename is the only filename to be searched. Because a require of .class almost always will indicate that the user wants to load a specific precompiled Ruby file, there's no good reason (I know of) to search for anything else. The original logic did not work at all; require 'foo.class' would search for 'foo.class.rb' and 'foo.class.class' (in that order) but never 'foo.class'. The claim is that this worked in 1.6.7.2, but I could not reproduce that with a simple case. The logic involved does not appear to have changed in a substantial way since well before 1.6.7.2, and in any case I believe the fix here is the right way for it to work. :100644 100644 bb7a9d6... 3c38f56... M src/org/jruby/runtime/load/LoadService.java
        Hide
        zaadjis added a comment -

        BTW JRUBY-6348 is probably the same bug (and probably can be closed too).

        I've been using a work-around in production since February (if anybody needs one):

        # place this hack in top of config/warbler.rb
        # HACK https://github.com/jruby/warbler/issues/55 (require vs. load)
        class Warbler::Jar
          def replace_compiled_ruby_files(config, compiled_ruby_files)
            config.excludes += compiled_ruby_files
            compiled_ruby_files.each do |ruby_source|
              files[apply_pathmaps(config, ruby_source, :application)] = StringIO.new("load __FILE__.sub(/\.rb$/, '.class')")
            end
          end
        end
        
        Show
        zaadjis added a comment - BTW JRUBY-6348 is probably the same bug (and probably can be closed too). I've been using a work-around in production since February (if anybody needs one): # place this hack in top of config/warbler.rb # HACK https://github.com/jruby/warbler/issues/55 (require vs. load) class Warbler::Jar def replace_compiled_ruby_files(config, compiled_ruby_files) config.excludes += compiled_ruby_files compiled_ruby_files.each do |ruby_source| files[apply_pathmaps(config, ruby_source, :application)] = StringIO.new("load __FILE__.sub(/\.rb$/, '.class')") end end end
        Hide
        Scott Nelson added a comment -

        Thanks! I've also created a ticket for the Warbler devs here:
        https://github.com/jruby/warbler/issues/102

        Show
        Scott Nelson added a comment - Thanks! I've also created a ticket for the Warbler devs here: https://github.com/jruby/warbler/issues/102

          People

          • Assignee:
            Charles Oliver Nutter
            Reporter:
            Scott Nelson
          • Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: