Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
BEGIN LICENSE BLOCK ***** Version: EPL 1.0/GPL 2.0/LGPL 2.1 The contents of this file are subject to the Eclipse Public License Version 1.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.eclipse.org/legal/epl-v10.html Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. Copyright (C) 2002-2011 JRuby Community Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se> Copyright (C) 2002-2004 Jan Arne Petersen <jpetersen@uni-bonn.de> Copyright (C) 2004 Thomas E Enebo <enebo@acm.org> Copyright (C) 2004-2005 Charles O Nutter <headius@headius.com> Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de> Copyright (C) 2006 Ola Bini <ola@ologix.com> Alternatively, the contents of this file may be used under the terms of either of the GNU General Public License Version 2 or later (the "GPL"), or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), in which case the provisions of the GPL or the LGPL are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of either the GPL or the LGPL, and not to allow others to use your version of this file under the terms of the EPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the GPL or the LGPL. If you do not delete the provisions above, a recipient may use your version of this file under the terms of any one of the EPL, the GPL or the LGPL. END LICENSE BLOCK ***
  
  package org.jruby.runtime.load;
  
  import java.io.File;
  import java.net.URI;
  import java.net.URL;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  import org.jruby.Ruby;
  
  import static org.jruby.util.URLUtil.getPath;

How require works in JRuby

When requiring a name from Ruby, JRuby will first remove any file extension it knows about, thereby making it possible to use this string to see if JRuby has already loaded the name in question. If a .rb extension is specified, JRuby will only try those extensions when searching. If a .so, .o, .dll, or .jar extension is specified, JRuby will only try .so or .jar when searching. Otherwise, JRuby goes through the known suffixes (.rb, .rb.ast.ser, .so, and .jar) and tries to find a library with this name. The process for finding a library follows this order for all searchable extensions:
  1. First, check if the name starts with 'jar:', then the path points to a jar-file resource which is returned.
  2. Second, try searching for the file in the current dir
  3. Then JRuby looks through the load path trying these variants:
    1. See if the current load path entry starts with 'jar:', if so check if this jar-file contains the name
    2. Otherwise JRuby tries to construct a path by combining the entry and the current working directy, and then see if a file with the correct name can be reached from this point.
  4. If all these fail, try to load the name as a resource from classloader resources, using the bare name as well as the load path entries
  5. When we get to this state, the normal JRuby loading has failed. At this stage JRuby tries to load Java native extensions, by following this process:
    1. First it checks that we haven't already found a library. If we found a library of type JarredScript, the method continues.
    2. The first step is translating the name given into a valid Java Extension class name. First it splits the string into each path segment, and then makes all but the last downcased. After this it takes the last entry, removes all underscores and capitalizes each part separated by underscores. It then joins everything together and tacks on a 'Service' at the end. Lastly, it removes all leading dots, to make it a valid Java FWCN.
    3. If the previous library was of type JarredScript, we try to add the jar-file to the classpath
    4. Now JRuby tries to instantiate the class with the name constructed. If this works, we return a ClassExtensionLibrary. Otherwise, the old library is put back in place, if there was one.
  6. When all separate methods have been tried and there was no result, a LoadError will be raised.
  7. Otherwise, the name will be added to the loaded features, and the library loaded

How to make a class that can get required by JRuby

First, decide on what name should be used to require the extension. In this purely hypothetical example, this name will be 'active_record/connection_adapters/jdbc_adapter'. Then create the class name for this require-name, by looking at the guidelines above. Our class should be named active_record.connection_adapters.JdbcAdapterService, and implement one of the library-interfaces. The easiest one is BasicLibraryService, where you define the basicLoad-method, which will get called when your library should be loaded.

The next step is to either put your compiled class on JRuby's classpath, or package the class/es inside a jar-file. To package into a jar-file, we first create the file, then rename it to jdbc_adapter.jar. Then we put this jar-file in the directory active_record/connection_adapters somewhere in JRuby's load path. For example, copying jdbc_adapter.jar into JRUBY_HOME/lib/ruby/site_ruby/1.8/active_record/connection_adapters will make everything work. If you've packaged your extension inside a RubyGem, write a setub.rb-script that copies the jar-file to this place.

Author(s):
jpetersen
 
 public class LoadService {
     private static final Logger LOG = LoggerFactory.getLogger("LoadService");
 
     private final LoadTimer loadTimer;
 
     public enum SuffixType {
         Source, Extension, Both, Neither;
 
         private static final String[] emptySuffixes = { "" };
         // NOTE: always search .rb first for speed
         public static final String[] sourceSuffixes = { ".rb"".class" };
         public static final String[] extensionSuffixes;
         private static final String[] allSuffixes;
 
         static {                // compute based on platform
             if (..load()) {
                 if (.) {
                      = new String[]{".jar"".dll"".jar.rb"};
                 } else if (.) {
                      = new String[]{".jar"".bundle"".jar.rb"};
                 } else {
                      = new String[]{".jar"".so"".jar.rb"};
                 }
             } else {
                  = new String[]{".jar"".jar.rb"};
             }
              = new String[. + .];
             System.arraycopy(, 0, , 0, .);
         }
 
         public String[] getSuffixes() {
             switch (this) {
             case :
                 return ;
             case :
                 return ;
             case :
                 return ;
             case :
                 return ;
             }
             throw new RuntimeException("Unknown SuffixType: " + this);
         }
     }
     protected static final Pattern sourcePattern = Pattern.compile("\\.(?:rb)$");
     protected static final Pattern extensionPattern = Pattern.compile("\\.(?:so|o|dll|bundle|jar)$");
 
     protected RubyArray loadPath;
     protected StringArraySet loadedFeatures;
     protected RubyArray loadedFeaturesDup;
     private final Map<StringStringloadedFeaturesIndex = new ConcurrentHashMap<StringString>();
     protected final Map<StringLibrarybuiltinLibraries = new HashMap<StringLibrary>();
 
     protected final Map<StringJarFilejarFiles = new HashMap<StringJarFile>();
 
     protected final Ruby runtime;
 
     public LoadService(Ruby runtime) {
         this. = runtime;
         if (.) {
              = new TracingLoadTimer();
         } else {
              = new LoadTimer();
         }
     }

    
Called to initialize the load path with a set of optional prepended directories and then the standard set of dirs. This should only be called once, at load time, since it wipes out loaded features.

Parameters:
prependDirectories
 
     public void init(List prependDirectories) {
          = RubyArray.newArray();
 
         String jrubyHome = .getJRubyHome();
          = new StringArraySet();
 
         // add all startup load paths to the list first
         addPaths(prependDirectories);
 
         // add $RUBYLIB paths
         RubyHash env = (RubyHash.getObject().getConstant("ENV");
         RubyString env_rubylib = .newString("RUBYLIB");
         if (env.has_key_p(env_rubylib).isTrue()) {
             String rubylib = env.op_aref(.getCurrentContext(), env_rubylib).toString();
             String[] paths = rubylib.split(.);
             addPaths(paths);
         }
 
         // wrap in try/catch for security exceptions in an applet
         try {
             if (jrubyHome != null) {
                 // siteDir has to come first, because rubygems insert paths after it
                 // and we must to prefer Gems to rubyLibDir/rubySharedLibDir (same as MRI)
                 addPath(RbConfigLibrary.getSiteDir());
                 // if vendorDirGeneral is different than siteDirGeneral,
                 // add vendorDir, too
                 // adding {vendor,site}{Lib,Arch}Dir dirs is not necessary,
                 // since they should be the same as {vendor,site}Dir
                 if (!RbConfigLibrary.isSiteVendorSame()) {
                     addPath(RbConfigLibrary.getVendorDir());
                 }
                 String rubygemsDir = RbConfigLibrary.getRubygemsDir();
                 if (rubygemsDir != null) {
                     addPath(rubygemsDir);
                 }
                 addPath(RbConfigLibrary.getRubySharedLibDir());
                 // if 2.0, we append 1.9 libs; our copy of 2.0 only has diffs right now
                 if (.is2_0()) {
                     addPath(RbConfigLibrary.getRubyLibDirFor("2.0"));
                 }
                 addPath(RbConfigLibrary.getRubyLibDir());
             }
 
         } catch(SecurityException ignore) {}
 
         // "." dir is used for relative path loads from a given file, as in require '../foo/bar'
         if (!.is1_9()) {
             addPath(".");
         }
     }

    
Add additional directories to the load path.

Parameters:
additionalDirectories a List of additional dirs to append to the load path
 
     public void addPaths(List<StringadditionalDirectories) {
         for (String dir : additionalDirectories) {
             addPath(dir);
         }
     }

    
Add additional directories to the load path.

Parameters:
additionalDirectories an array of additional dirs to append to the load path
 
     public void addPaths(String... additionalDirectories) {
         for (String dir : additionalDirectories) {
             addPath(dir);
         }
     }
     
     protected boolean isFeatureInIndex(String shortName) {
         return .containsKey(shortName);
     }
 
     @Deprecated
     protected void addLoadedFeature(String name) {
         addLoadedFeature(namename);
     }
 
     protected void addLoadedFeature(String shortNameString name) {
         .append(RubyString.newString(name));
         
         addFeatureToIndex(shortNamename);
     }
     
     protected void addFeatureToIndex(String shortNameString name) {
         .put(shortNamename);
     }
 
     protected void addPath(String path) {
         // Empty paths do not need to be added
         if (path == null || path.length() == 0) return;
 
         synchronized() {
             .append(.newString(path.replace('\\''/')));
         }
     }
 
     public void load(String fileboolean wrap) {
         long startTime = .startLoad(file);
         try {
             if(!.getProfile().allowLoad(file)) {
                 throw .newLoadError("no such file to load -- " + filefile);
             }
 
             SearchState state = new SearchState(file);
             state.prepareLoadSearch(file);
 
             Library library = findBuiltinLibrary(statestate.searchFilestate.suffixType);
             if (library == nulllibrary = findLibraryWithoutCWD(statestate.searchFilestate.suffixType);
 
             if (library == null) {
                 library = findLibraryWithClassloaders(statestate.searchFilestate.suffixType);
                 if (library == null) {
                     throw .newLoadError("no such file to load -- " + filefile);
                 }
             }
             try {
                 library.load(wrap);
             } catch (IOException e) {
                 if (.getDebug().isTrue()) e.printStackTrace(.getErr());
                 throw newLoadErrorFromThrowable(filee);
             }
         } finally {
             .endLoad(filestartTime);
         }
     }
 
     public void loadFromClassLoader(ClassLoader classLoaderString fileboolean wrap) {
         long startTime = .startLoad("classloader:" + file);
         try {
             SearchState state = new SearchState(file);
             state.prepareLoadSearch(file);
 
             Library library = null;
             LoadServiceResource resource = getClassPathResource(classLoaderfile);
             if (resource != null) {
                 state.loadName = resolveLoadName(resourcefile);
                 library = createLibrary(stateresource);
             }
             if (library == null) {
                 throw .newLoadError("no such file to load -- " + file);
             }
             try {
                 library.load(wrap);
             } catch (IOException e) {
                 if (.getDebug().isTrue()) e.printStackTrace(.getErr());
                 throw newLoadErrorFromThrowable(filee);
             }
         } finally {
             .endLoad("classloader:" + filestartTime);
         }
     }
 
     public SearchState findFileForLoad(String file) {
         if (.) {
             file = file.replace('\\''/');
         }
         // Even if we don't support .so, some stdlib require .so directly.
         // Replace it with .jar to look for a java extension
         // JRUBY-5033: The ExtensionSearcher will locate C exts, too, this way.
         if (file.endsWith(".so")) {
             file = file.replaceAll(".so$"".jar");
         }
 
         SearchState state = new SearchState(file);
         state.prepareRequireSearch(file);
 
         for (LoadSearcher searcher : ) {
             if (searcher.shouldTrySearch(state)) {
                 if (!searcher.trySearch(state)) {
                     return null;
                 }
             }
         }
 
         return state;
     }
 
     public boolean require(String requireName) {
         return requireCommon(requireNametrue) == .;
     }
 
     public boolean autoloadRequire(String requireName) {
         return requireCommon(requireNamefalse) != .;
     }
 
     private enum RequireState {
         LOADED, ALREADY_LOADED, CIRCULAR
     };
 
     private RequireState requireCommon(String requireNameboolean circularRequireWarning) {
         // check for requiredName without extension.
         if (featureAlreadyLoaded(requireName)) {
             return .;
         }
 
         if (!.lock(requireName)) {
             if (circularRequireWarning && .isVerbose() && .is1_9()) {
                 warnCircularRequire(requireName);
             }
             return .;
         }
         try {
             if (!.getProfile().allowRequire(requireName)) {
                 throw .newLoadError("no such file to load -- " + requireNamerequireName);
             }
 
             // check for requiredName again now that we're locked
             if (featureAlreadyLoaded(requireName)) {
                 return .;
             }
 
             // numbers from loadTimer does not include lock waiting time.
             long startTime = .startLoad(requireName);
             try {
                 boolean loaded = smartLoadInternal(requireName);
                 return loaded ? . : .;
             } finally {
                 .endLoad(requireNamestartTime);
             }
         } finally {
             .unlock(requireName);
         }
     }
     
     protected final RequireLocks requireLocks = new RequireLocks();
 
     private class RequireLocks {
         private final Map<StringReentrantLockpool;
         // global lock for require must be fair
         private final ReentrantLock globalLock;
 
         private RequireLocks() {
             this. = new HashMap<StringReentrantLock>();
             this. = new ReentrantLock(true);
         }

        
Get exclusive lock for the specified requireName. Acquire sync object for the requireName from the pool, then try to lock it. NOTE: This lock is not fair for now.

Parameters:
requireName just a name for the lock.
Returns:
If the sync object already locked by current thread, it just returns false without getting a lock. Otherwise true.
 
         private boolean lock(String requireName) {
             ReentrantLock lock;
 
             while (true) {
                 synchronized () {
                     lock = .get(requireName);
                     if (lock == null) {
                         if (.getInstanceConfig().isGlobalRequireLock()) {
                             lock = ;
                         } else {
                             lock = new ReentrantLock();
                         }
                         .put(requireNamelock);
                     } else if (lock.isHeldByCurrentThread()) {
                         return false;
                     }
                 }
 
                 lock.lock();
 
                 // repeat until locked object still in requireLocks.
                 synchronized () {
                     if (.get(requireName) == lock) {
                         // the object is locked && the lock is in the pool
                         return true;
                     }
                     // go next try
                     lock.unlock();
                 }
             }
         }

        
Unlock the lock for the specified requireName.

Parameters:
requireName name of the lock to be unlocked.
 
         private void unlock(String requireName) {
             synchronized () {
                 ReentrantLock lock = .get(requireName);
                 if (lock != null) {
                     assert lock.isHeldByCurrentThread();
                     lock.unlock();
                     .remove(requireName);
                 }
             }
         }
     }
 
     protected void warnCircularRequire(String requireName) {
         .getWarnings().warn("loading in progress, circular require considered harmful - " + requireName);
         // it's a hack for c:rb_backtrace impl.
         // We should introduce new method to Ruby.TraceType when rb_backtrace is widely used not only for this purpose.
         RaiseException ex = new RaiseException(.getRuntimeError(), nullfalse);
         // rb_backtrace dumps to stderr directly.
         ..print(trace.replaceFirst("[^\n]*\n"""));
     }

    
This method did require the specified file without getting a lock. Now we offer safe version only. Use require(java.lang.String) instead.
 
     @Deprecated
     public boolean smartLoad(String file) {
         return require(file);
     }
 
     private boolean smartLoadInternal(String file) {
         checkEmptyLoad(file);
         SearchState state = findFileForLoad(file);
         if (state == null) {
             return false;
         }
         if (state.library == null) {
             throw .newLoadError("no such file to load -- " + state.searchFilestate.searchFile);
         }
 
         // check with long name
         if (featureAlreadyLoaded(state.loadName)) {
             return false;
         }
 
         boolean loaded = tryLoadingLibraryOrScript(state);
         if (loaded) {
             addLoadedFeature(filestate.loadName);
         }
         return loaded;
     }
 
     private static class LoadTimer {
         public long startLoad(String file) { return 0L; }
         public void endLoad(String filelong startTime) {}
     }
 
     private static class TracingLoadTimer extends LoadTimer {
         private final AtomicInteger indent = new AtomicInteger(0);
         private String getIndentString() {
             StringBuilder buf = new StringBuilder();
             int i = .get();
             for (int j = 0; j < ij++) {
                 buf.append("  ");
             }
             return buf.toString();
         }
         @Override
         public long startLoad(String file) {
             .incrementAndGet();
             .info(getIndentString() + "-> " + file);
             return System.currentTimeMillis();
         }
         @Override
         public void endLoad(String filelong startTime) {
             .info(getIndentString() + "<- " + file + " - "
                     + (System.currentTimeMillis() - startTime) + "ms");
             .decrementAndGet();
         }
     }

    
Load the org.jruby.runtime.load.Library implementation specified by className. The purpose of using this method is to avoid having static references to the given library class, thereby avoiding the additional classloading when the library is not in use.

Parameters:
runtime The runtime in which to load
libraryName The name of the library, to use for error messages
className The class of the library
classLoader The classloader to use to load it
wrap Whether to wrap top-level in an anonymous module
 
     public static void reflectedLoad(Ruby runtimeString libraryNameString classNameClassLoader classLoaderboolean wrap) {
         try {
             if (classLoader == null && Ruby.isSecurityRestricted()) {
                 classLoader = runtime.getInstanceConfig().getLoader();
             }
 
             Object libObject = classLoader.loadClass(className).newInstance();
             if (libObject instanceof Library) {
                 Library library = (Library)libObject;
                 library.load(runtimefalse);
             } else if (libObject instanceof BasicLibraryService) {
                 BasicLibraryService service = (BasicLibraryService)libObject;
                 service.basicLoad(runtime);
             } else {
                 // invalid type of library, raise error
                 throw runtime.newLoadError("library `" + libraryName + "' is not of type Library or BasicLibraryService"libraryName);
             }
         } catch (RaiseException re) {
             throw re;
         } catch (Throwable e) {
             if (runtime.getDebug().isTrue()) e.printStackTrace();
             throw runtime.newLoadError("library `" + libraryName + "' could not be loaded: " + elibraryName);
         }
     }
 
     public IRubyObject getLoadPath() {
         return ;
     }
 
     public IRubyObject getLoadedFeatures() {
         return ;
     }
 
     public void addBuiltinLibrary(String nameLibrary library) {
         .put(namelibrary);
     }
 
     public void removeBuiltinLibrary(String name) {
         .remove(name);
     }
 
     public void removeInternalLoadedFeature(String name) {
         RubyString nameRubyString = .newString(name);
         .delete(.getCurrentContext(), nameRubyString.);
     }
     
     private boolean isFeaturesIndexUpToDate() {
         // disable tracing during index check
         .getCurrentContext().preTrace();
         try {
             return  != null && .eql();
         } finally {
             .getCurrentContext().postTrace();
         }
     }
 
     protected boolean featureAlreadyLoaded(String name) {
         if (.containsString(name)) return true;
         
         // Bail if our features index fell out of date.
         if (!isFeaturesIndexUpToDate()) { 
             .clear();
             return false;
         }
         
         return isFeatureInIndex(name);
     }
 
     protected boolean isJarfileLibrary(SearchState statefinal String file) {
         return state.library instanceof JarredScript && file.endsWith(".jar");
     }
 
     protected void reraiseRaiseExceptions(Throwable ethrows RaiseException {
         if (e instanceof RaiseException) {
             throw (RaiseExceptione;
         }
     }
 
     public interface LoadSearcher {
        

Parameters:
state
Returns:
true if trySearch should be called.
 
         public boolean shouldTrySearch(SearchState state);

        

Parameters:
state
Returns:
false if loadSearch must be bail-out.
 
         public boolean trySearch(SearchState state);
     }
 
     public class BailoutSearcher implements LoadSearcher {
         public boolean shouldTrySearch(SearchState state) {
             return state.library == null;
         }
 
         protected boolean trySearch(String fileSuffixType suffixType) {
             for (String suffix : suffixType.getSuffixes()) {
                 String searchName = file + suffix;
                 if (featureAlreadyLoaded(searchName)) {
                     return false;
                 }
             }
             return true;
         }
 
         public boolean trySearch(SearchState state) {
             return trySearch(state.searchFilestate.suffixType);
         }
     }
 
     public class SourceBailoutSearcher extends BailoutSearcher {
         public boolean shouldTrySearch(SearchState state) {
             // JRUBY-5032: Load extension files if they are required
             // explicitly, and even if an rb file of the same name
             // has already been loaded (effectively skipping the search for a source file).
             return !.matcher(state.loadName).find();
         }
 
         // According to Rubyspec, source files should be loaded even if an equally named
         // extension is loaded already. So we use the bailout search twice, once only
         // for source files and once for whatever suffix type the state determines
         public boolean trySearch(SearchState state) {
             return super.trySearch(state.searchFile.);
         }
     }
 
     public class NormalSearcher implements LoadSearcher {
         public boolean shouldTrySearch(SearchState state) {
             return state.library == null;
         }
 
         public boolean trySearch(SearchState state) {
             state.library = findLibraryWithoutCWD(statestate.searchFilestate.suffixType);
             return true;
         }
     }
 
     public class ClassLoaderSearcher implements LoadSearcher {
         public boolean shouldTrySearch(SearchState state) {
             return state.library == null;
         }
 
         public boolean trySearch(SearchState state) {
             state.library = findLibraryWithClassloaders(statestate.searchFilestate.suffixType);
             return true;
         }
     }
 
     public class ExtensionSearcher implements LoadSearcher {
         public boolean shouldTrySearch(SearchState state) {
             return (state.library == null || state.library instanceof JarredScript) && !state.searchFile.equalsIgnoreCase("");
         }
 
         public boolean trySearch(SearchState state) {
             debugLogTry("jarWithExtension"state.searchFile);
             
             // This code exploits the fact that all .jar files will be found for the JarredScript feature.
             // This is where the basic extension mechanism gets fixed
             Library oldLibrary = state.library;
 
             // Create package name, by splitting on / and joining all but the last elements with a ".", and downcasing them.
             String[] all = state.searchFile.split("/");
 
             StringBuilder finName = new StringBuilder();
             for(int i=0, j=(all.length-1); i<ji++) {
                 finName.append(all[i].toLowerCase()).append(".");
             }
 
             try {
                 // Make the class name look nice, by splitting on _ and capitalize each segment, then joining
                 // the, together without anything separating them, and last put on "Service" at the end.
                 String[] last = all[all.length-1].split("_");
                 for(int i=0, j=last.lengthi<ji++) {
                     if ("".equals(last[i])) break;
                     finName.append(Character.toUpperCase(last[i].charAt(0))).append(last[i].substring(1));
                 }
                 finName.append("Service");
 
                 // We don't want a package name beginning with dots, so we remove them
                 String className = finName.toString().replaceAll("^\\.*","");
 
                 // If there is a jar-file with the required name, we add this to the class path.
                 if(state.library instanceof JarredScript) {
                     // It's _really_ expensive to check that the class actually exists in the Jar, so
                     // we don't do that now.
                     URL jarURL = ((JarredScript)state.library).getResource().getURL();
                     .getJRubyClassLoader().addURL(jarURL);
                     debugLogFound("jarWithoutExtension"jarURL.toString());
                 }
 
                 // quietly try to load the class
                 Class theClass = .getJavaSupport().loadJavaClass(className);
                 state.library = new ClassExtensionLibrary(className + ".java"theClass);
                 debugLogFound("jarWithExtension"className);
             } catch (ClassNotFoundException cnfe) {
                 if (.isDebug()) cnfe.printStackTrace();
                 // we ignore this and assume the jar is not an extension
             } catch (UnsupportedClassVersionError ucve) {
                 if (.isDebug()) ucve.printStackTrace();
                 throw .newLoadError("JRuby ext built for wrong Java version in `" + finName + "': " + ucvefinName.toString());
             } catch (IOException ioe) {
                 if (.isDebug()) ioe.printStackTrace();
                 throw .newLoadError("IOException loading extension `" + finName + "`: " + ioefinName.toString());
             } catch (Exception e) {
                 if (.isDebug()) e.printStackTrace();
                 throw .newLoadError("Exception loading extension `" + finName + "`: " + efinName.toString());
             }
 
             // If there was a good library before, we go back to that
             if(state.library == null && oldLibrary != null) {
                 state.library = oldLibrary;
             }
             return true;
         }
     }
 
     public class ScriptClassSearcher implements LoadSearcher {
         public class ScriptClassLibrary implements Library {
             private Script script;
 
             public ScriptClassLibrary(Script script) {
                 this. = script;
             }
 
             public void load(Ruby runtimeboolean wrap) {
                 runtime.loadScript(wrap);
             }
         }
 
         public boolean shouldTrySearch(SearchState state) {
             return state.library == null;
         }
 
         public boolean trySearch(SearchState statethrows RaiseException {
             // no library or extension found, try to load directly as a class
             Script script;
             String className = buildClassName(state.searchFile);
             int lastSlashIndex = className.lastIndexOf('/');
             if (lastSlashIndex > -1 && lastSlashIndex < className.length() - 1 && !Character.isJavaIdentifierStart(className.charAt(lastSlashIndex + 1))) {
                 if (lastSlashIndex == -1) {
                     className = "_" + className;
                 } else {
                     className = className.substring(0, lastSlashIndex + 1) + "_" + className.substring(lastSlashIndex + 1);
                 }
             }
             className = className.replace('/''.');
             try {
                 Class scriptClass = Class.forName(className);
                 script = (ScriptscriptClass.newInstance();
             } catch (Exception cnfe) {
                 return true;
             }
             state.library = new ScriptClassLibrary(script);
             return true;
         }
     }
 
     public static class SearchState {
         public Library library;
         public String loadName;
         public SuffixType suffixType;
         public String searchFile;
 
         public SearchState(String file) {
              = file;
         }
 
         public void prepareRequireSearch(final String file) {
             // if an extension is specified, try more targetted searches
             if (file.lastIndexOf('.') > file.lastIndexOf('/')) {
                 Matcher matcher = null;
                 if ((matcher = .matcher(file)).find()) {
                     // source extensions
                      = .;
 
                     // trim extension to try other options
                      = file.substring(0, matcher.start());
                 } else if ((matcher = .matcher(file)).find()) {
                     // extension extensions
                      = .;
 
                     // trim extension to try other options
                      = file.substring(0, matcher.start());
                 } else if (file.endsWith(".class")) {
                     // For JRUBY-6731, treat require 'foo.class' as no other filename than 'foo.class'.
                      = .;
                      = file;
                 } else {
                     // unknown extension, fall back to search with extensions
                      = .;
                      = file;
                 }
             } else {
                 // try all extensions
                  = .;
                  = file;
             }
         }
 
         public void prepareLoadSearch(final String file) {
             // if a source extension is specified, try all source extensions
             if (file.lastIndexOf('.') > file.lastIndexOf('/')) {
                 Matcher matcher = null;
                 if ((matcher = .matcher(file)).find()) {
                     // source extensions
                      = .;
 
                     // trim extension to try other options
                      = file.substring(0, matcher.start());
                 } else {
                     // unknown extension, fall back to exact search
                      = .;
                      = file;
                 }
             } else {
                 // try only literal search
                  = .;
                  = file;
             }
         }
 
         @Override
         public String toString() {
             StringBuilder sb = new StringBuilder();
             sb.append(this.getClass().getName()).append(": ");
             sb.append("library=").append(.toString());
             sb.append(", loadName=").append();
             sb.append(", suffixType=").append(.toString());
             sb.append(", searchFile=").append();
             return sb.toString();
         }
     }
 
     protected boolean tryLoadingLibraryOrScript(Ruby runtimeSearchState state) {
         // attempt to load the found library
         try {
             state.library.load(runtimefalse);
             return true;
         } catch (MainExitException mee) {
             // allow MainExitException to propagate out for exec and friends
             throw mee;
         } catch (Throwable e) {
             if(isJarfileLibrary(statestate.searchFile)) {
                 return true;
             }
             reraiseRaiseExceptions(e);
 
             if(runtime.getDebug().isTrue()) e.printStackTrace(runtime.getErr());
 
             RaiseException re = newLoadErrorFromThrowable(runtimestate.searchFilee);
             re.initCause(e);
             throw re;
         }
     }
 
     private static RaiseException newLoadErrorFromThrowable(Ruby runtimeString fileThrowable t) {
         if (.t.printStackTrace();
         
         return runtime.newLoadError(String.format("load error: %s -- %s: %s"filet.getClass().getName(), t.getMessage()), file);
     }
 
     // Using the BailoutSearch twice, once only for source files and once for state suffixes,
     // in order to adhere to Rubyspec
     protected final List<LoadSearchersearchers = new ArrayList<LoadSearcher>();
     {
         .add(new SourceBailoutSearcher());
         .add(new NormalSearcher());
         .add(new ClassLoaderSearcher());
         .add(new BailoutSearcher());
         .add(new ExtensionSearcher());
         .add(new ScriptClassSearcher());
     }
 
     protected String buildClassName(String className) {
         // Remove any relative prefix, e.g. "./foo/bar" becomes "foo/bar".
         className = className.replaceFirst("^\\.\\/""");
         if (className.lastIndexOf(".") != -1) {
            className = className.substring(0, className.lastIndexOf("."));
        }
        className = className.replace("-""_minus_").replace('.''_');
        return className;
    }
    protected void checkEmptyLoad(String filethrows RaiseException {
        if (file.equals("")) {
            throw .newLoadError("no such file to load -- " + filefile);
        }
    }
    protected void debugLogTry(String whatString msg) {
            .info"trying " + what + ": " + msg );
        }
    }
    protected void debugLogFound(String whatString msg) {
            .info"found " + what + ": " + msg );
        }
    }
    protected void debugLogFoundLoadServiceResource resource ) {
            String resourceUrl;
            try {
                resourceUrl = resource.getURL().toString();
            } catch (IOException e) {
                resourceUrl = e.getMessage();
            }
            .info"found: " + resourceUrl );
        }
    }
    protected Library findBuiltinLibrary(SearchState stateString baseNameSuffixType suffixType) {
        for (String suffix : suffixType.getSuffixes()) {
            String namePlusSuffix = baseName + suffix;
            debugLogTry"builtinLib",  namePlusSuffix );
            if (.containsKey(namePlusSuffix)) {
                state.loadName = namePlusSuffix;
                Library lib = .get(namePlusSuffix);
                debugLogFound"builtinLib"namePlusSuffix );
                return lib;
            }
        }
        return null;
    }
    protected Library findLibraryWithoutCWD(SearchState stateString baseNameSuffixType suffixType) {
        Library library = null;
        switch (suffixType) {
        case :
            library = findBuiltinLibrary(statebaseName.);
            if (library == nulllibrary = createLibrary(statetryResourceFromJarURL(statebaseName.));
            if (library == nulllibrary = createLibrary(statetryResourceFromLoadPathOrURL(statebaseName.));
            // If we fail to find as a normal Ruby script, we try to find as an extension,
            // checking for a builtin first.
            if (library == nulllibrary = findBuiltinLibrary(statebaseName.);
            if (library == nulllibrary = createLibrary(statetryResourceFromJarURL(statebaseName.));
            if (library == nulllibrary = createLibrary(statetryResourceFromLoadPathOrURL(statebaseName.));
            break;
        case :
        case :
            // Check for a builtin first.
            library = findBuiltinLibrary(statebaseNamesuffixType);
            if (library == nulllibrary = createLibrary(statetryResourceFromJarURL(statebaseNamesuffixType));
            if (library == nulllibrary = createLibrary(statetryResourceFromLoadPathOrURL(statebaseNamesuffixType));
            break;
        case :
            library = createLibrary(statetryResourceFromJarURL(statebaseName.));
            if (library == nulllibrary = createLibrary(statetryResourceFromLoadPathOrURL(statebaseName.));
            break;
        }
        return library;
    }
    protected Library findLibraryWithClassloaders(SearchState stateString baseNameSuffixType suffixType) {
        for (String suffix : suffixType.getSuffixes()) {
            String file = baseName + suffix;
            LoadServiceResource resource = findFileInClasspath(file);
            if (resource != null) {
                state.loadName = resolveLoadName(resourcefile);
                return createLibrary(stateresource);
            }
        }
        return null;
    }
    protected Library createLibrary(SearchState stateLoadServiceResource resource) {
        if (resource == null) {
            return null;
        }
        String file = resource.getName();
        String location = state.loadName;
        if (file.endsWith(".so") || file.endsWith(".dll") || file.endsWith(".bundle")) {
            if (.getInstanceConfig().isCextEnabled()) {
                return new CExtension(resource);
            } else {
                throw .newLoadError("C extensions are disabled, can't load `" + resource.getName() + "'"resource.getName());
            }
        } else if (file.endsWith(".jar")) {
            return new JarredScript(resource);
        } else if (file.endsWith(".class")) {
            return new JavaCompiledScript(resource);
        } else {
            return new ExternalScript(resourcelocation);
        }
    }
    protected LoadServiceResource tryResourceFromCWD(SearchState stateString baseName,SuffixType suffixTypethrows RaiseException {