author | alanb |
Fri, 28 Oct 2016 10:18:07 +0100 | |
changeset 41817 | b90ad1de93ea |
parent 37779 | 7c84df693837 |
child 41911 | b3bb62588635 |
permissions | -rw-r--r-- |
36511 | 1 |
/* |
2 |
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. |
|
3 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 |
* |
|
5 |
* This code is free software; you can redistribute it and/or modify it |
|
6 |
* under the terms of the GNU General Public License version 2 only, as |
|
7 |
* published by the Free Software Foundation. Oracle designates this |
|
8 |
* particular file as subject to the "Classpath" exception as provided |
|
9 |
* by Oracle in the LICENSE file that accompanied this code. |
|
10 |
* |
|
11 |
* This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 |
* version 2 for more details (a copy is included in the LICENSE file that |
|
15 |
* accompanied this code). |
|
16 |
* |
|
17 |
* You should have received a copy of the GNU General Public License version |
|
18 |
* 2 along with this work; if not, write to the Free Software Foundation, |
|
19 |
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 |
* |
|
21 |
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
22 |
* or visit www.oracle.com if you need additional information or have any |
|
23 |
* questions. |
|
24 |
*/ |
|
25 |
||
26 |
package jdk.internal.loader; |
|
27 |
||
28 |
import java.io.File; |
|
29 |
import java.io.FilePermission; |
|
30 |
import java.io.IOException; |
|
31 |
import java.io.InputStream; |
|
32 |
import java.lang.module.ModuleReference; |
|
33 |
import java.lang.module.ModuleReader; |
|
34 |
import java.net.MalformedURLException; |
|
35 |
import java.net.URI; |
|
36 |
import java.net.URL; |
|
37 |
import java.nio.ByteBuffer; |
|
38 |
import java.security.AccessController; |
|
39 |
import java.security.CodeSigner; |
|
40 |
import java.security.CodeSource; |
|
41 |
import java.security.Permission; |
|
42 |
import java.security.PermissionCollection; |
|
43 |
import java.security.PrivilegedAction; |
|
44 |
import java.security.PrivilegedActionException; |
|
45 |
import java.security.PrivilegedExceptionAction; |
|
46 |
import java.security.SecureClassLoader; |
|
47 |
import java.util.ArrayList; |
|
48 |
import java.util.Collections; |
|
49 |
import java.util.Enumeration; |
|
50 |
import java.util.List; |
|
51 |
import java.util.Map; |
|
52 |
import java.util.Optional; |
|
53 |
import java.util.concurrent.ConcurrentHashMap; |
|
54 |
import java.util.jar.Attributes; |
|
55 |
import java.util.jar.Manifest; |
|
41817
b90ad1de93ea
8168789: ModuleReader.list and ModuleFinder.of update
alanb
parents:
37779
diff
changeset
|
56 |
import java.util.stream.Stream; |
36511 | 57 |
|
58 |
import jdk.internal.module.ModulePatcher.PatchedModuleReader; |
|
59 |
import jdk.internal.misc.VM; |
|
60 |
||
61 |
||
62 |
/** |
|
63 |
* The platform or application class loader. Resources loaded from modules |
|
64 |
* defined to the boot class loader are also loaded via an instance of this |
|
65 |
* ClassLoader type. |
|
66 |
* |
|
67 |
* <p> This ClassLoader supports loading of classes and resources from modules. |
|
68 |
* Modules are defined to the ClassLoader by invoking the {@link #loadModule} |
|
69 |
* method. Defining a module to this ClassLoader has the effect of making the |
|
70 |
* types in the module visible. </p> |
|
71 |
* |
|
72 |
* <p> This ClassLoader also supports loading of classes and resources from a |
|
73 |
* class path of URLs that are specified to the ClassLoader at construction |
|
74 |
* time. The class path may expand at runtime (the Class-Path attribute in JAR |
|
75 |
* files or via instrumentation agents). </p> |
|
76 |
* |
|
77 |
* <p> The delegation model used by this ClassLoader differs to the regular |
|
78 |
* delegation model. When requested to load a class then this ClassLoader first |
|
79 |
* maps the class name to its package name. If there is a module defined to a |
|
80 |
* BuiltinClassLoader containing this package then the class loader delegates |
|
81 |
* directly to that class loader. If there isn't a module containing the |
|
82 |
* package then it delegates the search to the parent class loader and if not |
|
83 |
* found in the parent then it searches the class path. The main difference |
|
84 |
* between this and the usual delegation model is that it allows the platform |
|
85 |
* class loader to delegate to the application class loader, important with |
|
86 |
* upgraded modules defined to the platform class loader. |
|
87 |
*/ |
|
88 |
||
89 |
public class BuiltinClassLoader |
|
90 |
extends SecureClassLoader |
|
91 |
{ |
|
92 |
static { |
|
93 |
if (!ClassLoader.registerAsParallelCapable()) |
|
94 |
throw new InternalError(); |
|
95 |
} |
|
96 |
||
97 |
// parent ClassLoader |
|
98 |
private final BuiltinClassLoader parent; |
|
99 |
||
100 |
// the URL class path or null if there is no class path |
|
101 |
private final URLClassPath ucp; |
|
102 |
||
103 |
||
104 |
/** |
|
105 |
* A module defined/loaded by a built-in class loader. |
|
106 |
* |
|
107 |
* A LoadedModule encapsulates a ModuleReference along with its CodeSource |
|
37779
7c84df693837
8154956: Module system implementation refresh (4/2016)
alanb
parents:
36674
diff
changeset
|
108 |
* URL to avoid needing to create this URL when defining classes. |
36511 | 109 |
*/ |
110 |
private static class LoadedModule { |
|
111 |
private final BuiltinClassLoader loader; |
|
112 |
private final ModuleReference mref; |
|
113 |
private final URL codeSourceURL; // may be null |
|
114 |
||
115 |
LoadedModule(BuiltinClassLoader loader, ModuleReference mref) { |
|
116 |
URL url = null; |
|
117 |
if (mref.location().isPresent()) { |
|
118 |
try { |
|
119 |
url = mref.location().get().toURL(); |
|
120 |
} catch (MalformedURLException e) { } |
|
121 |
} |
|
122 |
this.loader = loader; |
|
123 |
this.mref = mref; |
|
124 |
this.codeSourceURL = url; |
|
125 |
} |
|
126 |
||
127 |
BuiltinClassLoader loader() { return loader; } |
|
128 |
ModuleReference mref() { return mref; } |
|
129 |
String name() { return mref.descriptor().name(); } |
|
130 |
URL codeSourceURL() { return codeSourceURL; } |
|
131 |
} |
|
132 |
||
133 |
||
134 |
// maps package name to loaded module for modules in the boot layer |
|
135 |
private static final Map<String, LoadedModule> packageToModule |
|
136 |
= new ConcurrentHashMap<>(1024); |
|
137 |
||
138 |
// maps a module name to a module reference |
|
139 |
private final Map<String, ModuleReference> nameToModule; |
|
140 |
||
141 |
// maps a module reference to a module reader |
|
142 |
private final Map<ModuleReference, ModuleReader> moduleToReader; |
|
143 |
||
144 |
||
145 |
/** |
|
146 |
* Create a new instance. |
|
147 |
*/ |
|
148 |
BuiltinClassLoader(BuiltinClassLoader parent, URLClassPath ucp) { |
|
149 |
// ensure getParent() returns null when the parent is the boot loader |
|
150 |
super(parent == null || parent == ClassLoaders.bootLoader() ? null : parent); |
|
151 |
||
152 |
this.parent = parent; |
|
153 |
this.ucp = ucp; |
|
154 |
||
155 |
this.nameToModule = new ConcurrentHashMap<>(); |
|
156 |
this.moduleToReader = new ConcurrentHashMap<>(); |
|
157 |
} |
|
158 |
||
159 |
/** |
|
160 |
* Register a module this this class loader. This has the effect of making |
|
161 |
* the types in the module visible. |
|
162 |
*/ |
|
163 |
public void loadModule(ModuleReference mref) { |
|
164 |
String mn = mref.descriptor().name(); |
|
165 |
if (nameToModule.putIfAbsent(mn, mref) != null) { |
|
166 |
throw new InternalError(mn + " already defined to this loader"); |
|
167 |
} |
|
168 |
||
169 |
LoadedModule loadedModule = new LoadedModule(this, mref); |
|
170 |
for (String pn : mref.descriptor().packages()) { |
|
171 |
LoadedModule other = packageToModule.putIfAbsent(pn, loadedModule); |
|
172 |
if (other != null) { |
|
173 |
throw new InternalError(pn + " in modules " + mn + " and " |
|
174 |
+ other.mref().descriptor().name()); |
|
175 |
} |
|
176 |
} |
|
177 |
} |
|
178 |
||
179 |
/** |
|
180 |
* Returns the {@code ModuleReference} for the named module defined to |
|
181 |
* this class loader; or {@code null} if not defined. |
|
182 |
* |
|
183 |
* @param name The name of the module to find |
|
184 |
*/ |
|
185 |
protected ModuleReference findModule(String name) { |
|
186 |
return nameToModule.get(name); |
|
187 |
} |
|
188 |
||
189 |
||
190 |
// -- finding resources |
|
191 |
||
192 |
/** |
|
193 |
* Returns a URL to a resource of the given name in a module defined to |
|
194 |
* this class loader. |
|
195 |
*/ |
|
196 |
@Override |
|
197 |
public URL findResource(String mn, String name) throws IOException { |
|
198 |
ModuleReference mref = nameToModule.get(mn); |
|
199 |
if (mref == null) |
|
200 |
return null; // not defined to this class loader |
|
201 |
||
202 |
URL url; |
|
203 |
||
204 |
try { |
|
205 |
url = AccessController.doPrivileged( |
|
206 |
new PrivilegedExceptionAction<URL>() { |
|
207 |
@Override |
|
208 |
public URL run() throws IOException { |
|
209 |
URI u = moduleReaderFor(mref).find(name).orElse(null); |
|
210 |
if (u != null) { |
|
211 |
try { |
|
212 |
return u.toURL(); |
|
213 |
} catch (MalformedURLException e) { } |
|
214 |
} |
|
215 |
return null; |
|
216 |
} |
|
217 |
}); |
|
218 |
} catch (PrivilegedActionException pae) { |
|
219 |
throw (IOException) pae.getCause(); |
|
220 |
} |
|
221 |
||
222 |
// check access to the URL |
|
223 |
return checkURL(url); |
|
224 |
} |
|
225 |
||
226 |
/** |
|
227 |
* Returns an input stream to a resource of the given name in a module |
|
228 |
* defined to this class loader. |
|
229 |
*/ |
|
230 |
public InputStream findResourceAsStream(String mn, String name) |
|
231 |
throws IOException |
|
232 |
{ |
|
233 |
// Need URL to resource when running with a security manager so that |
|
234 |
// the right permission check is done. |
|
235 |
SecurityManager sm = System.getSecurityManager(); |
|
236 |
if (sm != null) { |
|
237 |
||
238 |
URL url = findResource(mn, name); |
|
239 |
return (url != null) ? url.openStream() : null; |
|
240 |
||
241 |
} else { |
|
242 |
||
243 |
ModuleReference mref = nameToModule.get(mn); |
|
244 |
if (mref == null) |
|
245 |
return null; // not defined to this class loader |
|
246 |
||
247 |
try { |
|
248 |
return AccessController.doPrivileged( |
|
249 |
new PrivilegedExceptionAction<InputStream>() { |
|
250 |
@Override |
|
251 |
public InputStream run() throws IOException { |
|
252 |
return moduleReaderFor(mref).open(name).orElse(null); |
|
253 |
} |
|
254 |
}); |
|
255 |
} catch (PrivilegedActionException pae) { |
|
256 |
throw (IOException) pae.getCause(); |
|
257 |
} |
|
258 |
} |
|
259 |
} |
|
260 |
||
261 |
/** |
|
262 |
* Finds the resource with the given name on the class path of this class |
|
263 |
* loader. |
|
264 |
*/ |
|
265 |
@Override |
|
266 |
public URL findResource(String name) { |
|
267 |
if (ucp != null) { |
|
268 |
PrivilegedAction<URL> pa = () -> ucp.findResource(name, false); |
|
269 |
URL url = AccessController.doPrivileged(pa); |
|
270 |
return checkURL(url); |
|
271 |
} else { |
|
272 |
return null; |
|
273 |
} |
|
274 |
} |
|
275 |
||
276 |
/** |
|
277 |
* Returns an enumeration of URL objects to all the resources with the |
|
278 |
* given name on the class path of this class loader. |
|
279 |
*/ |
|
280 |
@Override |
|
281 |
public Enumeration<URL> findResources(String name) throws IOException { |
|
282 |
if (ucp != null) { |
|
283 |
List<URL> result = new ArrayList<>(); |
|
284 |
PrivilegedAction<Enumeration<URL>> pa = () -> ucp.findResources(name, false); |
|
285 |
Enumeration<URL> e = AccessController.doPrivileged(pa); |
|
286 |
while (e.hasMoreElements()) { |
|
287 |
URL url = checkURL(e.nextElement()); |
|
288 |
if (url != null) { |
|
289 |
result.add(url); |
|
290 |
} |
|
291 |
} |
|
292 |
return Collections.enumeration(result); // checked URLs |
|
293 |
} else { |
|
294 |
return Collections.emptyEnumeration(); |
|
295 |
} |
|
296 |
} |
|
297 |
||
298 |
||
299 |
// -- finding/loading classes |
|
300 |
||
301 |
/** |
|
302 |
* Finds the class with the specified binary name. |
|
303 |
*/ |
|
304 |
@Override |
|
305 |
protected Class<?> findClass(String cn) throws ClassNotFoundException { |
|
306 |
// no class loading until VM is fully initialized |
|
307 |
if (!VM.isModuleSystemInited()) |
|
308 |
throw new ClassNotFoundException(cn); |
|
309 |
||
310 |
// find the candidate module for this class |
|
311 |
LoadedModule loadedModule = findLoadedModule(cn); |
|
312 |
||
313 |
Class<?> c = null; |
|
314 |
if (loadedModule != null) { |
|
315 |
||
316 |
// attempt to load class in module defined to this loader |
|
317 |
if (loadedModule.loader() == this) { |
|
318 |
c = findClassInModuleOrNull(loadedModule, cn); |
|
319 |
} |
|
320 |
||
321 |
} else { |
|
322 |
||
323 |
// search class path |
|
324 |
if (ucp != null) { |
|
325 |
c = findClassOnClassPathOrNull(cn); |
|
326 |
} |
|
327 |
||
328 |
} |
|
329 |
||
330 |
// not found |
|
331 |
if (c == null) |
|
332 |
throw new ClassNotFoundException(cn); |
|
333 |
||
334 |
return c; |
|
335 |
} |
|
336 |
||
337 |
/** |
|
338 |
* Finds the class with the specified binary name in a given module. |
|
339 |
* This method returns {@code null} if the class cannot be found. |
|
340 |
*/ |
|
341 |
@Override |
|
342 |
protected Class<?> findClass(String mn, String cn) { |
|
343 |
ModuleReference mref = nameToModule.get(mn); |
|
344 |
if (mref == null) |
|
345 |
return null; // not defined to this class loader |
|
346 |
||
347 |
// find the candidate module for this class |
|
348 |
LoadedModule loadedModule = findLoadedModule(cn); |
|
349 |
if (loadedModule == null || !loadedModule.name().equals(mn)) { |
|
350 |
return null; // module name does not match |
|
351 |
} |
|
352 |
||
353 |
// attempt to load class in module defined to this loader |
|
354 |
assert loadedModule.loader() == this; |
|
355 |
return findClassInModuleOrNull(loadedModule, cn); |
|
356 |
} |
|
357 |
||
358 |
/** |
|
359 |
* Loads the class with the specified binary name. |
|
360 |
*/ |
|
361 |
@Override |
|
362 |
protected Class<?> loadClass(String cn, boolean resolve) |
|
363 |
throws ClassNotFoundException |
|
364 |
{ |
|
365 |
Class<?> c = loadClassOrNull(cn, resolve); |
|
366 |
if (c == null) |
|
367 |
throw new ClassNotFoundException(cn); |
|
368 |
return c; |
|
369 |
} |
|
370 |
||
371 |
/** |
|
372 |
* A variation of {@code loadCass} to load a class with the specified |
|
373 |
* binary name. This method returns {@code null} when the class is not |
|
374 |
* found. |
|
375 |
*/ |
|
376 |
protected Class<?> loadClassOrNull(String cn, boolean resolve) { |
|
377 |
synchronized (getClassLoadingLock(cn)) { |
|
378 |
// check if already loaded |
|
379 |
Class<?> c = findLoadedClass(cn); |
|
380 |
||
381 |
if (c == null) { |
|
382 |
||
383 |
// find the candidate module for this class |
|
384 |
LoadedModule loadedModule = findLoadedModule(cn); |
|
385 |
if (loadedModule != null) { |
|
386 |
||
387 |
// package is in a module |
|
388 |
BuiltinClassLoader loader = loadedModule.loader(); |
|
389 |
if (loader == this) { |
|
390 |
if (VM.isModuleSystemInited()) { |
|
391 |
c = findClassInModuleOrNull(loadedModule, cn); |
|
392 |
} |
|
393 |
} else { |
|
394 |
// delegate to the other loader |
|
395 |
c = loader.loadClassOrNull(cn); |
|
396 |
} |
|
397 |
||
398 |
} else { |
|
399 |
||
400 |
// check parent |
|
401 |
if (parent != null) { |
|
402 |
c = parent.loadClassOrNull(cn); |
|
403 |
} |
|
404 |
||
405 |
// check class path |
|
406 |
if (c == null && ucp != null && VM.isModuleSystemInited()) { |
|
407 |
c = findClassOnClassPathOrNull(cn); |
|
408 |
} |
|
409 |
} |
|
410 |
||
411 |
} |
|
412 |
||
413 |
if (resolve && c != null) |
|
414 |
resolveClass(c); |
|
415 |
||
416 |
return c; |
|
417 |
} |
|
418 |
} |
|
419 |
||
420 |
/** |
|
421 |
* A variation of {@code loadCass} to load a class with the specified |
|
422 |
* binary name. This method returns {@code null} when the class is not |
|
423 |
* found. |
|
424 |
*/ |
|
425 |
protected Class<?> loadClassOrNull(String cn) { |
|
426 |
return loadClassOrNull(cn, false); |
|
427 |
} |
|
428 |
||
429 |
/** |
|
430 |
* Find the candidate loaded module for the given class name. |
|
431 |
* Returns {@code null} if none of the modules defined to this |
|
432 |
* class loader contain the API package for the class. |
|
433 |
*/ |
|
434 |
private LoadedModule findLoadedModule(String cn) { |
|
435 |
int pos = cn.lastIndexOf('.'); |
|
436 |
if (pos < 0) |
|
437 |
return null; // unnamed package |
|
438 |
||
439 |
String pn = cn.substring(0, pos); |
|
440 |
return packageToModule.get(pn); |
|
441 |
} |
|
442 |
||
443 |
/** |
|
444 |
* Finds the class with the specified binary name if in a module |
|
445 |
* defined to this ClassLoader. |
|
446 |
* |
|
447 |
* @return the resulting Class or {@code null} if not found |
|
448 |
*/ |
|
449 |
private Class<?> findClassInModuleOrNull(LoadedModule loadedModule, String cn) { |
|
450 |
PrivilegedAction<Class<?>> pa = () -> defineClass(cn, loadedModule); |
|
451 |
return AccessController.doPrivileged(pa); |
|
452 |
} |
|
453 |
||
454 |
/** |
|
455 |
* Finds the class with the specified binary name on the class path. |
|
456 |
* |
|
457 |
* @return the resulting Class or {@code null} if not found |
|
458 |
*/ |
|
459 |
private Class<?> findClassOnClassPathOrNull(String cn) { |
|
460 |
return AccessController.doPrivileged( |
|
461 |
new PrivilegedAction<Class<?>>() { |
|
462 |
public Class<?> run() { |
|
463 |
String path = cn.replace('.', '/').concat(".class"); |
|
464 |
Resource res = ucp.getResource(path, false); |
|
465 |
if (res != null) { |
|
466 |
try { |
|
467 |
return defineClass(cn, res); |
|
468 |
} catch (IOException ioe) { |
|
469 |
// TBD on how I/O errors should be propagated |
|
470 |
} |
|
471 |
} |
|
472 |
return null; |
|
473 |
} |
|
474 |
}); |
|
475 |
} |
|
476 |
||
477 |
/** |
|
478 |
* Defines the given binary class name to the VM, loading the class |
|
479 |
* bytes from the given module. |
|
480 |
* |
|
481 |
* @return the resulting Class or {@code null} if an I/O error occurs |
|
482 |
*/ |
|
483 |
private Class<?> defineClass(String cn, LoadedModule loadedModule) { |
|
484 |
ModuleReference mref = loadedModule.mref(); |
|
485 |
ModuleReader reader = moduleReaderFor(mref); |
|
486 |
||
487 |
try { |
|
488 |
ByteBuffer bb = null; |
|
489 |
URL csURL = null; |
|
490 |
||
491 |
// locate class file, special handling for patched modules to |
|
492 |
// avoid locating the resource twice |
|
493 |
String rn = cn.replace('.', '/').concat(".class"); |
|
494 |
if (reader instanceof PatchedModuleReader) { |
|
495 |
Resource r = ((PatchedModuleReader)reader).findResource(rn); |
|
496 |
if (r != null) { |
|
497 |
bb = r.getByteBuffer(); |
|
498 |
csURL = r.getCodeSourceURL(); |
|
499 |
} |
|
500 |
} else { |
|
501 |
bb = reader.read(rn).orElse(null); |
|
502 |
csURL = loadedModule.codeSourceURL(); |
|
503 |
} |
|
504 |
||
505 |
if (bb == null) { |
|
506 |
// class not found |
|
507 |
return null; |
|
508 |
} |
|
509 |
||
510 |
CodeSource cs = new CodeSource(csURL, (CodeSigner[]) null); |
|
511 |
try { |
|
512 |
// define class to VM |
|
513 |
return defineClass(cn, bb, cs); |
|
514 |
||
515 |
} finally { |
|
516 |
reader.release(bb); |
|
517 |
} |
|
518 |
||
519 |
} catch (IOException ioe) { |
|
520 |
// TBD on how I/O errors should be propagated |
|
521 |
return null; |
|
522 |
} |
|
523 |
} |
|
524 |
||
525 |
/** |
|
526 |
* Defines the given binary class name to the VM, loading the class |
|
527 |
* bytes via the given Resource object. |
|
528 |
* |
|
529 |
* @return the resulting Class |
|
530 |
* @throws IOException if reading the resource fails |
|
531 |
* @throws SecurityException if there is a sealing violation (JAR spec) |
|
532 |
*/ |
|
533 |
private Class<?> defineClass(String cn, Resource res) throws IOException { |
|
534 |
URL url = res.getCodeSourceURL(); |
|
535 |
||
536 |
// if class is in a named package then ensure that the package is defined |
|
537 |
int pos = cn.lastIndexOf('.'); |
|
538 |
if (pos != -1) { |
|
539 |
String pn = cn.substring(0, pos); |
|
540 |
Manifest man = res.getManifest(); |
|
541 |
defineOrCheckPackage(pn, man, url); |
|
542 |
} |
|
543 |
||
544 |
// defines the class to the runtime |
|
545 |
ByteBuffer bb = res.getByteBuffer(); |
|
546 |
if (bb != null) { |
|
547 |
CodeSigner[] signers = res.getCodeSigners(); |
|
548 |
CodeSource cs = new CodeSource(url, signers); |
|
549 |
return defineClass(cn, bb, cs); |
|
550 |
} else { |
|
551 |
byte[] b = res.getBytes(); |
|
552 |
CodeSigner[] signers = res.getCodeSigners(); |
|
553 |
CodeSource cs = new CodeSource(url, signers); |
|
554 |
return defineClass(cn, b, 0, b.length, cs); |
|
555 |
} |
|
556 |
} |
|
557 |
||
558 |
||
559 |
// -- packages |
|
560 |
||
561 |
/** |
|
562 |
* Defines a package in this ClassLoader. If the package is already defined |
|
563 |
* then its sealing needs to be checked if sealed by the legacy sealing |
|
564 |
* mechanism. |
|
565 |
* |
|
566 |
* @throws SecurityException if there is a sealing violation (JAR spec) |
|
567 |
*/ |
|
568 |
protected Package defineOrCheckPackage(String pn, Manifest man, URL url) { |
|
569 |
Package pkg = getAndVerifyPackage(pn, man, url); |
|
570 |
if (pkg == null) { |
|
571 |
try { |
|
572 |
if (man != null) { |
|
573 |
pkg = definePackage(pn, man, url); |
|
574 |
} else { |
|
575 |
pkg = definePackage(pn, null, null, null, null, null, null, null); |
|
576 |
} |
|
577 |
} catch (IllegalArgumentException iae) { |
|
578 |
// defined by another thread so need to re-verify |
|
579 |
pkg = getAndVerifyPackage(pn, man, url); |
|
580 |
if (pkg == null) |
|
581 |
throw new InternalError("Cannot find package: " + pn); |
|
582 |
} |
|
583 |
} |
|
584 |
return pkg; |
|
585 |
} |
|
586 |
||
587 |
/** |
|
588 |
* Get the Package with the specified package name. If defined |
|
589 |
* then verify that it against the manifest and code source. |
|
590 |
* |
|
591 |
* @throws SecurityException if there is a sealing violation (JAR spec) |
|
592 |
*/ |
|
593 |
private Package getAndVerifyPackage(String pn, Manifest man, URL url) { |
|
594 |
Package pkg = getDefinedPackage(pn); |
|
595 |
if (pkg != null) { |
|
596 |
if (pkg.isSealed()) { |
|
597 |
if (!pkg.isSealed(url)) { |
|
598 |
throw new SecurityException( |
|
599 |
"sealing violation: package " + pn + " is sealed"); |
|
600 |
} |
|
601 |
} else { |
|
602 |
// can't seal package if already defined without sealing |
|
603 |
if ((man != null) && isSealed(pn, man)) { |
|
604 |
throw new SecurityException( |
|
605 |
"sealing violation: can't seal package " + pn + |
|
606 |
": already defined"); |
|
607 |
} |
|
608 |
} |
|
609 |
} |
|
610 |
return pkg; |
|
611 |
} |
|
612 |
||
613 |
/** |
|
614 |
* Defines a new package in this ClassLoader. The attributes in the specified |
|
615 |
* Manifest are use to get the package version and sealing information. |
|
616 |
* |
|
617 |
* @throws IllegalArgumentException if the package name duplicates an |
|
618 |
* existing package either in this class loader or one of its ancestors |
|
619 |
*/ |
|
620 |
private Package definePackage(String pn, Manifest man, URL url) { |
|
621 |
String specTitle = null; |
|
622 |
String specVersion = null; |
|
623 |
String specVendor = null; |
|
624 |
String implTitle = null; |
|
625 |
String implVersion = null; |
|
626 |
String implVendor = null; |
|
627 |
String sealed = null; |
|
628 |
URL sealBase = null; |
|
629 |
||
630 |
if (man != null) { |
|
631 |
Attributes attr = man.getAttributes(pn.replace('.', '/').concat("/")); |
|
632 |
if (attr != null) { |
|
633 |
specTitle = attr.getValue(Attributes.Name.SPECIFICATION_TITLE); |
|
634 |
specVersion = attr.getValue(Attributes.Name.SPECIFICATION_VERSION); |
|
635 |
specVendor = attr.getValue(Attributes.Name.SPECIFICATION_VENDOR); |
|
636 |
implTitle = attr.getValue(Attributes.Name.IMPLEMENTATION_TITLE); |
|
637 |
implVersion = attr.getValue(Attributes.Name.IMPLEMENTATION_VERSION); |
|
638 |
implVendor = attr.getValue(Attributes.Name.IMPLEMENTATION_VENDOR); |
|
639 |
sealed = attr.getValue(Attributes.Name.SEALED); |
|
640 |
} |
|
641 |
||
642 |
attr = man.getMainAttributes(); |
|
643 |
if (attr != null) { |
|
644 |
if (specTitle == null) |
|
645 |
specTitle = attr.getValue(Attributes.Name.SPECIFICATION_TITLE); |
|
646 |
if (specVersion == null) |
|
647 |
specVersion = attr.getValue(Attributes.Name.SPECIFICATION_VERSION); |
|
648 |
if (specVendor == null) |
|
649 |
specVendor = attr.getValue(Attributes.Name.SPECIFICATION_VENDOR); |
|
650 |
if (implTitle == null) |
|
651 |
implTitle = attr.getValue(Attributes.Name.IMPLEMENTATION_TITLE); |
|
652 |
if (implVersion == null) |
|
653 |
implVersion = attr.getValue(Attributes.Name.IMPLEMENTATION_VERSION); |
|
654 |
if (implVendor == null) |
|
655 |
implVendor = attr.getValue(Attributes.Name.IMPLEMENTATION_VENDOR); |
|
656 |
if (sealed == null) |
|
657 |
sealed = attr.getValue(Attributes.Name.SEALED); |
|
658 |
} |
|
659 |
||
660 |
// package is sealed |
|
661 |
if ("true".equalsIgnoreCase(sealed)) |
|
662 |
sealBase = url; |
|
663 |
} |
|
664 |
return definePackage(pn, |
|
665 |
specTitle, |
|
666 |
specVersion, |
|
667 |
specVendor, |
|
668 |
implTitle, |
|
669 |
implVersion, |
|
670 |
implVendor, |
|
671 |
sealBase); |
|
672 |
} |
|
673 |
||
674 |
/** |
|
675 |
* Returns {@code true} if the specified package name is sealed according to |
|
676 |
* the given manifest. |
|
677 |
*/ |
|
678 |
private boolean isSealed(String pn, Manifest man) { |
|
679 |
String path = pn.replace('.', '/').concat("/"); |
|
680 |
Attributes attr = man.getAttributes(path); |
|
681 |
String sealed = null; |
|
682 |
if (attr != null) |
|
683 |
sealed = attr.getValue(Attributes.Name.SEALED); |
|
684 |
if (sealed == null && (attr = man.getMainAttributes()) != null) |
|
685 |
sealed = attr.getValue(Attributes.Name.SEALED); |
|
686 |
return "true".equalsIgnoreCase(sealed); |
|
687 |
} |
|
688 |
||
689 |
// -- permissions |
|
690 |
||
691 |
/** |
|
692 |
* Returns the permissions for the given CodeSource. |
|
693 |
*/ |
|
694 |
@Override |
|
695 |
protected PermissionCollection getPermissions(CodeSource cs) { |
|
696 |
PermissionCollection perms = super.getPermissions(cs); |
|
697 |
||
698 |
// add the permission to access the resource |
|
699 |
URL url = cs.getLocation(); |
|
700 |
if (url == null) |
|
701 |
return perms; |
|
702 |
Permission p = null; |
|
703 |
try { |
|
704 |
p = url.openConnection().getPermission(); |
|
705 |
if (p != null) { |
|
706 |
// for directories then need recursive access |
|
707 |
if (p instanceof FilePermission) { |
|
708 |
String path = p.getName(); |
|
709 |
if (path.endsWith(File.separator)) { |
|
710 |
path += "-"; |
|
711 |
p = new FilePermission(path, "read"); |
|
712 |
} |
|
713 |
} |
|
714 |
perms.add(p); |
|
715 |
} |
|
716 |
} catch (IOException ioe) { } |
|
717 |
||
718 |
return perms; |
|
719 |
} |
|
720 |
||
721 |
||
722 |
// -- miscellaneous supporting methods |
|
723 |
||
724 |
/** |
|
725 |
* Returns the ModuleReader for the given module. |
|
726 |
*/ |
|
727 |
private ModuleReader moduleReaderFor(ModuleReference mref) { |
|
728 |
return moduleToReader.computeIfAbsent(mref, m -> createModuleReader(mref)); |
|
729 |
} |
|
730 |
||
731 |
/** |
|
732 |
* Creates a ModuleReader for the given module. |
|
733 |
*/ |
|
734 |
private ModuleReader createModuleReader(ModuleReference mref) { |
|
735 |
try { |
|
736 |
return mref.open(); |
|
737 |
} catch (IOException e) { |
|
738 |
// Return a null module reader to avoid a future class load |
|
739 |
// attempting to open the module again. |
|
740 |
return new NullModuleReader(); |
|
741 |
} |
|
742 |
} |
|
743 |
||
744 |
/** |
|
745 |
* A ModuleReader that doesn't read any resources. |
|
746 |
*/ |
|
747 |
private static class NullModuleReader implements ModuleReader { |
|
748 |
@Override |
|
749 |
public Optional<URI> find(String name) { |
|
750 |
return Optional.empty(); |
|
751 |
} |
|
752 |
@Override |
|
41817
b90ad1de93ea
8168789: ModuleReader.list and ModuleFinder.of update
alanb
parents:
37779
diff
changeset
|
753 |
public Stream<String> list() { |
b90ad1de93ea
8168789: ModuleReader.list and ModuleFinder.of update
alanb
parents:
37779
diff
changeset
|
754 |
return Stream.empty(); |
b90ad1de93ea
8168789: ModuleReader.list and ModuleFinder.of update
alanb
parents:
37779
diff
changeset
|
755 |
} |
b90ad1de93ea
8168789: ModuleReader.list and ModuleFinder.of update
alanb
parents:
37779
diff
changeset
|
756 |
@Override |
36511 | 757 |
public void close() { |
758 |
throw new InternalError("Should not get here"); |
|
759 |
} |
|
760 |
}; |
|
761 |
||
762 |
/** |
|
763 |
* Checks access to the given URL. We use URLClassPath for consistent |
|
764 |
* checking with java.net.URLClassLoader. |
|
765 |
*/ |
|
766 |
private static URL checkURL(URL url) { |
|
767 |
return URLClassPath.checkURL(url); |
|
768 |
} |
|
769 |
} |