1 19 20 package org.netbeans; 21 22 import java.io.File ; 23 import java.io.FileInputStream ; 24 import java.io.FileOutputStream ; 25 import java.io.IOException ; 26 import java.io.InputStream ; 27 import java.io.OutputStream ; 28 import java.net.MalformedURLException ; 29 import java.net.URL ; 30 import java.security.CodeSource ; 31 import java.security.PermissionCollection ; 32 import java.security.Policy ; 33 import java.security.ProtectionDomain ; 34 import java.security.cert.Certificate ; 35 import java.util.ArrayList ; 36 import java.util.Arrays ; 37 import java.util.Enumeration ; 38 import java.util.HashSet ; 39 import java.util.List ; 40 import java.util.Set ; 41 import java.util.Vector ; 42 import java.util.jar.Attributes ; 43 import java.util.jar.Attributes.Name; 44 import java.util.jar.JarFile ; 45 import java.util.jar.Manifest ; 46 import java.util.logging.Level ; 47 import java.util.logging.Logger ; 48 import java.util.zip.ZipEntry ; 49 import org.openide.util.Union2; 50 51 57 public class JarClassLoader extends ProxyClassLoader { 58 59 private static final Logger LOGGER = Logger.getLogger(JarClassLoader.class.getName()); 60 61 private Source [] sources; 62 63 private Set <JarFile > deadJars = null; 64 65 68 public JarClassLoader(List <Union2<File ,JarFile >> files, ClassLoader [] parents) { 69 this(files, parents, true); 70 } 71 72 76 public JarClassLoader(List <Union2<File ,JarFile >> files, ClassLoader [] parents, boolean transitive) { 77 super(parents, transitive); 78 79 sources = new Source [files.size()]; 80 try { 81 int i=0; 82 for (Union2<File ,JarFile > file : files) { 83 if (file.hasFirst()) { 84 sources[i++] = new DirSource(file.first(), this); 85 } else { 86 sources[i++] = new JarSource(file.second(), this); 87 } 88 } 89 } catch (MalformedURLException exc) { 90 throw new IllegalArgumentException (exc.getMessage()); 91 } 92 93 } 94 95 97 final void addSources (List <Union2<File ,JarFile >> newSources) { 98 ArrayList <Source > l = new ArrayList <Source > (sources.length + newSources.size ()); 99 l.addAll (Arrays.asList (sources)); 100 try { 101 for (Union2<File ,JarFile > file : newSources) { 102 if (file.hasFirst()) { 103 l.add(new DirSource(file.first(), this)); 104 } else { 105 l.add(new JarSource(file.second(), this)); 106 } 107 } 108 } catch (MalformedURLException exc) { 109 throw new IllegalArgumentException (exc.getMessage()); 110 } 111 sources = l.toArray (sources); 112 } 113 114 116 protected PermissionCollection getPermissions( CodeSource cs ) { 117 return Policy.getPolicy().getPermissions(cs); 118 } 119 120 121 protected Package definePackage(String name, Manifest man, URL url) 122 throws IllegalArgumentException 123 { 124 if (man == null ) { 125 return definePackage(name, null, null, null, null, null, null, null); 126 } 127 128 String path = name.replace('.', '/').concat("/"); Attributes spec = man.getAttributes(path); 130 Attributes main = man.getMainAttributes(); 131 132 String specTitle = getAttr(spec, main, Name.SPECIFICATION_TITLE); 133 String implTitle = getAttr(spec, main, Name.IMPLEMENTATION_TITLE); 134 String specVersion = getAttr(spec, main, Name.SPECIFICATION_VERSION); 135 String implVersion = getAttr(spec, main, Name.IMPLEMENTATION_VERSION); 136 String specVendor = getAttr(spec, main, Name.SPECIFICATION_VENDOR); 137 String implVendor = getAttr(spec, main, Name.IMPLEMENTATION_VENDOR); 138 String sealed = getAttr(spec, main, Name.SEALED); 139 140 URL sealBase = "true".equalsIgnoreCase(sealed) ? url : null; return definePackage(name, specTitle, specVersion, specVendor, 142 implTitle, implVersion, implVendor, sealBase); 143 } 144 145 private static String getAttr(Attributes spec, Attributes main, Name name) { 146 String val = null; 147 if (spec != null) val = spec.getValue (name); 148 if (val == null && main != null) val = main.getValue (name); 149 return val; 150 } 151 152 protected Class simpleFindClass(String name, String path, String pkgnameSlashes) { 153 for( int i=0; i<sources.length; i++ ) { 155 Source src = sources[i]; 156 byte[] data = src.getClassData(name, path); 157 if (data == null) continue; 158 159 byte[] d = PatchByteCode.patch (data, name); 161 data = d; 162 163 int j = name.lastIndexOf('.'); 164 String pkgName = name.substring(0, j); 165 Package pkg = getPackageFast(pkgName, pkgnameSlashes, isSpecialResource(pkgnameSlashes)); 172 if (pkg != null) { 173 if (pkg.isSealed() && !pkg.isSealed(src.getURL())) throw new SecurityException ("sealing violation"); } else { 176 Manifest man = src.getManifest(); 177 definePackage (pkgName, man, src.getURL()); 178 } 179 180 return defineClass (name, data, 0, data.length, src.getProtectionDomain()); 181 } 182 return null; 183 } 184 protected URL findResource(String name) { 186 for( int i=0; i<sources.length; i++ ) { 187 URL item = sources[i].getResource(name); 188 if (item != null) return item; 189 } 190 return null; 191 } 192 193 protected Enumeration <URL > simpleFindResources(String name) { 194 Vector <URL > v = new Vector <URL >(3); 195 197 for( int i=0; i<sources.length; i++ ) { 198 URL item = sources[i].getResource(name); 199 if (item != null) v.add(item); 200 } 201 return v.elements(); 202 } 203 204 public void destroy() { 205 super.destroy (); 206 207 assert deadJars == null : "Already had dead JARs: " + deadJars; 209 deadJars = new HashSet <JarFile >(); 210 try { 211 for (int i = 0; i < sources.length; i++) { 212 if (sources[i] instanceof JarSource) { 213 JarFile origJar = ((JarSource)sources[i]).getJarFile(); 214 File orig = new File (origJar.getName()); 215 if (!orig.isFile()) { 216 continue; 223 } 224 String name = orig.getName(); 225 String prefix, suffix; 226 int idx = name.lastIndexOf('.'); 227 if (idx == -1) { 228 prefix = name; 229 suffix = null; 230 } else { 231 prefix = name.substring(0, idx); 232 suffix = name.substring(idx); 233 } 234 while (prefix.length() < 3) prefix += "x"; File temp = File.createTempFile(prefix, suffix); 236 temp.deleteOnExit(); 237 InputStream is = new FileInputStream (orig); 239 try { 240 OutputStream os = new FileOutputStream (temp); 241 try { 242 byte[] buf = new byte[4096]; 243 int j; 244 while ((j = is.read(buf)) != -1) { 245 os.write(buf, 0, j); 246 } 247 } finally { 248 os.close(); 249 } 250 } finally { 251 is.close(); 252 } 253 JarFile tempJar = new JarFile (temp); 256 origJar.close(); 257 deadJars.add(tempJar); 258 sources[i] = new JarSource(tempJar, this); 259 LOGGER.log(Level.FINE, "#21114: replacing {0} with {1}", new Object [] {orig, temp}); 260 } 261 } 262 } catch (IOException ioe) { 263 LOGGER.log(Level.WARNING, null, ioe); 264 } 265 } 266 267 270 protected void finalize() throws Throwable { 271 super.finalize(); 272 for (int i = 0; i < sources.length; i++) { 273 if (sources[i] instanceof JarSource) { 274 JarFile j = ((JarSource)sources[i]).getJarFile(); 275 File f = new File (j.getName()); 276 j.close(); 277 if (deadJars != null && deadJars.contains(j)) { 278 LOGGER.log(Level.FINE, "#21114: closing and deleting temporary JAR {0}", f); 279 if (f.isFile() && !f.delete()) { 280 LOGGER.log(Level.FINE, "(but failed to delete {0})", f); 281 } 282 } 283 } 284 } 285 } 286 287 static abstract class Source { 288 private URL url; 289 private ProtectionDomain pd; 290 protected JarClassLoader jcl; 291 292 public Source(URL url, JarClassLoader jcl) { 293 this.url = url; 294 this.jcl = jcl; 295 } 296 297 public final URL getURL() { 298 return url; 299 } 300 301 public final ProtectionDomain getProtectionDomain() { 302 if (pd == null) { 303 CodeSource cs = new CodeSource (url, new Certificate [0]); 304 pd = new ProtectionDomain (cs, jcl.getPermissions(cs)); 305 } 306 return pd; 307 } 308 309 public final URL getResource(String name) { 310 try { 311 return doGetResource(name); 312 } catch (Exception e) { 313 LOGGER.log(Level.FINE, null, e); 315 } 316 return null; 317 } 318 319 protected abstract URL doGetResource(String name) throws MalformedURLException ; 320 321 public final byte[] getClassData(String name, String path) { 322 try { 323 return readClass(name, path); 324 } catch (IOException e) { 325 LOGGER.log(Level.FINE, null, e); 326 } 327 return null; 328 } 329 330 protected abstract byte[] readClass(String name, String path) throws IOException ; 331 332 public Manifest getManifest() { 333 return null; 334 } 335 } 336 337 static class JarSource extends Source { 338 JarFile src; 339 private String resPrefix; 340 341 public JarSource(JarFile file, JarClassLoader jcl) throws MalformedURLException { 342 super(new File (file.getName()).toURI().toURL(), jcl); 343 src = file; 344 resPrefix = "jar:" + new File (src.getName()).toURI() + "!/"; } 346 347 public Manifest getManifest() { 348 try { 349 return src.getManifest(); 350 } catch (IOException e) { 351 return null; 352 } 353 } 354 355 JarFile getJarFile() { 356 return src; 357 } 358 359 protected URL doGetResource(String name) throws MalformedURLException { 360 ZipEntry ze; 361 try { 362 ze = src.getEntry(name); 363 } catch (IllegalStateException ex) { 364 return null; 367 } 368 if (ze == null) { 369 return null; 370 } 371 LOGGER.log(Level.FINER, "Loading {0} from {1}", new Object [] {name, src.getName()}); 372 return new URL (resPrefix + ze.getName()); 373 } 374 375 protected byte[] readClass(String name, String path) throws IOException { 376 ZipEntry ze; 377 try { 378 ze = src.getEntry(path); 379 } catch (IllegalStateException ex) { 380 return null; 383 } 384 if (ze == null) { 385 return null; 386 } 387 LOGGER.log(Level.FINER, "Loading {0} from {1}", new Object [] {path, src.getName()}); 388 389 int len = (int)ze.getSize(); 390 byte[] data = new byte[len]; 391 InputStream is = src.getInputStream(ze); 392 int count = 0; 393 while (count < len) { 394 count += is.read(data, count, len-count); 395 } 396 return data; 397 } 398 } 399 400 static class DirSource extends Source { 401 File dir; 402 403 public DirSource(File file, JarClassLoader jcl) throws MalformedURLException { 404 super(file.toURI().toURL(), jcl); 405 dir = file; 406 } 407 408 protected URL doGetResource(String name) throws MalformedURLException { 409 File resFile = new File (dir, name); 410 return resFile.exists() ? resFile.toURI().toURL() : null; 411 } 412 413 protected byte[] readClass(String name, String path) throws IOException { 414 File clsFile = new File (dir, path.replace('/', File.separatorChar)); 415 if (!clsFile.exists()) return null; 416 417 int len = (int)clsFile.length(); 418 byte[] data = new byte[len]; 419 InputStream is = new FileInputStream (clsFile); 420 int count = 0; 421 while (count < len) { 422 count += is.read(data, count, len-count); 423 } 424 return data; 425 } 426 427 } 428 429 } 430 | Popular Tags |