1 33 package org.jruby.runtime.load; 34 35 import java.io.FileNotFoundException ; 36 import java.io.IOException ; 37 import java.net.MalformedURLException ; 38 import java.net.URL ; 39 import java.security.AccessControlException ; 40 import java.util.Collections ; 41 import java.util.HashMap ; 42 import java.util.HashSet ; 43 import java.util.Iterator ; 44 import java.util.List ; 45 import java.util.Map ; 46 import java.util.Set ; 47 import java.util.jar.JarFile ; 48 import java.util.regex.Matcher ; 49 import java.util.regex.Pattern ; 50 51 import org.jruby.Ruby; 52 import org.jruby.RubyArray; 53 import org.jruby.exceptions.RaiseException; 54 import org.jruby.runtime.Constants; 55 import org.jruby.runtime.builtin.IRubyObject; 56 import org.jruby.util.BuiltinScript; 57 import org.jruby.util.JRubyFile; 58 import org.jruby.util.PreparsedScript; 59 60 119 public class LoadService { 120 private static final String JRUBY_BUILTIN_SUFFIX = ".rb"; 121 122 private static final String [] sourceSuffixes = { ".rb", ".rb.ast.ser" }; 123 private static final String [] extensionSuffixes = { ".so", ".jar" }; 124 private static final String [] allSuffixes = { ".rb", ".rb.ast.ser", ".so", ".jar" }; 125 private static final Pattern sourcePattern = Pattern.compile("^(.*)\\.(rb|rb\\.ast\\.ser)$"); 126 private static final Pattern extensionPattern = Pattern.compile("^(.*)\\.(so|o|dll|jar)$"); 127 128 private final RubyArray loadPath; 129 private final RubyArray loadedFeatures; 130 private final Set loadedFeaturesInternal = Collections.synchronizedSet(new HashSet ()); 131 private final Set firstLineLoadedFeatures = Collections.synchronizedSet(new HashSet ()); 132 private final Map builtinLibraries = new HashMap (); 133 134 private final Map jarFiles = new HashMap (); 135 136 private final Map autoloadMap = new HashMap (); 137 138 private final Ruby runtime; 139 140 public LoadService(Ruby runtime) { 141 this.runtime = runtime; 142 loadPath = RubyArray.newArray(runtime); 143 loadedFeatures = RubyArray.newArray(runtime); 144 } 145 146 public void init(List additionalDirectories) { 147 for (Iterator iter = additionalDirectories.iterator(); iter.hasNext();) { 149 addPath((String ) iter.next()); 150 } 151 152 try { 154 String jrubyHome = runtime.getJRubyHome(); 155 if (jrubyHome != null) { 156 char sep = '/'; 157 String rubyDir = jrubyHome + sep + "lib" + sep + "ruby" + sep; 158 159 addPath(rubyDir + "site_ruby" + sep + Constants.RUBY_MAJOR_VERSION); 160 addPath(rubyDir + "site_ruby"); 161 addPath(rubyDir + Constants.RUBY_MAJOR_VERSION); 162 addPath(rubyDir + Constants.RUBY_MAJOR_VERSION + sep + "java"); 163 164 addPath("lib" + sep + "ruby" + sep + Constants.RUBY_MAJOR_VERSION); 167 } 168 } catch (AccessControlException accessEx) { 169 } 171 172 if (runtime.getSafeLevel() == 0) { 174 addPath("."); 175 } 176 } 177 178 private void addPath(String path) { 179 synchronized(loadPath) { 180 loadPath.add(runtime.newString(path.replace('\\', '/'))); 181 } 182 } 183 184 public void load(String file) { 185 if(!runtime.getProfile().allowLoad(file)) { 186 throw runtime.newLoadError("No such file to load -- " + file); 187 } 188 189 Library library = null; 190 191 library = findLibrary(file); 192 193 if (library == null) { 194 library = findLibraryWithClassloaders(file); 195 if (library == null) { 196 throw runtime.newLoadError("No such file to load -- " + file); 197 } 198 } 199 try { 200 library.load(runtime); 201 } catch (IOException e) { 202 throw runtime.newLoadError("IO error -- " + file); 203 } 204 } 205 206 public boolean smartLoad(String file) { 207 if(firstLineLoadedFeatures.contains(file)) { 208 return false; 209 } 210 Library library = null; 211 String loadName = file; 212 String [] extensionsToSearch = null; 213 214 if (file.lastIndexOf('.') > file.lastIndexOf('/')) { 216 Matcher matcher = null; 217 if ((matcher = sourcePattern.matcher(file)).matches()) { 218 extensionsToSearch = sourceSuffixes; 220 221 file = matcher.group(1); 223 } else if ((matcher = extensionPattern.matcher(file)).matches()) { 224 extensionsToSearch = extensionSuffixes; 226 227 file = matcher.group(1); 229 } else { 230 throw runtime.newLoadError("no such file to load -- " + file); 232 } 233 } else { 234 extensionsToSearch = allSuffixes; 236 } 237 238 for (int i = 0; i < extensionsToSearch.length; i++) { 240 library = findLibrary(file + extensionsToSearch[i]); 241 if (library != null) { 242 loadName = file + extensionsToSearch[i]; 243 break; 244 } 245 } 246 247 if (library == null) { 249 for (int i = 0; i < extensionsToSearch.length; i++) { 250 library = findLibraryWithClassloaders(file + extensionsToSearch[i]); 251 if (library != null) { 252 loadName = file + extensionsToSearch[i]; 253 break; 254 } 255 } 256 } 257 258 library = tryLoadExtension(library,file); 259 260 if (library == null) { 262 throw runtime.newLoadError("no such file to load -- " + file); 263 } 264 265 if (loadedFeaturesInternal.contains(loadName)) { 266 return false; 267 } 268 269 try { 271 loadedFeaturesInternal.add(loadName); 272 firstLineLoadedFeatures.add(file); 273 synchronized(loadedFeatures) { 274 loadedFeatures.add(runtime.newString(loadName)); 275 } 276 277 library.load(runtime); 278 return true; 279 } catch (Exception e) { 280 loadedFeaturesInternal.remove(loadName); 281 firstLineLoadedFeatures.remove(file); 282 synchronized(loadedFeatures) { 283 loadedFeatures.remove(runtime.newString(loadName)); 284 } 285 if (e instanceof RaiseException) throw (RaiseException) e; 286 287 if(runtime.getDebug().isTrue()) e.printStackTrace(); 288 289 RaiseException re = runtime.newLoadError("IO error -- " + file); 290 re.initCause(e); 291 throw re; 292 } 293 } 294 295 public boolean require(String file) { 296 if(!runtime.getProfile().allowRequire(file)) { 297 throw runtime.newLoadError("No such file to load -- " + file); 298 } 299 return smartLoad(file); 300 } 301 302 public IRubyObject getLoadPath() { 303 return loadPath; 304 } 305 306 public IRubyObject getLoadedFeatures() { 307 return loadedFeatures; 308 } 309 310 public boolean isAutoloadDefined(String name) { 311 return autoloadMap.containsKey(name); 312 } 313 314 public IAutoloadMethod autoloadFor(String name) { 315 return (IAutoloadMethod)autoloadMap.get(name); 316 } 317 318 public IRubyObject autoload(String name) { 319 IAutoloadMethod loadMethod = (IAutoloadMethod)autoloadMap.remove(name); 320 if (loadMethod != null) { 321 return loadMethod.load(runtime, name); 322 } 323 return null; 324 } 325 326 public void addAutoload(String name, IAutoloadMethod loadMethod) { 327 autoloadMap.put(name, loadMethod); 328 } 329 330 public void registerBuiltin(String name, Library library) { 331 builtinLibraries.put(name, library); 332 } 333 334 public void registerRubyBuiltin(String libraryName) { 335 registerBuiltin(libraryName + JRUBY_BUILTIN_SUFFIX, new BuiltinScript(libraryName)); 336 } 337 338 private Library findLibrary(String file) { 339 if (builtinLibraries.containsKey(file)) { 340 return (Library) builtinLibraries.get(file); 341 } 342 343 LoadServiceResource resource = findFile(file); 344 if (resource == null) { 345 return null; 346 } 347 348 if (file.endsWith(".jar")) { 349 return new JarredScript(resource); 350 } else if (file.endsWith(".rb.ast.ser")) { 351 return new PreparsedScript(resource); 352 } else { 353 return new ExternalScript(resource, file); 354 } 355 } 356 357 private Library findLibraryWithClassloaders(String file) { 358 LoadServiceResource resource = findFileInClasspath(file); 359 if (resource == null) { 360 return null; 361 } 362 363 if (file.endsWith(".jar")) { 364 return new JarredScript(resource); 365 } else if (file.endsWith(".rb.ast.ser")) { 366 return new PreparsedScript(resource); 367 } else { 368 return new ExternalScript(resource, file); 369 } 370 } 371 372 380 private LoadServiceResource findFile(String name) { 381 if (name.startsWith("jar:")) { 383 try { 384 return new LoadServiceResource(new URL (name), name); 385 } catch (MalformedURLException e) { 386 throw runtime.newIOErrorFromException(e); 387 } 388 } 389 390 try { 392 JRubyFile file = JRubyFile.create(runtime.getCurrentDirectory(),name); 393 if(file.isFile() && file.isAbsolute()) { 394 try { 395 return new LoadServiceResource(file.toURL(),name); 396 } catch (MalformedURLException e) { 397 throw runtime.newIOErrorFromException(e); 398 } 399 } 400 } catch (AccessControlException accessEx) { 401 } catch (IllegalArgumentException illArgEx) { 403 } 404 405 for (Iterator pathIter = loadPath.getList().iterator(); pathIter.hasNext();) { 406 String entry = pathIter.next().toString(); 407 if (entry.startsWith("jar:")) { 408 JarFile current = (JarFile )jarFiles.get(entry); 409 if(null == current) { 410 try { 411 current = new JarFile (entry.substring(4)); 412 jarFiles.put(entry,current); 413 } catch (FileNotFoundException ignored) { 414 } catch (IOException e) { 415 throw runtime.newIOErrorFromException(e); 416 } 417 } 418 if (current.getJarEntry(name) != null) { 419 try { 420 return new LoadServiceResource(new URL (entry + name), entry + name); 421 } catch (MalformedURLException e) { 422 throw runtime.newIOErrorFromException(e); 423 } 424 } 425 } 426 427 try { 428 JRubyFile current = JRubyFile.create(JRubyFile.create(runtime.getCurrentDirectory(),entry).getAbsolutePath(), name); 429 if (current.isFile()) { 430 try { 431 return new LoadServiceResource(current.toURL(), current.getPath()); 432 } catch (MalformedURLException e) { 433 throw runtime.newIOErrorFromException(e); 434 } 435 } 436 } catch (AccessControlException accessEx) { 437 } catch (IllegalArgumentException illArgEx) { 439 } 441 } 442 443 return null; 444 } 445 446 454 private LoadServiceResource findFileInClasspath(String name) { 455 ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 458 459 for (Iterator pathIter = loadPath.getList().iterator(); pathIter.hasNext();) { 460 String entry = pathIter.next().toString(); 461 462 if (entry.charAt(0) == '/' || (entry.length() > 1 && entry.charAt(1) == ':')) continue; 464 465 URL loc = classLoader.getResource(entry + "/" + name); 467 468 if (isRequireable(loc)) { 470 return new LoadServiceResource(loc, loc.getPath()); 471 } 472 } 473 474 if (name.charAt(0) == '/' || (name.length() > 1 && name.charAt(1) == ':')) return null; 476 477 URL loc = classLoader.getResource(name); 480 481 return isRequireable(loc) ? new LoadServiceResource(loc, loc.getPath()) : null; 482 } 483 484 private Library tryLoadExtension(Library library, String file) { 485 Library oldLibrary = library; 488 489 if(library == null || library instanceof JarredScript) { 490 String [] all = file.split("/"); 492 StringBuffer finName = new StringBuffer (); 493 for(int i=0, j=(all.length-1); i<j; i++) { 494 finName.append(all[i].toLowerCase()).append("."); 495 496 } 497 498 String [] last = all[all.length-1].split("_"); 501 for(int i=0, j=last.length; i<j; i++) { 502 finName.append(Character.toUpperCase(last[i].charAt(0))).append(last[i].substring(1)); 503 } 504 finName.append("Service"); 505 506 String className = finName.toString().replaceAll("^\\.*",""); 508 509 if(library instanceof JarredScript) { 511 runtime.getJavaSupport().addToClasspath(((JarredScript)library).getResource().getURL()); 514 } 515 516 try { 517 Class theClass = runtime.getJavaSupport().loadJavaClass(className); 518 library = new ClassExtensionLibrary(theClass); 519 } catch(Exception ee) { 520 library = null; 521 } 522 } 523 524 if(library == null && oldLibrary != null) { 526 library = oldLibrary; 527 } 528 return library; 529 } 530 531 532 private boolean isRequireable(URL loc) { 533 if (loc != null) { 534 if (loc.getProtocol().equals("file") && new java.io.File (loc.getFile()).isDirectory()) { 535 return false; 536 } 537 538 try { 539 loc.openConnection(); 540 return true; 541 } catch (Exception e) {} 542 } 543 return false; 544 } 545 } 546 | Popular Tags |