Merge
This commit is contained in:
commit
f4fe8c34f2
@ -278,7 +278,6 @@ SUNWprivate_1.1 {
|
||||
Java_java_lang_Module_addExports0;
|
||||
Java_java_lang_Module_addExportsToAll0;
|
||||
Java_java_lang_Module_addExportsToAllUnnamed0;
|
||||
Java_java_lang_Module_addPackage0;
|
||||
|
||||
Java_jdk_internal_loader_BootLoader_getSystemPackageLocation;
|
||||
Java_jdk_internal_loader_BootLoader_getSystemPackageNames;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -50,6 +50,7 @@ import java.net.URL;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@ -2066,25 +2067,6 @@ public final class Class<T> implements java.io.Serializable,
|
||||
return getReflectionFactory().copyMethod(method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code Method} object that reflects the specified public
|
||||
* member method of the class or interface represented by this
|
||||
* {@code Class} object.
|
||||
*
|
||||
* @param name the name of the method
|
||||
* @param parameterTypes the list of parameters
|
||||
* @return the {@code Method} object that matches the specified
|
||||
* {@code name} and {@code parameterTypes}; {@code null}
|
||||
* if the method is not found or the name is
|
||||
* "<init>"or "<clinit>".
|
||||
*/
|
||||
Method getMethodOrNull(String name, Class<?>... parameterTypes) {
|
||||
Objects.requireNonNull(name);
|
||||
Method method = getMethod0(name, parameterTypes);
|
||||
return method == null ? null : getReflectionFactory().copyMethod(method);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a {@code Constructor} object that reflects the specified
|
||||
* public constructor of the class represented by this {@code Class}
|
||||
@ -2225,7 +2207,6 @@ public final class Class<T> implements java.io.Serializable,
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* Returns an array containing {@code Method} objects reflecting all the
|
||||
* declared methods of the class or interface represented by this {@code
|
||||
* Class} object, including public, protected, default (package)
|
||||
@ -2453,6 +2434,30 @@ public final class Class<T> implements java.io.Serializable,
|
||||
return getReflectionFactory().copyMethod(method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of {@code Method} objects for the declared public
|
||||
* methods of this class or interface that have the specified method name
|
||||
* and parameter types.
|
||||
*
|
||||
* @param name the name of the method
|
||||
* @param parameterTypes the parameter array
|
||||
* @return the list of {@code Method} objects for the public methods of
|
||||
* this class matching the specified name and parameters
|
||||
*/
|
||||
List<Method> getDeclaredPublicMethods(String name, Class<?>... parameterTypes) {
|
||||
Method[] methods = privateGetDeclaredMethods(/* publicOnly */ true);
|
||||
ReflectionFactory factory = getReflectionFactory();
|
||||
List<Method> result = new ArrayList<>();
|
||||
for (Method method : methods) {
|
||||
if (method.getName().equals(name)
|
||||
&& Arrays.equals(
|
||||
factory.getExecutableSharedParameterTypes(method),
|
||||
parameterTypes)) {
|
||||
result.add(factory.copyMethod(method));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code Constructor} object that reflects the specified
|
||||
|
@ -93,12 +93,20 @@ import sun.security.util.SecurityConstants;
|
||||
* <p> Class loaders may typically be used by security managers to indicate
|
||||
* security domains.
|
||||
*
|
||||
* <p> In addition to loading classes, a class loader is also responsible for
|
||||
* locating resources. A resource is some data (a "{@code .class}" file,
|
||||
* configuration data, or an image for example) that is identified with an
|
||||
* abstract '/'-separated path name. Resources are typically packaged with an
|
||||
* application or library so that they can be located by code in the
|
||||
* application or library. In some cases, the resources are included so that
|
||||
* they can be located by other libraries.
|
||||
*
|
||||
* <p> The {@code ClassLoader} class uses a delegation model to search for
|
||||
* classes and resources. Each instance of {@code ClassLoader} has an
|
||||
* associated parent class loader. When requested to find a class or
|
||||
* resource, a {@code ClassLoader} instance will delegate the search for the
|
||||
* class or resource to its parent class loader before attempting to find the
|
||||
* class or resource itself.
|
||||
* associated parent class loader. When requested to find a class or
|
||||
* resource, a {@code ClassLoader} instance will usually delegate the search
|
||||
* for the class or resource to its parent class loader before attempting to
|
||||
* find the class or resource itself.
|
||||
*
|
||||
* <p> Class loaders that support concurrent loading of classes are known as
|
||||
* <em>{@linkplain #isRegisteredAsParallelCapable() parallel capable}</em> class
|
||||
@ -129,11 +137,13 @@ import sun.security.util.SecurityConstants;
|
||||
* classes and JDK-specific run-time classes that are defined by the
|
||||
* platform class loader or its ancestors.
|
||||
* <p> To allow for upgrading/overriding of modules defined to the platform
|
||||
* class loader, and where classes in the upgraded version link to
|
||||
* classes in modules defined to the application class loader, the
|
||||
* platform class loader may delegate to the application class loader.
|
||||
* In other words, classes in named modules defined to the application
|
||||
* class loader may be visible to the platform class loader. </li>
|
||||
* class loader, and where upgraded modules read modules defined to class
|
||||
* loaders other than the platform class loader and its ancestors, then
|
||||
* the platform class loader may have to delegate to other class loaders,
|
||||
* the application class loader for example.
|
||||
* In other words, classes in named modules defined to class loaders
|
||||
* other than the platform class loader and its ancestors may be visible
|
||||
* to the platform class loader. </li>
|
||||
* <li><p>{@linkplain #getSystemClassLoader() System class loader}.
|
||||
* It is also known as <em>application class loader</em> and is distinct
|
||||
* from the platform class loader.
|
||||
@ -498,7 +508,7 @@ public abstract class ClassLoader {
|
||||
*
|
||||
* <li><p> Invoke the {@link #loadClass(String) loadClass} method
|
||||
* on the parent class loader. If the parent is {@code null} the class
|
||||
* loader built-in to the virtual machine is used, instead. </p></li>
|
||||
* loader built into the virtual machine is used, instead. </p></li>
|
||||
*
|
||||
* <li><p> Invoke the {@link #findClass(String)} method to find the
|
||||
* class. </p></li>
|
||||
@ -681,8 +691,9 @@ public abstract class ClassLoader {
|
||||
* This method should be overridden by class loader implementations that
|
||||
* follow the delegation model for loading classes, and will be invoked by
|
||||
* the {@link #loadClass loadClass} method after checking the
|
||||
* parent class loader for the requested class. The default implementation
|
||||
* throws a {@code ClassNotFoundException}.
|
||||
* parent class loader for the requested class.
|
||||
*
|
||||
* @implSpec The default implementation throws {@code ClassNotFoundException}.
|
||||
*
|
||||
* @param name
|
||||
* The <a href="#name">binary name</a> of the class
|
||||
@ -1127,8 +1138,9 @@ public abstract class ClassLoader {
|
||||
putIfAbsent(pname, (certs == null? nocerts:certs));
|
||||
}
|
||||
if (pcerts != null && !compareCerts(pcerts, certs)) {
|
||||
throw new SecurityException("class \""+ name +
|
||||
"\"'s signer information does not match signer information of other classes in the same package");
|
||||
throw new SecurityException("class \"" + name
|
||||
+ "\"'s signer information does not match signer information"
|
||||
+ " of other classes in the same package");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1329,12 +1341,7 @@ public abstract class ClassLoader {
|
||||
* that is independent of the location of the code.
|
||||
*
|
||||
* <p> The name of a resource is a '{@code /}'-separated path name that
|
||||
* identifies the resource.
|
||||
*
|
||||
* <p> This method will first search the parent class loader for the
|
||||
* resource; if the parent is {@code null} the path of the class loader
|
||||
* built-in to the virtual machine is searched. That failing, this method
|
||||
* will invoke {@link #findResource(String)} to find the resource. </p>
|
||||
* identifies the resource. </p>
|
||||
*
|
||||
* <p> Resources in named modules are subject to the encapsulation rules
|
||||
* specified by {@link Module#getResourceAsStream Module.getResourceAsStream}.
|
||||
@ -1344,6 +1351,11 @@ public abstract class ClassLoader {
|
||||
* opened} unconditionally (even if the caller of this method is in the
|
||||
* same module as the resource). </p>
|
||||
*
|
||||
* @implSpec The default implementation will first search the parent class
|
||||
* loader for the resource; if the parent is {@code null} the path of the
|
||||
* class loader built into the virtual machine is searched. If not found,
|
||||
* this method will invoke {@link #findResource(String)} to find the resource.
|
||||
*
|
||||
* @apiNote Where several modules are defined to the same class loader,
|
||||
* and where more than one module contains a resource with the given name,
|
||||
* then the ordering that modules are searched is not specified and may be
|
||||
@ -1387,10 +1399,7 @@ public abstract class ClassLoader {
|
||||
* that is independent of the location of the code.
|
||||
*
|
||||
* <p> The name of a resource is a {@code /}-separated path name that
|
||||
* identifies the resource.
|
||||
*
|
||||
* <p> The delegation order for searching is described in the documentation
|
||||
* for {@link #getResource(String)}. </p>
|
||||
* identifies the resource. </p>
|
||||
*
|
||||
* <p> Resources in named modules are subject to the encapsulation rules
|
||||
* specified by {@link Module#getResourceAsStream Module.getResourceAsStream}.
|
||||
@ -1398,7 +1407,15 @@ public abstract class ClassLoader {
|
||||
* name ending with "{@code .class}", this method will only find resources in
|
||||
* packages of named modules when the package is {@link Module#isOpen(String)
|
||||
* opened} unconditionally (even if the caller of this method is in the
|
||||
* same module as the resource).</p>
|
||||
* same module as the resource). </p>
|
||||
*
|
||||
* @implSpec The default implementation will first search the parent class
|
||||
* loader for the resource; if the parent is {@code null} the path of the
|
||||
* class loader built into the virtual machine is searched. It then
|
||||
* invokes {@link #findResources(String)} to find the resources with the
|
||||
* name in this class loader. It returns an enumeration whose elements
|
||||
* are the URLs found by searching the parent class loader followed by
|
||||
* the elements found with {@code findResources}.
|
||||
*
|
||||
* @apiNote Where several modules are defined to the same class loader,
|
||||
* and where more than one module contains a resource with the given name,
|
||||
@ -1424,8 +1441,6 @@ public abstract class ClassLoader {
|
||||
* If I/O errors occur
|
||||
* @throws NullPointerException If {@code name} is {@code null}
|
||||
*
|
||||
* @see #findResources(String)
|
||||
*
|
||||
* @since 1.2
|
||||
* @revised 9
|
||||
* @spec JPMS
|
||||
@ -1453,9 +1468,6 @@ public abstract class ClassLoader {
|
||||
* <p> The name of a resource is a {@code /}-separated path name that
|
||||
* identifies the resource.
|
||||
*
|
||||
* <p> The search order is described in the documentation for {@link
|
||||
* #getResource(String)}.
|
||||
*
|
||||
* <p> The resources will be located when the returned stream is evaluated.
|
||||
* If the evaluation results in an {@code IOException} then the I/O
|
||||
* exception is wrapped in an {@link UncheckedIOException} that is then
|
||||
@ -1469,6 +1481,10 @@ public abstract class ClassLoader {
|
||||
* opened} unconditionally (even if the caller of this method is in the
|
||||
* same module as the resource). </p>
|
||||
*
|
||||
* @implSpec The default implementation invokes {@link #getResources(String)
|
||||
* getResources} to find all the resources with the given name and returns
|
||||
* a stream with the elements in the enumeration as the source.
|
||||
*
|
||||
* @apiNote When overriding this method it is recommended that an
|
||||
* implementation ensures that any delegation is consistent with the {@link
|
||||
* #getResource(java.lang.String) getResource(String)} method. This should
|
||||
@ -1486,8 +1502,6 @@ public abstract class ClassLoader {
|
||||
*
|
||||
* @throws NullPointerException If {@code name} is {@code null}
|
||||
*
|
||||
* @see #findResources(String)
|
||||
*
|
||||
* @since 9
|
||||
*/
|
||||
public Stream<URL> resources(String name) {
|
||||
@ -1506,7 +1520,7 @@ public abstract class ClassLoader {
|
||||
|
||||
/**
|
||||
* Finds the resource with the given name. Class loader implementations
|
||||
* should override this method to specify where to find resources.
|
||||
* should override this method.
|
||||
*
|
||||
* <p> For resources in named modules then the method must implement the
|
||||
* rules for encapsulation specified in the {@code Module} {@link
|
||||
@ -1515,6 +1529,8 @@ public abstract class ClassLoader {
|
||||
* modules unless the package is {@link Module#isOpen(String) opened}
|
||||
* unconditionally. </p>
|
||||
*
|
||||
* @implSpec The default implementation returns {@code null}.
|
||||
*
|
||||
* @param name
|
||||
* The resource name
|
||||
*
|
||||
@ -1535,8 +1551,7 @@ public abstract class ClassLoader {
|
||||
/**
|
||||
* Returns an enumeration of {@link java.net.URL URL} objects
|
||||
* representing all the resources with the given name. Class loader
|
||||
* implementations should override this method to specify where to load
|
||||
* resources from.
|
||||
* implementations should override this method.
|
||||
*
|
||||
* <p> For resources in named modules then the method must implement the
|
||||
* rules for encapsulation specified in the {@code Module} {@link
|
||||
@ -1545,6 +1560,9 @@ public abstract class ClassLoader {
|
||||
* modules unless the package is {@link Module#isOpen(String) opened}
|
||||
* unconditionally. </p>
|
||||
*
|
||||
* @implSpec The default implementation returns an enumeration that
|
||||
* contains no elements.
|
||||
*
|
||||
* @param name
|
||||
* The resource name
|
||||
*
|
||||
@ -1899,7 +1917,8 @@ public abstract class ClassLoader {
|
||||
// the system class loader is the built-in app class loader during startup
|
||||
return getBuiltinAppClassLoader();
|
||||
case 3:
|
||||
throw new InternalError("getSystemClassLoader should only be called after VM booted");
|
||||
String msg = "getSystemClassLoader should only be called after VM booted";
|
||||
throw new InternalError(msg);
|
||||
case 4:
|
||||
// system fully initialized
|
||||
assert VM.isBooted() && scl != null;
|
||||
|
@ -63,8 +63,8 @@ public class LayerInstantiationException extends RuntimeException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@code FindException} with the given detail message
|
||||
* and cause.
|
||||
* Constructs a {@code LayerInstantiationException} with the given detail
|
||||
* message and cause.
|
||||
*
|
||||
* @param msg
|
||||
* The detail message; can be {@code null}
|
||||
@ -74,6 +74,5 @@ public class LayerInstantiationException extends RuntimeException {
|
||||
public LayerInstantiationException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,7 @@ import java.security.PrivilegedAction;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@ -55,8 +56,10 @@ import java.util.stream.Stream;
|
||||
|
||||
import jdk.internal.loader.BuiltinClassLoader;
|
||||
import jdk.internal.loader.BootLoader;
|
||||
import jdk.internal.loader.ClassLoaders;
|
||||
import jdk.internal.misc.JavaLangAccess;
|
||||
import jdk.internal.misc.SharedSecrets;
|
||||
import jdk.internal.module.IllegalAccessLogger;
|
||||
import jdk.internal.module.ModuleLoaderMap;
|
||||
import jdk.internal.module.ServicesCatalog;
|
||||
import jdk.internal.module.Resources;
|
||||
@ -162,7 +165,6 @@ public final class Module implements AnnotatedElement {
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns {@code true} if this module is a named module.
|
||||
*
|
||||
@ -249,12 +251,10 @@ public final class Module implements AnnotatedElement {
|
||||
|
||||
// special Module to mean "all unnamed modules"
|
||||
private static final Module ALL_UNNAMED_MODULE = new Module(null);
|
||||
private static final Set<Module> ALL_UNNAMED_MODULE_SET = Set.of(ALL_UNNAMED_MODULE);
|
||||
|
||||
// special Module to mean "everyone"
|
||||
private static final Module EVERYONE_MODULE = new Module(null);
|
||||
|
||||
// set contains EVERYONE_MODULE, used when a package is opened or
|
||||
// exported unconditionally
|
||||
private static final Set<Module> EVERYONE_SET = Set.of(EVERYONE_MODULE);
|
||||
|
||||
|
||||
@ -534,12 +534,12 @@ public final class Module implements AnnotatedElement {
|
||||
return true;
|
||||
|
||||
// all packages are exported/open to self
|
||||
if (other == this && containsPackage(pn))
|
||||
if (other == this && descriptor.packages().contains(pn))
|
||||
return true;
|
||||
|
||||
// all packages in open and automatic modules are open
|
||||
if (descriptor.isOpen() || descriptor.isAutomatic())
|
||||
return containsPackage(pn);
|
||||
return descriptor.packages().contains(pn);
|
||||
|
||||
// exported/opened via module declaration/descriptor
|
||||
if (isStaticallyExportedOrOpen(pn, other, open))
|
||||
@ -555,42 +555,48 @@ public final class Module implements AnnotatedElement {
|
||||
|
||||
/**
|
||||
* Returns {@code true} if this module exports or opens a package to
|
||||
* the given module via its module declaration.
|
||||
* the given module via its module declaration or CLI options.
|
||||
*/
|
||||
private boolean isStaticallyExportedOrOpen(String pn, Module other, boolean open) {
|
||||
// package is open to everyone or <other>
|
||||
// test if package is open to everyone or <other>
|
||||
Map<String, Set<Module>> openPackages = this.openPackages;
|
||||
if (openPackages != null) {
|
||||
Set<Module> targets = openPackages.get(pn);
|
||||
if (targets != null) {
|
||||
if (targets.contains(EVERYONE_MODULE))
|
||||
return true;
|
||||
if (other != EVERYONE_MODULE && targets.contains(other))
|
||||
return true;
|
||||
}
|
||||
if (openPackages != null && allows(openPackages.get(pn), other)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!open) {
|
||||
// package is exported to everyone or <other>
|
||||
// test package is exported to everyone or <other>
|
||||
Map<String, Set<Module>> exportedPackages = this.exportedPackages;
|
||||
if (exportedPackages != null) {
|
||||
Set<Module> targets = exportedPackages.get(pn);
|
||||
if (targets != null) {
|
||||
if (targets.contains(EVERYONE_MODULE))
|
||||
return true;
|
||||
if (other != EVERYONE_MODULE && targets.contains(other))
|
||||
return true;
|
||||
}
|
||||
if (exportedPackages != null && allows(exportedPackages.get(pn), other)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if targets is non-null and contains EVERYONE_MODULE
|
||||
* or the given module. Also returns true if the given module is an unnamed
|
||||
* module and targets contains ALL_UNNAMED_MODULE.
|
||||
*/
|
||||
private boolean allows(Set<Module> targets, Module module) {
|
||||
if (targets != null) {
|
||||
if (targets.contains(EVERYONE_MODULE))
|
||||
return true;
|
||||
if (module != EVERYONE_MODULE) {
|
||||
if (targets.contains(module))
|
||||
return true;
|
||||
if (!module.isNamed() && targets.contains(ALL_UNNAMED_MODULE))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if this module reflectively exports or opens given
|
||||
* package package to the given module.
|
||||
* Returns {@code true} if this module reflectively exports or opens the
|
||||
* given package to the given module.
|
||||
*/
|
||||
private boolean isReflectivelyExportedOrOpen(String pn, Module other, boolean open) {
|
||||
// exported or open to all modules
|
||||
@ -632,6 +638,22 @@ public final class Module implements AnnotatedElement {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if this module reflectively exports the
|
||||
* given package to the given module.
|
||||
*/
|
||||
boolean isReflectivelyExported(String pn, Module other) {
|
||||
return isReflectivelyExportedOrOpen(pn, other, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if this module reflectively opens the
|
||||
* given package to the given module.
|
||||
*/
|
||||
boolean isReflectivelyOpened(String pn, Module other) {
|
||||
return isReflectivelyExportedOrOpen(pn, other, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If the caller's module is this module then update this module to export
|
||||
@ -800,7 +822,7 @@ public final class Module implements AnnotatedElement {
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates this module to export a package to all unnamed modules.
|
||||
* Updates this module to open a package to all unnamed modules.
|
||||
*
|
||||
* @apiNote Used by the --add-opens command line option.
|
||||
*/
|
||||
@ -808,7 +830,6 @@ public final class Module implements AnnotatedElement {
|
||||
implAddExportsOrOpens(pn, Module.ALL_UNNAMED_MODULE, true, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates a module to export or open a module to another module.
|
||||
*
|
||||
@ -825,12 +846,31 @@ public final class Module implements AnnotatedElement {
|
||||
if (!isNamed() || descriptor.isOpen() || descriptor.isAutomatic())
|
||||
return;
|
||||
|
||||
// nothing to do if already exported/open to other
|
||||
if (implIsExportedOrOpen(pn, other, open))
|
||||
return;
|
||||
// check if the package is already exported/open to other
|
||||
if (implIsExportedOrOpen(pn, other, open)) {
|
||||
|
||||
// if the package is exported/open for illegal access then we need
|
||||
// to record that it has also been exported/opened reflectively so
|
||||
// that the IllegalAccessLogger doesn't emit a warning.
|
||||
boolean needToAdd = false;
|
||||
if (!other.isNamed()) {
|
||||
IllegalAccessLogger l = IllegalAccessLogger.illegalAccessLogger();
|
||||
if (l != null) {
|
||||
if (open) {
|
||||
needToAdd = l.isOpenForIllegalAccess(this, pn);
|
||||
} else {
|
||||
needToAdd = l.isExportedForIllegalAccess(this, pn);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!needToAdd) {
|
||||
// nothing to do
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// can only export a package in the module
|
||||
if (!containsPackage(pn)) {
|
||||
if (!descriptor.packages().contains(pn)) {
|
||||
throw new IllegalArgumentException("package " + pn
|
||||
+ " not in contents");
|
||||
}
|
||||
@ -850,7 +890,6 @@ public final class Module implements AnnotatedElement {
|
||||
Map<String, Boolean> map = reflectivelyExports
|
||||
.computeIfAbsent(this, other,
|
||||
(m1, m2) -> new ConcurrentHashMap<>());
|
||||
|
||||
if (open) {
|
||||
map.put(pn, Boolean.TRUE); // may need to promote from FALSE to TRUE
|
||||
} else {
|
||||
@ -858,6 +897,38 @@ public final class Module implements AnnotatedElement {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a module to open all packages returned by the given iterator to
|
||||
* all unnamed modules.
|
||||
*
|
||||
* @apiNote Used during startup to open packages for illegal access.
|
||||
*/
|
||||
void implAddOpensToAllUnnamed(Iterator<String> iterator) {
|
||||
if (jdk.internal.misc.VM.isModuleSystemInited()) {
|
||||
throw new IllegalStateException("Module system already initialized");
|
||||
}
|
||||
|
||||
// replace this module's openPackages map with a new map that opens
|
||||
// the packages to all unnamed modules.
|
||||
Map<String, Set<Module>> openPackages = this.openPackages;
|
||||
if (openPackages == null) {
|
||||
openPackages = new HashMap<>();
|
||||
} else {
|
||||
openPackages = new HashMap<>(openPackages);
|
||||
}
|
||||
while (iterator.hasNext()) {
|
||||
String pn = iterator.next();
|
||||
Set<Module> prev = openPackages.putIfAbsent(pn, ALL_UNNAMED_MODULE_SET);
|
||||
if (prev != null) {
|
||||
prev.add(ALL_UNNAMED_MODULE);
|
||||
}
|
||||
|
||||
// update VM to export the package
|
||||
addExportsToAllUnnamed0(this, pn);
|
||||
}
|
||||
this.openPackages = openPackages;
|
||||
}
|
||||
|
||||
|
||||
// -- services --
|
||||
|
||||
@ -947,19 +1018,6 @@ public final class Module implements AnnotatedElement {
|
||||
|
||||
// -- packages --
|
||||
|
||||
// Additional packages that are added to the module at run-time.
|
||||
private volatile Map<String, Boolean> extraPackages;
|
||||
|
||||
private boolean containsPackage(String pn) {
|
||||
if (descriptor.packages().contains(pn))
|
||||
return true;
|
||||
Map<String, Boolean> extraPackages = this.extraPackages;
|
||||
if (extraPackages != null && extraPackages.containsKey(pn))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the set of package names for the packages in this module.
|
||||
*
|
||||
@ -974,89 +1032,19 @@ public final class Module implements AnnotatedElement {
|
||||
*/
|
||||
public Set<String> getPackages() {
|
||||
if (isNamed()) {
|
||||
|
||||
Set<String> packages = descriptor.packages();
|
||||
Map<String, Boolean> extraPackages = this.extraPackages;
|
||||
if (extraPackages == null) {
|
||||
return packages;
|
||||
} else {
|
||||
return Stream.concat(packages.stream(),
|
||||
extraPackages.keySet().stream())
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
return descriptor.packages();
|
||||
} else {
|
||||
// unnamed module
|
||||
Stream<Package> packages;
|
||||
if (loader == null) {
|
||||
packages = BootLoader.packages();
|
||||
} else {
|
||||
packages = SharedSecrets.getJavaLangAccess().packages(loader);
|
||||
packages = loader.packages();
|
||||
}
|
||||
return packages.map(Package::getName).collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a package to this module without notifying the VM.
|
||||
*
|
||||
* @apiNote This method is VM white-box testing.
|
||||
*/
|
||||
void implAddPackageNoSync(String pn) {
|
||||
implAddPackage(pn.replace('/', '.'), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a package to this module.
|
||||
*
|
||||
* If {@code syncVM} is {@code true} then the VM is notified. This method is
|
||||
* a no-op if this is an unnamed module or the module already contains the
|
||||
* package.
|
||||
*
|
||||
* @throws IllegalArgumentException if the package name is not legal
|
||||
* @throws IllegalStateException if the package is defined to another module
|
||||
*/
|
||||
private void implAddPackage(String pn, boolean syncVM) {
|
||||
// no-op if unnamed module
|
||||
if (!isNamed())
|
||||
return;
|
||||
|
||||
// no-op if module contains the package
|
||||
if (containsPackage(pn))
|
||||
return;
|
||||
|
||||
// check package name is legal for named modules
|
||||
if (pn.isEmpty())
|
||||
throw new IllegalArgumentException("Cannot add <unnamed> package");
|
||||
for (int i=0; i<pn.length(); i++) {
|
||||
char c = pn.charAt(i);
|
||||
if (c == '/' || c == ';' || c == '[') {
|
||||
throw new IllegalArgumentException("Illegal character: " + c);
|
||||
}
|
||||
}
|
||||
|
||||
// create extraPackages if needed
|
||||
Map<String, Boolean> extraPackages = this.extraPackages;
|
||||
if (extraPackages == null) {
|
||||
synchronized (this) {
|
||||
extraPackages = this.extraPackages;
|
||||
if (extraPackages == null)
|
||||
this.extraPackages = extraPackages = new ConcurrentHashMap<>();
|
||||
}
|
||||
}
|
||||
|
||||
// update VM first in case it fails. This is a no-op if another thread
|
||||
// beats us to add the package first
|
||||
if (syncVM) {
|
||||
// throws IllegalStateException if defined to another module
|
||||
addPackage0(this, pn);
|
||||
if (descriptor.isOpen() || descriptor.isAutomatic()) {
|
||||
addExportsToAll0(this, pn);
|
||||
}
|
||||
}
|
||||
extraPackages.putIfAbsent(pn, Boolean.TRUE);
|
||||
}
|
||||
|
||||
|
||||
// -- creating Module objects --
|
||||
|
||||
@ -1075,18 +1063,22 @@ public final class Module implements AnnotatedElement {
|
||||
Map<String, Module> nameToModule = new HashMap<>();
|
||||
Map<String, ClassLoader> moduleToLoader = new HashMap<>();
|
||||
|
||||
boolean isBootLayer = (ModuleLayer.boot() == null);
|
||||
Set<ClassLoader> loaders = new HashSet<>();
|
||||
boolean hasPlatformModules = false;
|
||||
|
||||
// map each module to a class loader
|
||||
for (ResolvedModule resolvedModule : cf.modules()) {
|
||||
String name = resolvedModule.name();
|
||||
ClassLoader loader = clf.apply(name);
|
||||
if (loader != null) {
|
||||
moduleToLoader.put(name, loader);
|
||||
moduleToLoader.put(name, loader);
|
||||
if (loader == null || loader == ClassLoaders.platformClassLoader()) {
|
||||
if (!(clf instanceof ModuleLoaderMap.Mapper)) {
|
||||
throw new IllegalArgumentException("loader can't be 'null'"
|
||||
+ " or the platform class loader");
|
||||
}
|
||||
hasPlatformModules = true;
|
||||
} else {
|
||||
loaders.add(loader);
|
||||
} else if (!(clf instanceof ModuleLoaderMap.Mapper)) {
|
||||
throw new IllegalArgumentException("loader can't be 'null'");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1098,7 +1090,7 @@ public final class Module implements AnnotatedElement {
|
||||
URI uri = mref.location().orElse(null);
|
||||
ClassLoader loader = moduleToLoader.get(resolvedModule.name());
|
||||
Module m;
|
||||
if (loader == null && isBootLayer && name.equals("java.base")) {
|
||||
if (loader == null && name.equals("java.base")) {
|
||||
// java.base is already defined to the VM
|
||||
m = Object.class.getModule();
|
||||
} else {
|
||||
@ -1157,8 +1149,12 @@ public final class Module implements AnnotatedElement {
|
||||
initExportsAndOpens(m, nameToSource, nameToModule, layer.parents());
|
||||
}
|
||||
|
||||
// register the modules in the boot layer
|
||||
if (isBootLayer) {
|
||||
// if there are modules defined to the boot or platform class loaders
|
||||
// then register the modules in the class loader's services catalog
|
||||
if (hasPlatformModules) {
|
||||
ClassLoader pcl = ClassLoaders.platformClassLoader();
|
||||
ServicesCatalog bootCatalog = BootLoader.getServicesCatalog();
|
||||
ServicesCatalog pclCatalog = ServicesCatalog.getServicesCatalog(pcl);
|
||||
for (ResolvedModule resolvedModule : cf.modules()) {
|
||||
ModuleReference mref = resolvedModule.reference();
|
||||
ModuleDescriptor descriptor = mref.descriptor();
|
||||
@ -1166,13 +1162,11 @@ public final class Module implements AnnotatedElement {
|
||||
String name = descriptor.name();
|
||||
Module m = nameToModule.get(name);
|
||||
ClassLoader loader = moduleToLoader.get(name);
|
||||
ServicesCatalog catalog;
|
||||
if (loader == null) {
|
||||
catalog = BootLoader.getServicesCatalog();
|
||||
} else {
|
||||
catalog = ServicesCatalog.getServicesCatalog(loader);
|
||||
bootCatalog.register(m);
|
||||
} else if (loader == pcl) {
|
||||
pclCatalog.register(m);
|
||||
}
|
||||
catalog.register(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1587,7 +1581,4 @@ public final class Module implements AnnotatedElement {
|
||||
|
||||
// JVM_AddModuleExportsToAllUnnamed
|
||||
private static native void addExportsToAllUnnamed0(Module from, String pn);
|
||||
|
||||
// JVM_AddModulePackage
|
||||
private static native void addPackage0(Module m, String pn);
|
||||
}
|
||||
|
@ -244,6 +244,32 @@ public final class ModuleLayer {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates module {@code source} in the layer to export a package to
|
||||
* module {@code target}. This method is a no-op if {@code source}
|
||||
* already exports the package to at least {@code target}.
|
||||
*
|
||||
* @param source
|
||||
* The source module
|
||||
* @param pn
|
||||
* The package name
|
||||
* @param target
|
||||
* The target module
|
||||
*
|
||||
* @return This controller
|
||||
*
|
||||
* @throws IllegalArgumentException
|
||||
* If {@code source} is not in the module layer or the package
|
||||
* is not in the source module
|
||||
*
|
||||
* @see Module#addExports
|
||||
*/
|
||||
public Controller addExports(Module source, String pn, Module target) {
|
||||
ensureInLayer(source);
|
||||
source.implAddExports(pn, target);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates module {@code source} in the layer to open a package to
|
||||
* module {@code target}. This method is a no-op if {@code source}
|
||||
@ -254,7 +280,7 @@ public final class ModuleLayer {
|
||||
* @param pn
|
||||
* The package name
|
||||
* @param target
|
||||
* The target module to read
|
||||
* The target module
|
||||
*
|
||||
* @return This controller
|
||||
*
|
||||
@ -397,7 +423,7 @@ public final class ModuleLayer {
|
||||
* class loader and defines all modules to that class loader.
|
||||
*
|
||||
* <p> The class loader created by this method implements <em>direct
|
||||
* delegation</em> when loading types from modules. When its {@link
|
||||
* delegation</em> when loading classes from modules. If the {@link
|
||||
* ClassLoader#loadClass(String, boolean) loadClass} method is invoked to
|
||||
* load a class then it uses the package name of the class to map it to a
|
||||
* module. This may be a module in this layer and hence defined to the same
|
||||
@ -408,6 +434,12 @@ public final class ModuleLayer {
|
||||
* When {@code loadClass} is invoked to load classes that do not map to a
|
||||
* module then it delegates to the parent class loader. </p>
|
||||
*
|
||||
* <p> The class loader created by this method locates resources
|
||||
* ({@link ClassLoader#getResource(String) getResource}, {@link
|
||||
* ClassLoader#getResources(String) getResources}, and other resource
|
||||
* methods) in all modules in the layer before searching the parent class
|
||||
* loader. </p>
|
||||
*
|
||||
* <p> Attempting to create a layer with all modules defined to the same
|
||||
* class loader can fail for the following reasons:
|
||||
*
|
||||
@ -417,8 +449,8 @@ public final class ModuleLayer {
|
||||
* configuration have the same package. </p></li>
|
||||
*
|
||||
* <li><p> <em>Split delegation</em>: The resulting class loader would
|
||||
* need to delegate to more than one class loader in order to load types
|
||||
* in a specific package. </p></li>
|
||||
* need to delegate to more than one class loader in order to load
|
||||
* classes in a specific package. </p></li>
|
||||
*
|
||||
* </ul>
|
||||
*
|
||||
@ -481,7 +513,7 @@ public final class ModuleLayer {
|
||||
* class loader.
|
||||
*
|
||||
* <p> The class loaders created by this method implement <em>direct
|
||||
* delegation</em> when loading types from modules. When {@link
|
||||
* delegation</em> when loading classes from modules. If the {@link
|
||||
* ClassLoader#loadClass(String, boolean) loadClass} method is invoked to
|
||||
* load a class then it uses the package name of the class to map it to a
|
||||
* module. The package may be in the module defined to the class loader.
|
||||
@ -489,9 +521,15 @@ public final class ModuleLayer {
|
||||
* module defined to the class loader. It may be in a package exported by a
|
||||
* module in a parent layer. The class loader delegates to the class loader
|
||||
* of the module, throwing {@code ClassNotFoundException} if not found by
|
||||
* that class loader.
|
||||
* When {@code loadClass} is invoked to load classes that do not map to a
|
||||
* module then it delegates to the parent class loader. </p>
|
||||
* that class loader. When {@code loadClass} is invoked to load a class
|
||||
* that does not map to a module then it delegates to the parent class
|
||||
* loader. </p>
|
||||
*
|
||||
* <p> The class loaders created by this method locate resources
|
||||
* ({@link ClassLoader#getResource(String) getResource}, {@link
|
||||
* ClassLoader#getResources(String) getResources}, and other resource
|
||||
* methods) in the module defined to the class loader before searching
|
||||
* the parent class loader. </p>
|
||||
*
|
||||
* <p> If there is a security manager then the class loaders created by
|
||||
* this method will load classes and resources with privileges that are
|
||||
@ -576,10 +614,9 @@ public final class ModuleLayer {
|
||||
* <p> In addition, a layer cannot be created if the configuration contains
|
||||
* a module named "{@code java.base}", a configuration contains a module
|
||||
* with a package named "{@code java}" or a package name starting with
|
||||
* "{@code java.}" and the module is mapped to a class loader other than
|
||||
* the {@link ClassLoader#getPlatformClassLoader() platform class loader},
|
||||
* or the function to map a module name to a class loader returns
|
||||
* {@code null}. </p>
|
||||
* "{@code java.}", or the function to map a module name to a class loader
|
||||
* returns {@code null} or the {@linkplain ClassLoader#getPlatformClassLoader()
|
||||
* platform class loader}. </p>
|
||||
*
|
||||
* <p> If the function to map a module name to class loader throws an error
|
||||
* or runtime exception then it is propagated to the caller of this method.
|
||||
|
@ -47,6 +47,8 @@ import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.nio.channels.Channel;
|
||||
import java.nio.channels.spi.SelectorProvider;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Properties;
|
||||
@ -2067,8 +2069,8 @@ public final class System {
|
||||
private static void setJavaLangAccess() {
|
||||
// Allow privileged classes outside of java.lang
|
||||
SharedSecrets.setJavaLangAccess(new JavaLangAccess() {
|
||||
public Method getMethodOrNull(Class<?> klass, String name, Class<?>... parameterTypes) {
|
||||
return klass.getMethodOrNull(name, parameterTypes);
|
||||
public List<Method> getDeclaredPublicMethods(Class<?> klass, String name, Class<?>... parameterTypes) {
|
||||
return klass.getDeclaredPublicMethods(name, parameterTypes);
|
||||
}
|
||||
public jdk.internal.reflect.ConstantPool getConstantPool(Class<?> klass) {
|
||||
return klass.getConstantPool();
|
||||
@ -2092,7 +2094,7 @@ public final class System {
|
||||
return Class.getExecutableTypeAnnotationBytes(executable);
|
||||
}
|
||||
public <E extends Enum<E>>
|
||||
E[] getEnumConstantsShared(Class<E> klass) {
|
||||
E[] getEnumConstantsShared(Class<E> klass) {
|
||||
return klass.getEnumConstantsShared();
|
||||
}
|
||||
public void blockedOn(Thread t, Interruptible b) {
|
||||
@ -2120,9 +2122,6 @@ public final class System {
|
||||
public Class<?> findBootstrapClassOrNull(ClassLoader cl, String name) {
|
||||
return cl.findBootstrapClassOrNull(name);
|
||||
}
|
||||
public Stream<Package> packages(ClassLoader cl) {
|
||||
return cl.packages();
|
||||
}
|
||||
public Package definePackage(ClassLoader cl, String name, Module module) {
|
||||
return cl.definePackage(name, module);
|
||||
}
|
||||
@ -2161,9 +2160,18 @@ public final class System {
|
||||
public void addOpensToAllUnnamed(Module m, String pn) {
|
||||
m.implAddOpensToAllUnnamed(pn);
|
||||
}
|
||||
public void addOpensToAllUnnamed(Module m, Iterator<String> packages) {
|
||||
m.implAddOpensToAllUnnamed(packages);
|
||||
}
|
||||
public void addUses(Module m, Class<?> service) {
|
||||
m.implAddUses(service);
|
||||
}
|
||||
public boolean isReflectivelyExported(Module m, String pn, Module other) {
|
||||
return m.isReflectivelyExported(pn, other);
|
||||
}
|
||||
public boolean isReflectivelyOpened(Module m, String pn, Module other) {
|
||||
return m.isReflectivelyOpened(pn, other);
|
||||
}
|
||||
public ServicesCatalog getServicesCatalog(ModuleLayer layer) {
|
||||
return layer.getServicesCatalog();
|
||||
}
|
||||
|
@ -212,7 +212,7 @@ public class MethodHandles {
|
||||
if (!callerModule.isNamed() && targetModule.isNamed()) {
|
||||
IllegalAccessLogger logger = IllegalAccessLogger.illegalAccessLogger();
|
||||
if (logger != null) {
|
||||
logger.logIfOpenedByBackdoor(lookup, targetClass);
|
||||
logger.logIfOpenedForIllegalAccess(lookup, targetClass);
|
||||
}
|
||||
}
|
||||
return new Lookup(targetClass);
|
||||
|
@ -43,11 +43,10 @@ import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* A configuration that is the result of <a href="package-summary.html#resolution">
|
||||
* resolution</a> or resolution with <a href="package-summary.html#servicebinding">
|
||||
* service binding</a>.
|
||||
* resolution</a> or resolution with <a href="#service-binding">service binding</a>.
|
||||
*
|
||||
* <p> A configuration encapsulates the <em>readability graph</em> that is the
|
||||
* output of resolution. A readability graph is a directed graph where the nodes
|
||||
* output of resolution. A readability graph is a directed graph whose vertices
|
||||
* are of type {@link ResolvedModule} and the edges represent the readability
|
||||
* amongst the modules. {@code Configuration} defines the {@link #modules()
|
||||
* modules()} method to get the set of resolved modules in the graph. {@code
|
||||
@ -176,8 +175,8 @@ public final class Configuration {
|
||||
* If resolution fails for any of the observability-related reasons
|
||||
* specified by the static {@code resolve} method
|
||||
* @throws ResolutionException
|
||||
* If any of the post-resolution consistency checks specified by
|
||||
* the static {@code resolve} method fail
|
||||
* If resolution fails any of the consistency checks specified by
|
||||
* the static {@code resolve} method
|
||||
* @throws SecurityException
|
||||
* If locating a module is denied by the security manager
|
||||
*/
|
||||
@ -219,8 +218,8 @@ public final class Configuration {
|
||||
* If resolution fails for any of the observability-related reasons
|
||||
* specified by the static {@code resolve} method
|
||||
* @throws ResolutionException
|
||||
* If any of the post-resolution consistency checks specified by
|
||||
* the static {@code resolve} method fail
|
||||
* If resolution fails any of the consistency checks specified by
|
||||
* the static {@code resolve} method
|
||||
* @throws SecurityException
|
||||
* If locating a module is denied by the security manager
|
||||
*/
|
||||
@ -234,7 +233,7 @@ public final class Configuration {
|
||||
|
||||
/**
|
||||
* Resolves a collection of root modules, with service binding, and with
|
||||
* the empty configuration as its parent. The post resolution checks
|
||||
* the empty configuration as its parent. The consistency checks
|
||||
* are optionally run.
|
||||
*
|
||||
* This method is used to create the configuration for the boot layer.
|
||||
@ -264,10 +263,9 @@ public final class Configuration {
|
||||
* or dependences that are located in a parent configuration are resolved
|
||||
* no further and are not included in the resulting configuration. </p>
|
||||
*
|
||||
* <p> When all modules have been resolved then the resulting dependency
|
||||
* graph is checked to ensure that it does not contain cycles. A
|
||||
* readability graph is constructed, and in conjunction with the module
|
||||
* exports and service use, checked for consistency. </p>
|
||||
* <p> When all modules have been enumerated then a readability graph
|
||||
* is computed, and in conjunction with the module exports and service use,
|
||||
* checked for consistency. </p>
|
||||
*
|
||||
* <p> Resolution may fail with {@code FindException} for the following
|
||||
* <em>observability-related</em> reasons: </p>
|
||||
@ -284,8 +282,8 @@ public final class Configuration {
|
||||
*
|
||||
* </ul>
|
||||
*
|
||||
* <p> Post-resolution consistency checks may fail with {@code
|
||||
* ResolutionException} for the following reasons: </p>
|
||||
* <p> Resolution may fail with {@code ResolutionException} if any of the
|
||||
* following consistency checks fail: </p>
|
||||
*
|
||||
* <ul>
|
||||
*
|
||||
@ -329,9 +327,11 @@ public final class Configuration {
|
||||
* root modules
|
||||
*
|
||||
* @throws FindException
|
||||
* If resolution fails for an observability-related reason
|
||||
* If resolution fails for any of observability-related reasons
|
||||
* specified above
|
||||
* @throws ResolutionException
|
||||
* If a post-resolution consistency checks fails
|
||||
* If resolution fails for any of the consistency checks specified
|
||||
* above
|
||||
* @throws IllegalArgumentException
|
||||
* If the list of parents is empty, or the list has two or more
|
||||
* parents with modules for different target operating systems,
|
||||
@ -368,11 +368,11 @@ public final class Configuration {
|
||||
* resolve} except that the graph of resolved modules is augmented
|
||||
* with modules induced by the service-use dependence relation. </p>
|
||||
*
|
||||
* <p> More specifically, the root modules are resolved as if by calling
|
||||
* {@code resolve}. The resolved modules, and all modules in the
|
||||
* parent configurations, with {@link ModuleDescriptor#uses() service
|
||||
* dependences} are then examined. All modules found by the given module
|
||||
* finders that {@link ModuleDescriptor#provides() provide} an
|
||||
* <p id="service-binding"> More specifically, the root modules are
|
||||
* resolved as if by calling {@code resolve}. The resolved modules, and
|
||||
* all modules in the parent configurations, with {@link ModuleDescriptor#uses()
|
||||
* service dependences} are then examined. All modules found by the given
|
||||
* module finders that {@link ModuleDescriptor#provides() provide} an
|
||||
* implementation of one or more of the service types are added to the
|
||||
* module graph and then resolved as if by calling the {@code
|
||||
* resolve} method. Adding modules to the module graph may introduce new
|
||||
@ -402,8 +402,8 @@ public final class Configuration {
|
||||
* If resolution fails for any of the observability-related reasons
|
||||
* specified by the static {@code resolve} method
|
||||
* @throws ResolutionException
|
||||
* If any of the post-resolution consistency checks specified by
|
||||
* the static {@code resolve} method fail
|
||||
* If resolution fails any of the consistency checks specified by
|
||||
* the static {@code resolve} method
|
||||
* @throws IllegalArgumentException
|
||||
* If the list of parents is empty, or the list has two or more
|
||||
* parents with modules for different target operating systems,
|
||||
|
@ -48,7 +48,7 @@ import jdk.internal.module.SystemModuleFinder;
|
||||
/**
|
||||
* A finder of modules. A {@code ModuleFinder} is used to find modules during
|
||||
* <a href="package-summary.html#resolution">resolution</a> or
|
||||
* <a href="package-summary.html#servicebinding">service binding</a>.
|
||||
* <a href="Configuration.html#service-binding">service binding</a>.
|
||||
*
|
||||
* <p> A {@code ModuleFinder} can only find one module with a given name. A
|
||||
* {@code ModuleFinder} that finds modules in a sequence of directories, for
|
||||
@ -239,30 +239,35 @@ public interface ModuleFinder {
|
||||
*
|
||||
* <ul>
|
||||
*
|
||||
* <li><p> The module {@link ModuleDescriptor#name() name}, and {@link
|
||||
* ModuleDescriptor#version() version} if applicable, is derived from
|
||||
* the file name of the JAR file as follows: </p>
|
||||
* <li><p> If the JAR file has the attribute "{@code Automatic-Module-Name}"
|
||||
* in its main manifest then its value is the {@linkplain
|
||||
* ModuleDescriptor#name() module name}. The module name is otherwise
|
||||
* derived from the name of the JAR file. </p></li>
|
||||
*
|
||||
* <li><p> The {@link ModuleDescriptor#version() version}, and the
|
||||
* module name when the attribute "{@code Automatic-Module-Name}" is not
|
||||
* present, are derived from the file name of the JAR file as follows: </p>
|
||||
*
|
||||
* <ul>
|
||||
*
|
||||
* <li><p> The {@code .jar} suffix is removed. </p></li>
|
||||
* <li><p> The "{@code .jar}" suffix is removed. </p></li>
|
||||
*
|
||||
* <li><p> If the name matches the regular expression {@code
|
||||
* "-(\\d+(\\.|$))"} then the module name will be derived from the
|
||||
* subsequence preceding the hyphen of the first occurrence. The
|
||||
* subsequence after the hyphen is parsed as a {@link
|
||||
* ModuleDescriptor.Version} and ignored if it cannot be parsed as
|
||||
* a {@code Version}. </p></li>
|
||||
* ModuleDescriptor.Version Version} and ignored if it cannot be
|
||||
* parsed as a {@code Version}. </p></li>
|
||||
*
|
||||
* <li><p> All non-alphanumeric characters ({@code [^A-Za-z0-9]})
|
||||
* in the module name are replaced with a dot ({@code "."}), all
|
||||
* repeating dots are replaced with one dot, and all leading and
|
||||
* trailing dots are removed. </p></li>
|
||||
*
|
||||
* <li><p> As an example, a JAR file named {@code foo-bar.jar} will
|
||||
* derive a module name {@code foo.bar} and no version. A JAR file
|
||||
* named {@code foo-bar-1.2.3-SNAPSHOT.jar} will derive a module
|
||||
* name {@code foo.bar} and {@code 1.2.3-SNAPSHOT} as the version.
|
||||
* <li><p> As an example, a JAR file named "{@code foo-bar.jar}" will
|
||||
* derive a module name "{@code foo.bar}" and no version. A JAR file
|
||||
* named "{@code foo-bar-1.2.3-SNAPSHOT.jar}" will derive a module
|
||||
* name "{@code foo.bar}" and "{@code 1.2.3-SNAPSHOT}" as the version.
|
||||
* </p></li>
|
||||
*
|
||||
* </ul></li>
|
||||
@ -295,11 +300,12 @@ public interface ModuleFinder {
|
||||
* <p> If a {@code ModuleDescriptor} cannot be created (by means of the
|
||||
* {@link ModuleDescriptor.Builder ModuleDescriptor.Builder} API) for an
|
||||
* automatic module then {@code FindException} is thrown. This can arise
|
||||
* when a legal module name cannot be derived from the file name of the JAR
|
||||
* file, where the JAR file contains a {@code .class} in the top-level
|
||||
* directory of the JAR file, where an entry in a service configuration
|
||||
* file is not a legal class name or its package name is not in the set of
|
||||
* packages derived for the module. </p>
|
||||
* when the value of the "{@code Automatic-Module-Name}" attribute is not a
|
||||
* legal module name, a legal module name cannot be derived from the file
|
||||
* name of the JAR file, where the JAR file contains a {@code .class} in
|
||||
* the top-level directory of the JAR file, where an entry in a service
|
||||
* configuration file is not a legal class name or its package name is not
|
||||
* in the set of packages derived for the module. </p>
|
||||
*
|
||||
* <p> In addition to JAR files, an implementation may also support modules
|
||||
* that are packaged in other implementation specific module formats. If
|
||||
|
@ -27,118 +27,178 @@
|
||||
* Classes to support module descriptors and creating configurations of modules
|
||||
* by means of resolution and service binding.
|
||||
*
|
||||
* <h2><a id="resolution">Resolution</a></h2>
|
||||
*
|
||||
* <p> Resolution is the process of computing the transitive closure of a set
|
||||
* of root modules over a set of observable modules by resolving the
|
||||
* dependences expressed by {@link
|
||||
* java.lang.module.ModuleDescriptor.Requires requires} clauses.
|
||||
* The <em>dependence graph</em> is augmented with edges that take account of
|
||||
* implicitly declared dependences ({@code requires transitive}) to create a
|
||||
* <em>readability graph</em>. The result of resolution is a {@link
|
||||
* java.lang.module.Configuration Configuration} that encapsulates the
|
||||
* readability graph. </p>
|
||||
*
|
||||
* <p> As an example, suppose we have the following observable modules: </p>
|
||||
* <pre> {@code
|
||||
* module m1 { requires m2; }
|
||||
* module m2 { requires transitive m3; }
|
||||
* module m3 { }
|
||||
* module m4 { }
|
||||
* } </pre>
|
||||
*
|
||||
* <p> If the module {@code m1} is resolved then the resulting configuration
|
||||
* contains three modules ({@code m1}, {@code m2}, {@code m3}). The edges in
|
||||
* its readability graph are: </p>
|
||||
* <pre> {@code
|
||||
* m1 --> m2 (meaning m1 reads m2)
|
||||
* m1 --> m3
|
||||
* m2 --> m3
|
||||
* } </pre>
|
||||
*
|
||||
* <p> Resolution is an additive process. When computing the transitive closure
|
||||
* then the dependence relation may include dependences on modules in {@link
|
||||
* java.lang.module.Configuration#parents() parent} configurations. The result
|
||||
* is a <em>relative configuration</em> that is relative to one or more parent
|
||||
* configurations and where the readability graph may have edges from modules
|
||||
* in the configuration to modules in parent configurations. </p>
|
||||
*
|
||||
* <p> As an example, suppose we have the following observable modules: </p>
|
||||
* <pre> {@code
|
||||
* module m1 { requires m2; requires java.xml; }
|
||||
* module m2 { }
|
||||
* } </pre>
|
||||
*
|
||||
* <p> If module {@code m1} is resolved with the configuration for the {@link
|
||||
* java.lang.ModuleLayer#boot() boot} layer as the parent then the resulting
|
||||
* configuration contains two modules ({@code m1}, {@code m2}). The edges in
|
||||
* its readability graph are:
|
||||
* <pre> {@code
|
||||
* m1 --> m2
|
||||
* m1 --> java.xml
|
||||
* } </pre>
|
||||
* where module {@code java.xml} is in the parent configuration. For
|
||||
* simplicity, this example omits the implicitly declared dependence on the
|
||||
* {@code java.base} module.
|
||||
*
|
||||
* <p> Requires clauses that are "{@code requires static}" express an optional
|
||||
* dependence (except at compile-time). If a module declares that it
|
||||
* "{@code requires static M}" then resolution does not search the observable
|
||||
* modules for "{@code M}". However, if "{@code M}" is resolved (because resolution
|
||||
* resolves a module that requires "{@code M}" without the {@link
|
||||
* java.lang.module.ModuleDescriptor.Requires.Modifier#STATIC static} modifier)
|
||||
* then the readability graph will contain read edges for each module that
|
||||
* "{@code requires static M}". </p>
|
||||
*
|
||||
* <p> {@link java.lang.module.ModuleDescriptor#isAutomatic() Automatic} modules
|
||||
* receive special treatment during resolution. Each automatic module is resolved
|
||||
* as if it "{@code requires transitive}" all observable automatic modules and
|
||||
* all automatic modules in the parent configurations. Each automatic module is
|
||||
* resolved so that it reads all other modules in the resulting configuration and
|
||||
* all modules in parent configurations. </p>
|
||||
*
|
||||
* <h2><a id="servicebinding">Service binding</a></h2>
|
||||
*
|
||||
* <p> Service binding is the process of augmenting a graph of resolved modules
|
||||
* from the set of observable modules induced by the service-use dependence
|
||||
* ({@code uses} and {@code provides} clauses). Any module that was not
|
||||
* previously in the graph requires resolution to compute its transitive
|
||||
* closure. Service binding is an iterative process in that adding a module
|
||||
* that satisfies some service-use dependence may introduce new service-use
|
||||
* dependences. </p>
|
||||
*
|
||||
* <p> Suppose we have the following observable modules: </p>
|
||||
* <pre> {@code
|
||||
* module m1 { exports p; uses p.S; }
|
||||
* module m2 { requires m1; provides p.S with p2.S2; }
|
||||
* module m3 { requires m1; requires m4; provides p.S with p3.S3; }
|
||||
* module m4 { }
|
||||
* } </pre>
|
||||
*
|
||||
* <p> If the module {@code m1} is resolved then the resulting graph of modules
|
||||
* has one module ({@code m1}). If the graph is augmented with modules induced
|
||||
* by the service-use dependence relation then the configuration will contain
|
||||
* four modules ({@code m1}, {@code m2}, {@code m3}, {@code m4}). The edges in
|
||||
* its readability graph are: </p>
|
||||
* <pre> {@code
|
||||
* m2 --> m1
|
||||
* m3 --> m1
|
||||
* m3 --> m4
|
||||
* } </pre>
|
||||
* <p> The edges in the conceptual service-use graph are: </p>
|
||||
* <pre> {@code
|
||||
* m1 --> m2 (meaning m1 uses a service that is provided by m2)
|
||||
* m1 --> m3
|
||||
* } </pre>
|
||||
*
|
||||
* <h2>General Exceptions</h2>
|
||||
*
|
||||
* <p> Unless otherwise noted, passing a {@code null} argument to a constructor
|
||||
* or method of any class or interface in this package will cause a {@link
|
||||
* java.lang.NullPointerException NullPointerException} to be thrown. Additionally,
|
||||
* invoking a method with an array or collection containing a {@code null} element
|
||||
* will cause a {@code NullPointerException}, unless otherwise specified. </p>
|
||||
*
|
||||
*
|
||||
* <h1><a id="resolution">Resolution</a></h1>
|
||||
*
|
||||
* <p> Resolution is the process of computing how modules depend on each other.
|
||||
* The process occurs at compile time and run time. </p>
|
||||
*
|
||||
* <p> Resolution is a two-step process. The first step recursively enumerates
|
||||
* the 'requires' directives of a set of root modules. If all the enumerated
|
||||
* modules are observable, then the second step computes their readability graph.
|
||||
* The readability graph embodies how modules depend on each other, which in
|
||||
* turn controls access across module boundaries. </p>
|
||||
*
|
||||
* <h2> Step 1: Recursive enumeration </h2>
|
||||
*
|
||||
* <p> Recursive enumeration takes a set of module names, looks up each of their
|
||||
* module declarations, and for each module declaration, recursively enumerates:
|
||||
*
|
||||
* <ul>
|
||||
* <li> <p> the module names given by the 'requires' directives with the
|
||||
* 'transitive' modifier, and </p></li>
|
||||
* <li> <p> at the discretion of the host system, the module names given by
|
||||
* the 'requires' directives without the 'transitive' modifier. </p></li>
|
||||
* </ul>
|
||||
*
|
||||
* <p> Module declarations are looked up in a set of observable modules. The set
|
||||
* of observable modules is determined in an implementation specific manner. The
|
||||
* set of observable modules may include modules with explicit declarations
|
||||
* (that is, with a {@code module-info.java} source file or {@code module-info.class}
|
||||
* file) and modules with implicit declarations (that is,
|
||||
* <a href="ModuleFinder.html#automatic-modules">automatic modules</a>).
|
||||
* Because an automatic module has no explicit module declaration, it has no
|
||||
* 'requires' directives of its own, although its name may be given by a
|
||||
* 'requires' directive of an explicit module declaration. </p>
|
||||
|
||||
* <p> The set of root modules, whose names are the initial input to this
|
||||
* algorithm, is determined in an implementation specific manner. The set of
|
||||
* root modules may include automatic modules. </p>
|
||||
*
|
||||
* <p> If at least one automatic module is enumerated by this algorithm, then
|
||||
* every observable automatic module must be enumerated, regardless of whether
|
||||
* any of their names are given by 'requires' directives of explicit module
|
||||
* declarations. </p>
|
||||
*
|
||||
* <p> If any of the following conditions occur, then resolution fails:
|
||||
* <ul>
|
||||
* <li><p> Any root module is not observable. </p></li>
|
||||
* <li><p> Any module whose name is given by a 'requires' directive with the
|
||||
* 'transitive' modifier is not observable. </p></li>
|
||||
* <li><p> At the discretion of the host system, any module whose name is given
|
||||
* by a 'requires' directive without the 'transitive' modifier is not
|
||||
* observable. </p></li>
|
||||
* <li><p> The algorithm in this step enumerates the same module name twice. This
|
||||
* indicates a cycle in the 'requires' directives, disregarding any 'transitive'
|
||||
* modifiers. </p></li>
|
||||
* </ul>
|
||||
*
|
||||
* <p> Otherwise, resolution proceeds to step 2. </p>
|
||||
*
|
||||
* <h2> Step 2: Computing the readability graph </h2>
|
||||
*
|
||||
* <p> A 'requires' directive (irrespective of 'transitive') expresses that
|
||||
* one module depends on some other module. The effect of the 'transitive'
|
||||
* modifier is to cause additional modules to also depend on the other module.
|
||||
* If module M 'requires transitive N', then not only does M depend on N, but
|
||||
* any module that depends on M also depends on N. This allows M to be
|
||||
* refactored so that some or all of its content can be moved to a new module N
|
||||
* without breaking modules that have a 'requires M' directive. </p>
|
||||
*
|
||||
* <p> Module dependencies are represented by the readability graph. The
|
||||
* readability graph is a directed graph whose vertices are the modules
|
||||
* enumerated in step 1 and whose edges represent readability between pairs of
|
||||
* modules. The edges are specified as follows:
|
||||
*
|
||||
* <p> First, readability is determined by the 'requires' directives of the
|
||||
* enumerated modules, disregarding any 'transitive' modifiers:
|
||||
*
|
||||
* <ul>
|
||||
* <li><p> For each enumerated module A that 'requires' B: A "reads" B. </p></li>
|
||||
* <li><p> For each enumerated module X that is automatic: X "reads" every
|
||||
* other enumerated module (it is "as if" an automatic module has 'requires'
|
||||
* directives for every other enumerated module). </p></li>
|
||||
* </ul>
|
||||
*
|
||||
* <p> Second, readability is augmented to account for 'transitive' modifiers:
|
||||
* <ul>
|
||||
* <li> <p> For each enumerated module A that "reads" B: </p>
|
||||
* <ul>
|
||||
* <li><p> If B 'requires transitive' C, then A "reads" C as well as B. This
|
||||
* augmentation is recursive: since A "reads" C, if C 'requires transitive'
|
||||
* D, then A "reads" D as well as C and B. </p></li>
|
||||
* <li><p> If B is an automatic module, then A "reads" every other enumerated
|
||||
* automatic module. (It is "as if" an automatic module has 'requires transitive'
|
||||
* directives for every other enumerated automatic module).</p> </li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* <p> Finally, every module "reads" itself. </p>
|
||||
*
|
||||
* <p> If any of the following conditions occur in the readability graph, then
|
||||
* resolution fails:
|
||||
* <ul>
|
||||
* <li><p> A module "reads" two or more modules with the same name. This includes
|
||||
* the case where a module "reads" another with the same name as itself. </p></li>
|
||||
* <li><p> Two or more modules export a package with the same name to a module
|
||||
* that "reads" both. This includes the case where a module M containing package
|
||||
* p "reads" another module that exports p to M. </p></li>
|
||||
* <li><p> A module M declares that it 'uses p.S' or 'provides p.S with ...' but
|
||||
* package p is neither in module M nor exported to M by any module that M
|
||||
* "reads". </p></li>
|
||||
* </ul>
|
||||
* <p> Otherwise, resolution succeeds, and the result of resolution is the
|
||||
* readability graph.
|
||||
*
|
||||
* <h2> Root modules </h2>
|
||||
*
|
||||
* <p> The set of root modules at compile-time is usually the set of modules
|
||||
* being compiled. At run-time, the set of root modules is usually the
|
||||
* application module specified to the 'java' launcher. When compiling code in
|
||||
* the unnamed module, or at run-time when the main application class is loaded
|
||||
* from the class path, then the default set of root modules is implementation
|
||||
* specific (In the JDK implementation it is the module "java.se", if observable,
|
||||
* and every observable module that exports an API). </p>
|
||||
*
|
||||
* <h2> Observable modules </h2>
|
||||
*
|
||||
* <p> The set of observable modules at both compile-time and run-time is determined
|
||||
* by searching an abstract module path. The module path is typically composed
|
||||
* of search paths that are searched in order: </p>
|
||||
*
|
||||
* <ul>
|
||||
* <li><p> At compile-time only, a compilation module path that contains module
|
||||
* definitions in source form. </p></li>
|
||||
* <li><p> The upgrade module path containing compiled definitions of modules
|
||||
* intended to be used in place of upgradeable modules built-in to the
|
||||
* environment. </p></li>
|
||||
* <li><p> The system modules which are the compiled modules built-in to the
|
||||
* environment. </p></li>
|
||||
* <li><p> The application module path which contains compiled definitions of
|
||||
* library and application modules. </p></li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2> 'requires' directives with 'static' modifier </h2>
|
||||
*
|
||||
* <p> 'requires' directives that have the 'static' modifier express an optional
|
||||
* dependence at run time. If a module declares that it 'requires static M' then
|
||||
* resolution does not search the observable modules for M to satisfy the dependency.
|
||||
* However, if M is recursively enumerated at step 1 then all modules that are
|
||||
* enumerated and `requires static M` will read M. </p>
|
||||
*
|
||||
* <h2> Completeness </h2>
|
||||
*
|
||||
* <p> Resolution may be partial at compile-time in that the complete transitive
|
||||
* closure may not be required to compile a set of modules. Minimally, the
|
||||
* readability graph that is constructed and validated at compile-time includes
|
||||
* the modules being compiled, their direct dependences, and all implicitly
|
||||
* declared dependences (requires transitive). </p>
|
||||
*
|
||||
* <p> At run-time, resolution is an additive process. The recursive enumeration
|
||||
* at step 1 may be relative to previous resolutions so that a root module,
|
||||
* or a module named in a 'requires' directive, is not enumerated when it was
|
||||
* enumerated by a previous (or parent) resolution. The readability graph that
|
||||
* is the result of resolution may therefore have a vertex for a module enumerated
|
||||
* in step 1 but with an edge to represent that the module reads a module that
|
||||
* was enumerated by previous (or parent) resolution. </p>
|
||||
*
|
||||
* @since 9
|
||||
* @spec JPMS
|
||||
*/
|
||||
|
@ -304,7 +304,7 @@ public class AccessibleObject implements AnnotatedElement {
|
||||
if (isClassPublic && declaringModule.isExported(pn, callerModule)) {
|
||||
// member is public
|
||||
if (Modifier.isPublic(modifiers)) {
|
||||
logIfExportedByBackdoor(caller, declaringClass);
|
||||
logIfExportedForIllegalAccess(caller, declaringClass);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -312,14 +312,14 @@ public class AccessibleObject implements AnnotatedElement {
|
||||
if (Modifier.isProtected(modifiers)
|
||||
&& Modifier.isStatic(modifiers)
|
||||
&& isSubclassOf(caller, declaringClass)) {
|
||||
logIfExportedByBackdoor(caller, declaringClass);
|
||||
logIfExportedForIllegalAccess(caller, declaringClass);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// package is open to caller
|
||||
if (declaringModule.isOpen(pn, callerModule)) {
|
||||
logIfOpenedByBackdoor(caller, declaringClass);
|
||||
logIfOpenedForIllegalAccess(caller, declaringClass);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -353,26 +353,26 @@ public class AccessibleObject implements AnnotatedElement {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void logIfOpenedByBackdoor(Class<?> caller, Class<?> declaringClass) {
|
||||
private void logIfOpenedForIllegalAccess(Class<?> caller, Class<?> declaringClass) {
|
||||
Module callerModule = caller.getModule();
|
||||
Module targetModule = declaringClass.getModule();
|
||||
// callerModule is null during early startup
|
||||
if (callerModule != null && !callerModule.isNamed() && targetModule.isNamed()) {
|
||||
IllegalAccessLogger logger = IllegalAccessLogger.illegalAccessLogger();
|
||||
if (logger != null) {
|
||||
logger.logIfOpenedByBackdoor(caller, declaringClass, this::toShortString);
|
||||
logger.logIfOpenedForIllegalAccess(caller, declaringClass, this::toShortString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void logIfExportedByBackdoor(Class<?> caller, Class<?> declaringClass) {
|
||||
private void logIfExportedForIllegalAccess(Class<?> caller, Class<?> declaringClass) {
|
||||
Module callerModule = caller.getModule();
|
||||
Module targetModule = declaringClass.getModule();
|
||||
// callerModule is null during early startup
|
||||
if (callerModule != null && !callerModule.isNamed() && targetModule.isNamed()) {
|
||||
IllegalAccessLogger logger = IllegalAccessLogger.illegalAccessLogger();
|
||||
if (logger != null) {
|
||||
logger.logIfExportedByBackdoor(caller, declaringClass, this::toShortString);
|
||||
logger.logIfExportedForIllegalAccess(caller, declaringClass, this::toShortString);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -634,7 +634,7 @@ public class AccessibleObject implements AnnotatedElement {
|
||||
}
|
||||
|
||||
// access okay
|
||||
logIfExportedByBackdoor(caller, memberClass);
|
||||
logIfExportedForIllegalAccess(caller, memberClass);
|
||||
|
||||
// Success: Update the cache.
|
||||
Object cache = (targetClass != null
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -27,33 +27,12 @@ package java.util;
|
||||
|
||||
|
||||
/**
|
||||
* Error thrown when something goes wrong while loading a service provider.
|
||||
*
|
||||
* <p> This error will be thrown in the following situations:
|
||||
*
|
||||
* <ul>
|
||||
*
|
||||
* <li> The format of a provider-configuration file violates the <a
|
||||
* href="ServiceLoader.html#format">specification</a>; </li>
|
||||
*
|
||||
* <li> An {@link java.io.IOException IOException} occurs while reading a
|
||||
* provider-configuration file; </li>
|
||||
*
|
||||
* <li> A concrete provider class named in a provider-configuration file
|
||||
* cannot be found; </li>
|
||||
*
|
||||
* <li> A concrete provider class is not a subclass of the service class;
|
||||
* </li>
|
||||
*
|
||||
* <li> A concrete provider class cannot be instantiated; or
|
||||
*
|
||||
* <li> Some other kind of error occurs. </li>
|
||||
*
|
||||
* </ul>
|
||||
*
|
||||
* Error thrown when something goes wrong while locating, loading, or
|
||||
* instantiating a service provider.
|
||||
*
|
||||
* @author Mark Reinhold
|
||||
* @since 1.6
|
||||
* @see ServiceLoader
|
||||
*/
|
||||
|
||||
public class ServiceConfigurationError
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -49,8 +49,10 @@ import java.security.SecureClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.jar.Attributes;
|
||||
@ -336,16 +338,43 @@ public class BuiltinClassLoader
|
||||
}
|
||||
}
|
||||
|
||||
// search class path
|
||||
// class path (not checked)
|
||||
Enumeration<URL> e = findResourcesOnClassPath(name);
|
||||
while (e.hasMoreElements()) {
|
||||
URL url = checkURL(e.nextElement());
|
||||
if (url != null) {
|
||||
checked.add(url);
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.enumeration(checked);
|
||||
// concat the checked URLs and the (not checked) class path
|
||||
return new Enumeration<>() {
|
||||
final Iterator<URL> iterator = checked.iterator();
|
||||
URL next;
|
||||
private boolean hasNext() {
|
||||
if (next != null) {
|
||||
return true;
|
||||
} else if (iterator.hasNext()) {
|
||||
next = iterator.next();
|
||||
return true;
|
||||
} else {
|
||||
// need to check each URL
|
||||
while (e.hasMoreElements() && next == null) {
|
||||
next = checkURL(e.nextElement());
|
||||
}
|
||||
return next != null;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public boolean hasMoreElements() {
|
||||
return hasNext();
|
||||
}
|
||||
@Override
|
||||
public URL nextElement() {
|
||||
if (hasNext()) {
|
||||
URL result = next;
|
||||
next = null;
|
||||
return result;
|
||||
} else {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -28,6 +28,7 @@ package jdk.internal.loader;
|
||||
import java.io.File;
|
||||
import java.io.FilePermission;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.lang.module.Configuration;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.module.ModuleReader;
|
||||
@ -52,11 +53,17 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Spliterator;
|
||||
import java.util.Spliterators;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import jdk.internal.misc.SharedSecrets;
|
||||
import jdk.internal.module.Resources;
|
||||
@ -79,8 +86,8 @@ import jdk.internal.module.Resources;
|
||||
* loader. This allows automatic modules (for example) to link to types in the
|
||||
* unnamed module of the parent class loader.
|
||||
*
|
||||
* @see ModuleModuleLayer#defineModulesWithOneLoader
|
||||
* @see ModuleModuleLayer#defineModulesWithManyLoaders
|
||||
* @see ModuleLayer#defineModulesWithOneLoader
|
||||
* @see ModuleLayer#defineModulesWithManyLoaders
|
||||
*/
|
||||
|
||||
public final class Loader extends SecureClassLoader {
|
||||
@ -303,7 +310,6 @@ public final class Loader extends SecureClassLoader {
|
||||
|
||||
// -- resources --
|
||||
|
||||
|
||||
/**
|
||||
* Returns a URL to a resource of the given name in a module defined to
|
||||
* this class loader.
|
||||
@ -388,33 +394,96 @@ public final class Loader extends SecureClassLoader {
|
||||
|
||||
@Override
|
||||
public Enumeration<URL> findResources(String name) throws IOException {
|
||||
List<URL> urls = new ArrayList<>();
|
||||
return Collections.enumeration(findResourcesAsList(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getResource(String name) {
|
||||
Objects.requireNonNull(name);
|
||||
|
||||
// this loader
|
||||
URL url = findResource(name);
|
||||
if (url != null) {
|
||||
return url;
|
||||
} else {
|
||||
// parent loader
|
||||
return parent.getResource(name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration<URL> getResources(String name) throws IOException {
|
||||
Objects.requireNonNull(name);
|
||||
|
||||
// this loader
|
||||
List<URL> urls = findResourcesAsList(name);
|
||||
|
||||
// parent loader
|
||||
Enumeration<URL> e = parent.getResources(name);
|
||||
|
||||
// concat the URLs with the URLs returned by the parent
|
||||
return new Enumeration<>() {
|
||||
final Iterator<URL> iterator = urls.iterator();
|
||||
@Override
|
||||
public boolean hasMoreElements() {
|
||||
return (iterator.hasNext() || e.hasMoreElements());
|
||||
}
|
||||
@Override
|
||||
public URL nextElement() {
|
||||
if (iterator.hasNext()) {
|
||||
return iterator.next();
|
||||
} else {
|
||||
return e.nextElement();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<URL> resources(String name) {
|
||||
Objects.requireNonNull(name);
|
||||
// ordering not specified
|
||||
int characteristics = (Spliterator.NONNULL | Spliterator.IMMUTABLE |
|
||||
Spliterator.SIZED | Spliterator.SUBSIZED);
|
||||
Supplier<Spliterator<URL>> supplier = () -> {
|
||||
try {
|
||||
List<URL> urls = findResourcesAsList(name);
|
||||
return Spliterators.spliterator(urls, characteristics);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
};
|
||||
Stream<URL> s1 = StreamSupport.stream(supplier, characteristics, false);
|
||||
Stream<URL> s2 = parent.resources(name);
|
||||
return Stream.concat(s1, s2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the resources with the given name in this class loader.
|
||||
*/
|
||||
private List<URL> findResourcesAsList(String name) throws IOException {
|
||||
String pn = Resources.toPackageName(name);
|
||||
LoadedModule module = localPackageToModule.get(pn);
|
||||
if (module != null) {
|
||||
try {
|
||||
URL url = findResource(module.name(), name);
|
||||
if (url != null
|
||||
URL url = findResource(module.name(), name);
|
||||
if (url != null
|
||||
&& (name.endsWith(".class")
|
||||
|| url.toString().endsWith("/")
|
||||
|| isOpen(module.mref(), pn))) {
|
||||
urls.add(url);
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
// ignore
|
||||
|| url.toString().endsWith("/")
|
||||
|| isOpen(module.mref(), pn))) {
|
||||
return List.of(url);
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
} else {
|
||||
List<URL> urls = new ArrayList<>();
|
||||
for (ModuleReference mref : nameToModule.values()) {
|
||||
try {
|
||||
URL url = findResource(mref.descriptor().name(), name);
|
||||
if (url != null)
|
||||
urls.add(url);
|
||||
} catch (IOException ioe) {
|
||||
// ignore
|
||||
URL url = findResource(mref.descriptor().name(), name);
|
||||
if (url != null) {
|
||||
urls.add(url);
|
||||
}
|
||||
}
|
||||
return urls;
|
||||
}
|
||||
return Collections.enumeration(urls);
|
||||
}
|
||||
|
||||
|
||||
|
@ -32,6 +32,8 @@ import java.lang.reflect.Method;
|
||||
import java.net.URI;
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Stream;
|
||||
@ -44,13 +46,15 @@ import sun.nio.ch.Interruptible;
|
||||
public interface JavaLangAccess {
|
||||
|
||||
/**
|
||||
* Returns a {@code Method} object that reflects the specified public
|
||||
* member method of the given class. Returns {@code null} if the
|
||||
* method is not defined.
|
||||
* Returns the list of {@code Method} objects for the declared public
|
||||
* methods of this class or interface that have the specified method name
|
||||
* and parameter types.
|
||||
*/
|
||||
Method getMethodOrNull(Class<?> klass, String name, Class<?>... parameterTypes);
|
||||
List<Method> getDeclaredPublicMethods(Class<?> klass, String name, Class<?>... parameterTypes);
|
||||
|
||||
/** Return the constant pool for a class. */
|
||||
/**
|
||||
* Return the constant pool for a class.
|
||||
*/
|
||||
ConstantPool getConstantPool(Class<?> klass);
|
||||
|
||||
/**
|
||||
@ -95,7 +99,9 @@ public interface JavaLangAccess {
|
||||
*/
|
||||
<E extends Enum<E>> E[] getEnumConstantsShared(Class<E> klass);
|
||||
|
||||
/** Set thread's blocker field. */
|
||||
/**
|
||||
* Set thread's blocker field.
|
||||
*/
|
||||
void blockedOn(Thread t, Interruptible b);
|
||||
|
||||
/**
|
||||
@ -154,11 +160,6 @@ public interface JavaLangAccess {
|
||||
*/
|
||||
Class<?> findBootstrapClassOrNull(ClassLoader cl, String name);
|
||||
|
||||
/**
|
||||
* Returns the Packages for the given class loader.
|
||||
*/
|
||||
Stream<Package> packages(ClassLoader cl);
|
||||
|
||||
/**
|
||||
* Define a Package of the given name and module by the given class loader.
|
||||
*/
|
||||
@ -223,15 +224,30 @@ public interface JavaLangAccess {
|
||||
void addOpens(Module m1, String pkg, Module m2);
|
||||
|
||||
/**
|
||||
* Updates a module m to open a package to all unnamed modules.
|
||||
* Updates module m to open a package to all unnamed modules.
|
||||
*/
|
||||
void addOpensToAllUnnamed(Module m, String pkg);
|
||||
|
||||
/**
|
||||
* Updates a module m to use a service.
|
||||
* Updates module m to open all packages returned by the given iterator.
|
||||
*/
|
||||
void addOpensToAllUnnamed(Module m, Iterator<String> packages);
|
||||
|
||||
/**
|
||||
* Updates module m to use a service.
|
||||
*/
|
||||
void addUses(Module m, Class<?> service);
|
||||
|
||||
/**
|
||||
* Returns true if module m reflectively exports a package to other
|
||||
*/
|
||||
boolean isReflectivelyExported(Module module, String pn, Module other);
|
||||
|
||||
/**
|
||||
* Returns true if module m reflectively opens a package to other
|
||||
*/
|
||||
boolean isReflectivelyOpened(Module module, String pn, Module other);
|
||||
|
||||
/**
|
||||
* Returns the ServicesCatalog for the given Layer.
|
||||
*/
|
||||
|
@ -33,146 +33,240 @@ import java.security.CodeSource;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import static java.util.Collections.*;
|
||||
|
||||
import jdk.internal.loader.BootLoader;
|
||||
import sun.security.action.GetPropertyAction;
|
||||
import jdk.internal.misc.JavaLangAccess;
|
||||
import jdk.internal.misc.SharedSecrets;
|
||||
|
||||
/**
|
||||
* Supports logging of access to members of API packages that are exported or
|
||||
* opened via backdoor mechanisms to code in unnamed modules.
|
||||
* Supports logging of access to members of exported and concealed packages
|
||||
* that are opened to code in unnamed modules for illegal access.
|
||||
*/
|
||||
|
||||
public final class IllegalAccessLogger {
|
||||
|
||||
/**
|
||||
* Holder class to lazily create the StackWalker object and determine
|
||||
* if the stack trace should be printed
|
||||
* Logger modes
|
||||
*/
|
||||
static class Holder {
|
||||
static final StackWalker STACK_WALKER;
|
||||
static final boolean PRINT_STACK_TRACE;
|
||||
public static enum Mode {
|
||||
/**
|
||||
* Prints a warning when an illegal access succeeds and then
|
||||
* discards the logger so that there is no further output.
|
||||
*/
|
||||
ONESHOT,
|
||||
/**
|
||||
* Print warnings when illegal access succeeds
|
||||
*/
|
||||
WARN,
|
||||
/**
|
||||
* Prints warnings and a stack trace when illegal access succeeds
|
||||
*/
|
||||
DEBUG,
|
||||
}
|
||||
|
||||
static {
|
||||
PrivilegedAction<StackWalker> pa = () ->
|
||||
StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
|
||||
STACK_WALKER = AccessController.doPrivileged(pa);
|
||||
/**
|
||||
* A builder for IllegalAccessLogger objects.
|
||||
*/
|
||||
public static class Builder {
|
||||
private final Mode mode;
|
||||
private final PrintStream warningStream;
|
||||
private final Map<Module, Set<String>> moduleToConcealedPackages;
|
||||
private final Map<Module, Set<String>> moduleToExportedPackages;
|
||||
private boolean complete;
|
||||
|
||||
String name = "sun.reflect.debugModuleAccessChecks";
|
||||
String value = GetPropertyAction.privilegedGetProperty(name, null);
|
||||
PRINT_STACK_TRACE = "access" .equals(value);
|
||||
private void ensureNotComplete() {
|
||||
if (complete) throw new IllegalStateException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a builder.
|
||||
*/
|
||||
public Builder(Mode mode, PrintStream warningStream) {
|
||||
this.mode = mode;
|
||||
this.warningStream = warningStream;
|
||||
this.moduleToConcealedPackages = new HashMap<>();
|
||||
this.moduleToExportedPackages = new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adding logging of reflective-access to any member of a type in
|
||||
* otherwise concealed packages.
|
||||
*/
|
||||
public Builder logAccessToConcealedPackages(Module m, Set<String> packages) {
|
||||
ensureNotComplete();
|
||||
moduleToConcealedPackages.put(m, unmodifiableSet(packages));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adding logging of reflective-access to non-public members/types in
|
||||
* otherwise exported (not open) packages.
|
||||
*/
|
||||
public Builder logAccessToExportedPackages(Module m, Set<String> packages) {
|
||||
ensureNotComplete();
|
||||
moduleToExportedPackages.put(m, unmodifiableSet(packages));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the IllegalAccessLogger and sets it as the system-wise logger.
|
||||
*/
|
||||
public void complete() {
|
||||
Map<Module, Set<String>> map1 = unmodifiableMap(moduleToConcealedPackages);
|
||||
Map<Module, Set<String>> map2 = unmodifiableMap(moduleToExportedPackages);
|
||||
logger = new IllegalAccessLogger(mode, warningStream, map1, map2);
|
||||
complete = true;
|
||||
}
|
||||
}
|
||||
|
||||
// the maximum number of frames to capture
|
||||
private static final int MAX_STACK_FRAMES = 32;
|
||||
// need access to java.lang.Module
|
||||
private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
|
||||
|
||||
// lock to avoid interference when printing stack traces
|
||||
private static final Object OUTPUT_LOCK = new Object();
|
||||
// system-wide IllegalAccessLogger
|
||||
private static volatile IllegalAccessLogger logger;
|
||||
|
||||
// caller -> usages
|
||||
private final Map<Class<?>, Set<Usage>> callerToUsages = new WeakHashMap<>();
|
||||
|
||||
// module -> (package name -> CLI option)
|
||||
private final Map<Module, Map<String, String>> exported;
|
||||
private final Map<Module, Map<String, String>> opened;
|
||||
// logger mode
|
||||
private final Mode mode;
|
||||
|
||||
// the print stream to send the warnings
|
||||
private final PrintStream warningStream;
|
||||
|
||||
private IllegalAccessLogger(Map<Module, Map<String, String>> exported,
|
||||
Map<Module, Map<String, String>> opened,
|
||||
PrintStream warningStream) {
|
||||
this.exported = deepCopy(exported);
|
||||
this.opened = deepCopy(opened);
|
||||
// module -> packages open for illegal access
|
||||
private final Map<Module, Set<String>> moduleToConcealedPackages;
|
||||
private final Map<Module, Set<String>> moduleToExportedPackages;
|
||||
|
||||
// caller -> usages
|
||||
private final Map<Class<?>, Usages> callerToUsages = new WeakHashMap<>();
|
||||
|
||||
private IllegalAccessLogger(Mode mode,
|
||||
PrintStream warningStream,
|
||||
Map<Module, Set<String>> moduleToConcealedPackages,
|
||||
Map<Module, Set<String>> moduleToExportedPackages)
|
||||
{
|
||||
this.mode = mode;
|
||||
this.warningStream = warningStream;
|
||||
this.moduleToConcealedPackages = moduleToConcealedPackages;
|
||||
this.moduleToExportedPackages = moduleToExportedPackages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns that a Builder that is seeded with the packages known to this logger.
|
||||
* Returns the system-wide IllegalAccessLogger or {@code null} if there is
|
||||
* no logger.
|
||||
*/
|
||||
public Builder toBuilder() {
|
||||
return new Builder(exported, opened);
|
||||
public static IllegalAccessLogger illegalAccessLogger() {
|
||||
return logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the module exports a concealed package for illegal
|
||||
* access.
|
||||
*/
|
||||
public boolean isExportedForIllegalAccess(Module module, String pn) {
|
||||
Set<String> packages = moduleToConcealedPackages.get(module);
|
||||
if (packages != null && packages.contains(pn))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the module opens a concealed or exported package for
|
||||
* illegal access.
|
||||
*/
|
||||
public boolean isOpenForIllegalAccess(Module module, String pn) {
|
||||
if (isExportedForIllegalAccess(module, pn))
|
||||
return true;
|
||||
Set<String> packages = moduleToExportedPackages.get(module);
|
||||
if (packages != null && packages.contains(pn))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs access to the member of a target class by a caller class if the class
|
||||
* is in a package that is exported via a backdoor mechanism.
|
||||
* is in a package that is exported for illegal access.
|
||||
*
|
||||
* The {@code whatSupplier} supplies the message that describes the member.
|
||||
*/
|
||||
public void logIfExportedByBackdoor(Class<?> caller,
|
||||
Class<?> target,
|
||||
Supplier<String> whatSupplier) {
|
||||
Map<String, String> packages = exported.get(target.getModule());
|
||||
if (packages != null) {
|
||||
String how = packages.get(target.getPackageName());
|
||||
if (how != null) {
|
||||
log(caller, whatSupplier.get(), how);
|
||||
public void logIfExportedForIllegalAccess(Class<?> caller,
|
||||
Class<?> target,
|
||||
Supplier<String> whatSupplier) {
|
||||
Module targetModule = target.getModule();
|
||||
String targetPackage = target.getPackageName();
|
||||
if (isExportedForIllegalAccess(targetModule, targetPackage)) {
|
||||
Module callerModule = caller.getModule();
|
||||
if (!JLA.isReflectivelyExported(targetModule, targetPackage, callerModule)) {
|
||||
log(caller, whatSupplier.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs access to the member of a target class by a caller class if the class
|
||||
* is in a package that is opened via a backdoor mechanism.
|
||||
* is in a package that is opened for illegal access.
|
||||
*
|
||||
* The {@code what} parameter supplies the message that describes the member.
|
||||
*/
|
||||
public void logIfOpenedByBackdoor(Class<?> caller,
|
||||
Class<?> target,
|
||||
Supplier<String> whatSupplier) {
|
||||
Map<String, String> packages = opened.get(target.getModule());
|
||||
if (packages != null) {
|
||||
String how = packages.get(target.getPackageName());
|
||||
if (how != null) {
|
||||
log(caller, whatSupplier.get(), how);
|
||||
public void logIfOpenedForIllegalAccess(Class<?> caller,
|
||||
Class<?> target,
|
||||
Supplier<String> whatSupplier) {
|
||||
Module targetModule = target.getModule();
|
||||
String targetPackage = target.getPackageName();
|
||||
if (isOpenForIllegalAccess(targetModule, targetPackage)) {
|
||||
Module callerModule = caller.getModule();
|
||||
if (!JLA.isReflectivelyOpened(targetModule, targetPackage, callerModule)) {
|
||||
log(caller, whatSupplier.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs access by caller lookup if the target class is in a package that is
|
||||
* opened for illegal access.
|
||||
*/
|
||||
public void logIfOpenedForIllegalAccess(MethodHandles.Lookup caller, Class<?> target) {
|
||||
Module targetModule = target.getModule();
|
||||
String targetPackage = target.getPackageName();
|
||||
if (isOpenForIllegalAccess(targetModule, targetPackage)) {
|
||||
Class<?> callerClass = caller.lookupClass();
|
||||
Module callerModule = callerClass.getModule();
|
||||
if (!JLA.isReflectivelyOpened(targetModule, targetPackage, callerModule)) {
|
||||
URL url = codeSource(callerClass);
|
||||
final String source;
|
||||
if (url == null) {
|
||||
source = callerClass.getName();
|
||||
} else {
|
||||
source = callerClass.getName() + " (" + url + ")";
|
||||
}
|
||||
log(callerClass, target.getName(), () ->
|
||||
"WARNING: Illegal reflective access using Lookup on " + source
|
||||
+ " to " + target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs access by a caller class. The {@code what} parameter describes
|
||||
* the member is accessed, the {@code how} parameter is the means by which
|
||||
* access is allocated (CLI option for example).
|
||||
* the member being accessed.
|
||||
*/
|
||||
private void log(Class<?> caller, String what, String how) {
|
||||
private void log(Class<?> caller, String what) {
|
||||
log(caller, what, () -> {
|
||||
PrivilegedAction<ProtectionDomain> pa = caller::getProtectionDomain;
|
||||
CodeSource cs = AccessController.doPrivileged(pa).getCodeSource();
|
||||
URL url = (cs != null) ? cs.getLocation() : null;
|
||||
URL url = codeSource(caller);
|
||||
String source = caller.getName();
|
||||
if (url != null)
|
||||
source += " (" + url + ")";
|
||||
return "WARNING: Illegal access by " + source + " to " + what
|
||||
+ " (permitted by " + how + ")";
|
||||
return "WARNING: Illegal reflective access by " + source + " to " + what;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Logs access to caller class if the class is in a package that is opened via
|
||||
* a backdoor mechanism.
|
||||
*/
|
||||
public void logIfOpenedByBackdoor(MethodHandles.Lookup caller, Class<?> target) {
|
||||
Map<String, String> packages = opened.get(target.getModule());
|
||||
if (packages != null) {
|
||||
String how = packages.get(target.getPackageName());
|
||||
if (how != null) {
|
||||
log(caller.lookupClass(), target.getName(), () ->
|
||||
"WARNING: Illegal access using Lookup on " + caller.lookupClass()
|
||||
+ " to " + target + " (permitted by " + how + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log access by a caller. The {@code what} parameter describes the class or
|
||||
* member that is being accessed. The {@code msgSupplier} supplies the log
|
||||
@ -184,53 +278,73 @@ public final class IllegalAccessLogger {
|
||||
* keys so it can be expunged when the caller is GC'ed/unloaded.
|
||||
*/
|
||||
private void log(Class<?> caller, String what, Supplier<String> msgSupplier) {
|
||||
if (mode == Mode.ONESHOT) {
|
||||
synchronized (IllegalAccessLogger.class) {
|
||||
// discard the system wide logger
|
||||
if (logger == null)
|
||||
return;
|
||||
logger = null;
|
||||
}
|
||||
warningStream.println(loudWarning(caller, msgSupplier));
|
||||
return;
|
||||
}
|
||||
|
||||
// stack trace without the top-most frames in java.base
|
||||
List<StackWalker.StackFrame> stack = Holder.STACK_WALKER.walk(s ->
|
||||
List<StackWalker.StackFrame> stack = StackWalkerHolder.INSTANCE.walk(s ->
|
||||
s.dropWhile(this::isJavaBase)
|
||||
.limit(MAX_STACK_FRAMES)
|
||||
.limit(32)
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
|
||||
// check if the access has already been recorded
|
||||
// record usage if this is the first (or not recently recorded)
|
||||
Usage u = new Usage(what, hash(stack));
|
||||
boolean firstUsage;
|
||||
boolean added;
|
||||
synchronized (this) {
|
||||
firstUsage = callerToUsages.computeIfAbsent(caller, k -> new HashSet<>()).add(u);
|
||||
added = callerToUsages.computeIfAbsent(caller, k -> new Usages()).add(u);
|
||||
}
|
||||
|
||||
// log message if first usage
|
||||
if (firstUsage) {
|
||||
// print warning if this is the first (or not a recent) usage
|
||||
if (added) {
|
||||
String msg = msgSupplier.get();
|
||||
if (Holder.PRINT_STACK_TRACE) {
|
||||
synchronized (OUTPUT_LOCK) {
|
||||
warningStream.println(msg);
|
||||
stack.forEach(f -> warningStream.println("\tat " + f));
|
||||
}
|
||||
} else {
|
||||
warningStream.println(msg);
|
||||
if (mode == Mode.DEBUG) {
|
||||
StringBuilder sb = new StringBuilder(msg);
|
||||
stack.forEach(f ->
|
||||
sb.append(System.lineSeparator()).append("\tat " + f)
|
||||
);
|
||||
msg = sb.toString();
|
||||
}
|
||||
warningStream.println(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Usage {
|
||||
private final String what;
|
||||
private final int stack;
|
||||
Usage(String what, int stack) {
|
||||
this.what = what;
|
||||
this.stack = stack;
|
||||
}
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return what.hashCode() ^ stack;
|
||||
}
|
||||
@Override
|
||||
public boolean equals(Object ob) {
|
||||
if (ob instanceof Usage) {
|
||||
Usage that = (Usage)ob;
|
||||
return what.equals(that.what) && stack == (that.stack);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Returns the code source for the given class or null if there is no code source
|
||||
*/
|
||||
private URL codeSource(Class<?> clazz) {
|
||||
PrivilegedAction<ProtectionDomain> pa = clazz::getProtectionDomain;
|
||||
CodeSource cs = AccessController.doPrivileged(pa).getCodeSource();
|
||||
return (cs != null) ? cs.getLocation() : null;
|
||||
}
|
||||
|
||||
private String loudWarning(Class<?> caller, Supplier<String> msgSupplier) {
|
||||
StringJoiner sj = new StringJoiner(System.lineSeparator());
|
||||
sj.add("WARNING: An illegal reflective access operation has occurred");
|
||||
sj.add(msgSupplier.get());
|
||||
sj.add("WARNING: Please consider reporting this to the maintainers of "
|
||||
+ caller.getName());
|
||||
sj.add("WARNING: Use --illegal-access=warn to enable warnings of further"
|
||||
+ " illegal reflective access operations");
|
||||
sj.add("WARNING: All illegal access operations will be denied in a"
|
||||
+ " future release");
|
||||
return sj.toString();
|
||||
}
|
||||
|
||||
private static class StackWalkerHolder {
|
||||
static final StackWalker INSTANCE;
|
||||
static {
|
||||
PrivilegedAction<StackWalker> pa = () ->
|
||||
StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
|
||||
INSTANCE = AccessController.doPrivileged(pa);
|
||||
}
|
||||
}
|
||||
|
||||
@ -256,89 +370,39 @@ public final class IllegalAccessLogger {
|
||||
return hash;
|
||||
}
|
||||
|
||||
// system-wide IllegalAccessLogger
|
||||
private static volatile IllegalAccessLogger logger;
|
||||
|
||||
/**
|
||||
* Sets the system-wide IllegalAccessLogger
|
||||
*/
|
||||
public static void setIllegalAccessLogger(IllegalAccessLogger l) {
|
||||
if (l.exported.isEmpty() && l.opened.isEmpty()) {
|
||||
logger = null;
|
||||
} else {
|
||||
logger = l;
|
||||
private static class Usage {
|
||||
private final String what;
|
||||
private final int stack;
|
||||
Usage(String what, int stack) {
|
||||
this.what = what;
|
||||
this.stack = stack;
|
||||
}
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return what.hashCode() ^ stack;
|
||||
}
|
||||
@Override
|
||||
public boolean equals(Object ob) {
|
||||
if (ob instanceof Usage) {
|
||||
Usage that = (Usage)ob;
|
||||
return what.equals(that.what) && stack == (that.stack);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the system-wide IllegalAccessLogger or {@code null} if there is
|
||||
* no logger.
|
||||
*/
|
||||
public static IllegalAccessLogger illegalAccessLogger() {
|
||||
return logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* A builder for IllegalAccessLogger objects.
|
||||
*/
|
||||
public static class Builder {
|
||||
private final Module UNNAMED = BootLoader.getUnnamedModule();
|
||||
private Map<Module, Map<String, String>> exported;
|
||||
private Map<Module, Map<String, String>> opened;
|
||||
private PrintStream warningStream = System.err;
|
||||
|
||||
public Builder() { }
|
||||
|
||||
public Builder(Map<Module, Map<String, String>> exported,
|
||||
Map<Module, Map<String, String>> opened) {
|
||||
this.exported = deepCopy(exported);
|
||||
this.opened = deepCopy(opened);
|
||||
@SuppressWarnings("serial")
|
||||
private static class Usages extends LinkedHashMap<Usage, Boolean> {
|
||||
Usages() { }
|
||||
boolean add(Usage u) {
|
||||
return (putIfAbsent(u, Boolean.TRUE) == null);
|
||||
}
|
||||
|
||||
public Builder logAccessToExportedPackage(Module m, String pn, String how) {
|
||||
if (!m.isExported(pn, UNNAMED)) {
|
||||
if (exported == null)
|
||||
exported = new HashMap<>();
|
||||
exported.computeIfAbsent(m, k -> new HashMap<>()).putIfAbsent(pn, how);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder logAccessToOpenPackage(Module m, String pn, String how) {
|
||||
// opens implies exported at run-time.
|
||||
logAccessToExportedPackage(m, pn, how);
|
||||
|
||||
if (!m.isOpen(pn, UNNAMED)) {
|
||||
if (opened == null)
|
||||
opened = new HashMap<>();
|
||||
opened.computeIfAbsent(m, k -> new HashMap<>()).putIfAbsent(pn, how);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder warningStream(PrintStream warningStream) {
|
||||
this.warningStream = Objects.requireNonNull(warningStream);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the logger.
|
||||
*/
|
||||
public IllegalAccessLogger build() {
|
||||
return new IllegalAccessLogger(exported, opened, warningStream);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static Map<Module, Map<String, String>> deepCopy(Map<Module, Map<String, String>> map) {
|
||||
if (map == null || map.isEmpty()) {
|
||||
return new HashMap<>();
|
||||
} else {
|
||||
Map<Module, Map<String, String>> newMap = new HashMap<>();
|
||||
for (Map.Entry<Module, Map<String, String>> e : map.entrySet()) {
|
||||
newMap.put(e.getKey(), new HashMap<>(e.getValue()));
|
||||
}
|
||||
return newMap;
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry<Usage, Boolean> oldest) {
|
||||
// prevent map growing too big, say where a utility class
|
||||
// is used by generated code to do illegal access
|
||||
return size() > 16;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package jdk.internal.module;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.module.ModuleFinder;
|
||||
import java.lang.module.ModuleReference;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.*;
|
||||
|
||||
/**
|
||||
* Generates the maps of concealed and exported packages to open at run-time.
|
||||
*
|
||||
* This is used at run-time for exploded builds, and at link-time to generate
|
||||
* the maps for the system modules in the run-time image.
|
||||
*/
|
||||
|
||||
public class IllegalAccessMaps {
|
||||
private final Map<String, Set<String>> concealedPackagesToOpen;
|
||||
private final Map<String, Set<String>> exportedPackagesToOpen;
|
||||
|
||||
private IllegalAccessMaps(Map<String, Set<String>> map1,
|
||||
Map<String, Set<String>> map2) {
|
||||
this.concealedPackagesToOpen = map1;
|
||||
this.exportedPackagesToOpen = map2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the map of concealed packages to open. The map key is the
|
||||
* module name, the value is the set of concealed packages to open.
|
||||
*/
|
||||
public Map<String, Set<String>> concealedPackagesToOpen() {
|
||||
return concealedPackagesToOpen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the map of exported packages to open. The map key is the
|
||||
* module name, the value is the set of exported packages to open.
|
||||
*/
|
||||
public Map<String, Set<String>> exportedPackagesToOpen() {
|
||||
return exportedPackagesToOpen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the maps of module to concealed and exported packages for
|
||||
* the system modules that are observable with the given module finder.
|
||||
*/
|
||||
public static IllegalAccessMaps generate(ModuleFinder finder) {
|
||||
Map<String, ModuleDescriptor> map = new HashMap<>();
|
||||
finder.findAll().stream()
|
||||
.map(ModuleReference::descriptor)
|
||||
.forEach(md -> md.packages().forEach(pn -> map.putIfAbsent(pn, md)));
|
||||
|
||||
Map<String, Set<String>> concealedPackagesToOpen = new HashMap<>();
|
||||
Map<String, Set<String>> exportedPackagesToOpen = new HashMap<>();
|
||||
|
||||
String rn = "jdk8_packages.dat";
|
||||
InputStream in = IllegalAccessMaps.class.getResourceAsStream(rn);
|
||||
if (in == null) {
|
||||
throw new InternalError(rn + " not found");
|
||||
}
|
||||
try (BufferedReader br = new BufferedReader(new InputStreamReader(in, UTF_8))) {
|
||||
br.lines()
|
||||
.filter(line -> !line.isEmpty() && !line.startsWith("#"))
|
||||
.forEach(pn -> {
|
||||
ModuleDescriptor descriptor = map.get(pn);
|
||||
if (descriptor != null && !isOpen(descriptor, pn)) {
|
||||
String name = descriptor.name();
|
||||
if (isExported(descriptor, pn)) {
|
||||
exportedPackagesToOpen.computeIfAbsent(name,
|
||||
k -> new HashSet<>()).add(pn);
|
||||
} else {
|
||||
concealedPackagesToOpen.computeIfAbsent(name,
|
||||
k -> new HashSet<>()).add(pn);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
} catch (IOException ioe) {
|
||||
throw new UncheckedIOException(ioe);
|
||||
}
|
||||
|
||||
return new IllegalAccessMaps(concealedPackagesToOpen, exportedPackagesToOpen);
|
||||
}
|
||||
|
||||
private static boolean isExported(ModuleDescriptor descriptor, String pn) {
|
||||
return descriptor.exports()
|
||||
.stream()
|
||||
.anyMatch(e -> e.source().equals(pn) && !e.isQualified());
|
||||
}
|
||||
|
||||
private static boolean isOpen(ModuleDescriptor descriptor, String pn) {
|
||||
return descriptor.opens()
|
||||
.stream()
|
||||
.anyMatch(e -> e.source().equals(pn) && !e.isQualified());
|
||||
}
|
||||
}
|
@ -39,14 +39,17 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import jdk.internal.loader.BootLoader;
|
||||
import jdk.internal.loader.BuiltinClassLoader;
|
||||
import jdk.internal.misc.JavaLangAccess;
|
||||
import jdk.internal.misc.SharedSecrets;
|
||||
import jdk.internal.perf.PerfCounter;
|
||||
|
||||
@ -59,7 +62,7 @@ import jdk.internal.perf.PerfCounter;
|
||||
* -m and --add-modules options. The modules are located on a module path that
|
||||
* is constructed from the upgrade module path, system modules, and application
|
||||
* module path. The Configuration is instantiated as the boot layer with each
|
||||
* module in the the configuration defined to one of the built-in class loaders.
|
||||
* module in the the configuration defined to a class loader.
|
||||
*/
|
||||
|
||||
public final class ModuleBootstrap {
|
||||
@ -119,20 +122,20 @@ public final class ModuleBootstrap {
|
||||
*/
|
||||
public static ModuleLayer boot() {
|
||||
|
||||
long t0 = System.nanoTime();
|
||||
|
||||
// system modules (may be patched)
|
||||
ModuleFinder systemModules = ModuleFinder.ofSystem();
|
||||
|
||||
PerfCounters.systemModulesTime.addElapsedTimeFrom(t0);
|
||||
|
||||
// Step 1: Locate system modules (may be patched)
|
||||
|
||||
long t1 = System.nanoTime();
|
||||
ModuleFinder systemModules = ModuleFinder.ofSystem();
|
||||
PerfCounters.systemModulesTime.addElapsedTimeFrom(t1);
|
||||
|
||||
|
||||
// Step 2: Define and load java.base. This patches all classes loaded
|
||||
// to date so that they are members of java.base. Once java.base is
|
||||
// loaded then resources in java.base are available for error messages
|
||||
// needed from here on.
|
||||
|
||||
long t2 = System.nanoTime();
|
||||
|
||||
// Once we have the system modules then we define the base module to
|
||||
// the VM. We do this here so that java.base is defined as early as
|
||||
// possible and also that resources in the base module can be located
|
||||
// for error messages that may happen from here on.
|
||||
ModuleReference base = systemModules.find(JAVA_BASE).orElse(null);
|
||||
if (base == null)
|
||||
throw new InternalError(JAVA_BASE + " not found");
|
||||
@ -142,15 +145,23 @@ public final class ModuleBootstrap {
|
||||
BootLoader.loadModule(base);
|
||||
Modules.defineModule(null, base.descriptor(), baseUri);
|
||||
|
||||
PerfCounters.defineBaseTime.addElapsedTimeFrom(t1);
|
||||
PerfCounters.defineBaseTime.addElapsedTimeFrom(t2);
|
||||
|
||||
|
||||
// Step 2a: If --validate-modules is specified then the VM needs to
|
||||
// start with only java.base, all other options are ignored.
|
||||
|
||||
// special mode to boot with only java.base, ignores other options
|
||||
String propValue = getAndRemoveProperty("jdk.module.minimumBoot");
|
||||
if (propValue != null) {
|
||||
return createMinimalBootLayer();
|
||||
}
|
||||
|
||||
long t2 = System.nanoTime();
|
||||
|
||||
// Step 3: Construct the module path and the set of root modules to
|
||||
// resolve. If --limit-modules is specified then it limits the set
|
||||
// modules that are observable.
|
||||
|
||||
long t3 = System.nanoTime();
|
||||
|
||||
// --upgrade-module-path option specified to launcher
|
||||
ModuleFinder upgradeModulePath
|
||||
@ -269,10 +280,13 @@ public final class ModuleBootstrap {
|
||||
.forEach(mn -> roots.add(mn));
|
||||
}
|
||||
|
||||
PerfCounters.optionsAndRootsTime.addElapsedTimeFrom(t2);
|
||||
PerfCounters.optionsAndRootsTime.addElapsedTimeFrom(t3);
|
||||
|
||||
|
||||
long t3 = System.nanoTime();
|
||||
// Step 4: Resolve the root modules, with service binding, to create
|
||||
// the configuration for the boot layer.
|
||||
|
||||
long t4 = System.nanoTime();
|
||||
|
||||
// determine if post resolution checks are needed
|
||||
boolean needPostResolutionChecks = true;
|
||||
@ -295,11 +309,17 @@ public final class ModuleBootstrap {
|
||||
needPostResolutionChecks,
|
||||
traceOutput);
|
||||
|
||||
// time to create configuration
|
||||
PerfCounters.resolveTime.addElapsedTimeFrom(t3);
|
||||
PerfCounters.resolveTime.addElapsedTimeFrom(t4);
|
||||
|
||||
// check module names and incubating status
|
||||
checkModuleNamesAndStatus(cf);
|
||||
|
||||
// Step 5: Map the modules in the configuration to class loaders.
|
||||
// The static configuration provides the mapping of standard and JDK
|
||||
// modules to the boot and platform loaders. All other modules (JDK
|
||||
// tool modules, and both explicit and automatic modules on the
|
||||
// application module path) are defined to the application class
|
||||
// loader.
|
||||
|
||||
long t5 = System.nanoTime();
|
||||
|
||||
// mapping of modules to class loaders
|
||||
Function<String, ClassLoader> clf = ModuleLoaderMap.mappingFunction(cf);
|
||||
@ -312,11 +332,9 @@ public final class ModuleBootstrap {
|
||||
String name = mref.descriptor().name();
|
||||
ClassLoader cl = clf.apply(name);
|
||||
if (cl == null) {
|
||||
|
||||
if (upgradeModulePath != null
|
||||
&& upgradeModulePath.find(name).isPresent())
|
||||
fail(name + ": cannot be loaded from upgrade module path");
|
||||
|
||||
if (!systemModules.find(name).isPresent())
|
||||
fail(name + ": cannot be loaded from application module path");
|
||||
}
|
||||
@ -330,55 +348,38 @@ public final class ModuleBootstrap {
|
||||
}
|
||||
}
|
||||
|
||||
// if needed check that there are no split packages in the set of
|
||||
// resolved modules for the boot layer
|
||||
// check for split packages in the modules mapped to the built-in loaders
|
||||
if (SystemModules.hasSplitPackages() || needPostResolutionChecks) {
|
||||
Map<String, String> packageToModule = new HashMap<>();
|
||||
for (ResolvedModule resolvedModule : cf.modules()) {
|
||||
ModuleDescriptor descriptor = resolvedModule.reference().descriptor();
|
||||
String name = descriptor.name();
|
||||
for (String p : descriptor.packages()) {
|
||||
String other = packageToModule.putIfAbsent(p, name);
|
||||
if (other != null) {
|
||||
String msg = "Package " + p + " in both module "
|
||||
+ name + " and module " + other;
|
||||
throw new LayerInstantiationException(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
checkSplitPackages(cf, clf);
|
||||
}
|
||||
|
||||
long t4 = System.nanoTime();
|
||||
|
||||
// define modules to VM/runtime
|
||||
ModuleLayer bootLayer = ModuleLayer.empty().defineModules(cf, clf);
|
||||
|
||||
PerfCounters.layerCreateTime.addElapsedTimeFrom(t4);
|
||||
|
||||
|
||||
long t5 = System.nanoTime();
|
||||
|
||||
// define the module to its class loader, except java.base
|
||||
for (ResolvedModule resolvedModule : cf.modules()) {
|
||||
ModuleReference mref = resolvedModule.reference();
|
||||
String name = mref.descriptor().name();
|
||||
ClassLoader cl = clf.apply(name);
|
||||
if (cl == null) {
|
||||
if (!name.equals(JAVA_BASE)) BootLoader.loadModule(mref);
|
||||
} else {
|
||||
((BuiltinClassLoader)cl).loadModule(mref);
|
||||
}
|
||||
}
|
||||
// load/register the modules with the built-in class loaders
|
||||
loadModules(cf, clf);
|
||||
|
||||
PerfCounters.loadModulesTime.addElapsedTimeFrom(t5);
|
||||
|
||||
|
||||
// --add-reads, --add-exports/--add-opens
|
||||
// Step 6: Define all modules to the VM
|
||||
|
||||
long t6 = System.nanoTime();
|
||||
ModuleLayer bootLayer = ModuleLayer.empty().defineModules(cf, clf);
|
||||
PerfCounters.layerCreateTime.addElapsedTimeFrom(t6);
|
||||
|
||||
|
||||
// Step 7: Miscellaneous
|
||||
|
||||
// check incubating status
|
||||
checkIncubatingStatus(cf);
|
||||
|
||||
// --add-reads, --add-exports/--add-opens, and -illegal-access
|
||||
long t7 = System.nanoTime();
|
||||
addExtraReads(bootLayer);
|
||||
addExtraExportsAndOpens(bootLayer);
|
||||
boolean extraExportsOrOpens = addExtraExportsAndOpens(bootLayer);
|
||||
addIllegalAccess(bootLayer, upgradeModulePath, extraExportsOrOpens);
|
||||
PerfCounters.adjustModulesTime.addElapsedTimeFrom(t7);
|
||||
|
||||
// total time to initialize
|
||||
PerfCounters.bootstrapTime.addElapsedTimeFrom(t0);
|
||||
PerfCounters.bootstrapTime.addElapsedTimeFrom(t1);
|
||||
|
||||
return bootLayer;
|
||||
}
|
||||
@ -397,6 +398,51 @@ public final class ModuleBootstrap {
|
||||
return ModuleLayer.empty().defineModules(cf, clf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load/register the modules to the built-in class loaders.
|
||||
*/
|
||||
private static void loadModules(Configuration cf,
|
||||
Function<String, ClassLoader> clf) {
|
||||
for (ResolvedModule resolvedModule : cf.modules()) {
|
||||
ModuleReference mref = resolvedModule.reference();
|
||||
String name = resolvedModule.name();
|
||||
ClassLoader loader = clf.apply(name);
|
||||
if (loader == null) {
|
||||
// skip java.base as it is already loaded
|
||||
if (!name.equals(JAVA_BASE)) {
|
||||
BootLoader.loadModule(mref);
|
||||
}
|
||||
} else if (loader instanceof BuiltinClassLoader) {
|
||||
((BuiltinClassLoader) loader).loadModule(mref);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for split packages between modules defined to the built-in class
|
||||
* loaders.
|
||||
*/
|
||||
private static void checkSplitPackages(Configuration cf,
|
||||
Function<String, ClassLoader> clf) {
|
||||
Map<String, String> packageToModule = new HashMap<>();
|
||||
for (ResolvedModule resolvedModule : cf.modules()) {
|
||||
ModuleDescriptor descriptor = resolvedModule.reference().descriptor();
|
||||
String name = descriptor.name();
|
||||
ClassLoader loader = clf.apply(name);
|
||||
if (loader == null || loader instanceof BuiltinClassLoader) {
|
||||
for (String p : descriptor.packages()) {
|
||||
String other = packageToModule.putIfAbsent(p, name);
|
||||
if (other != null) {
|
||||
String msg = "Package " + p + " in both module "
|
||||
+ name + " and module " + other;
|
||||
throw new LayerInstantiationException(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a ModuleFinder that limits observability to the given root
|
||||
* modules, their transitive dependences, plus a set of other modules.
|
||||
@ -458,15 +504,14 @@ public final class ModuleBootstrap {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialize the module patcher for the initial configuration passed on the
|
||||
* value of the --patch-module options.
|
||||
*/
|
||||
private static ModulePatcher initModulePatcher() {
|
||||
Map<String, List<String>> map = decode("jdk.module.patch.",
|
||||
File.pathSeparator,
|
||||
false);
|
||||
File.pathSeparator,
|
||||
false);
|
||||
return new ModulePatcher(map);
|
||||
}
|
||||
|
||||
@ -538,38 +583,27 @@ public final class ModuleBootstrap {
|
||||
* Process the --add-exports and --add-opens options to export/open
|
||||
* additional packages specified on the command-line.
|
||||
*/
|
||||
private static void addExtraExportsAndOpens(ModuleLayer bootLayer) {
|
||||
private static boolean addExtraExportsAndOpens(ModuleLayer bootLayer) {
|
||||
boolean extraExportsOrOpens = false;
|
||||
|
||||
// --add-exports
|
||||
String prefix = "jdk.module.addexports.";
|
||||
Map<String, List<String>> extraExports = decode(prefix);
|
||||
if (!extraExports.isEmpty()) {
|
||||
addExtraExportsOrOpens(bootLayer, extraExports, false);
|
||||
extraExportsOrOpens = true;
|
||||
}
|
||||
|
||||
|
||||
// --add-opens
|
||||
prefix = "jdk.module.addopens.";
|
||||
Map<String, List<String>> extraOpens = decode(prefix);
|
||||
if (!extraOpens.isEmpty()) {
|
||||
addExtraExportsOrOpens(bootLayer, extraOpens, true);
|
||||
extraExportsOrOpens = true;
|
||||
}
|
||||
|
||||
// --permit-illegal-access
|
||||
if (getAndRemoveProperty("jdk.module.permitIllegalAccess") != null) {
|
||||
warn("--permit-illegal-access will be removed in the next major release");
|
||||
IllegalAccessLogger.Builder builder = new IllegalAccessLogger.Builder();
|
||||
Module unnamed = BootLoader.getUnnamedModule();
|
||||
bootLayer.modules().stream().forEach(m -> {
|
||||
m.getDescriptor()
|
||||
.packages()
|
||||
.stream()
|
||||
.filter(pn -> !m.isOpen(pn, unnamed)) // skip if opened by --add-opens
|
||||
.forEach(pn -> {
|
||||
builder.logAccessToOpenPackage(m, pn, "--permit-illegal-access");
|
||||
Modules.addOpensToAllUnnamed(m, pn);
|
||||
});
|
||||
});
|
||||
IllegalAccessLogger.setIllegalAccessLogger(builder.build());
|
||||
}
|
||||
return extraExportsOrOpens;
|
||||
}
|
||||
|
||||
private static void addExtraExportsOrOpens(ModuleLayer bootLayer,
|
||||
@ -638,6 +672,102 @@ public final class ModuleBootstrap {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the --illegal-access option (and its default) to open packages
|
||||
* of system modules in the boot layer to code in unnamed modules.
|
||||
*/
|
||||
private static void addIllegalAccess(ModuleLayer bootLayer,
|
||||
ModuleFinder upgradeModulePath,
|
||||
boolean extraExportsOrOpens) {
|
||||
String value = getAndRemoveProperty("jdk.module.illegalAccess");
|
||||
IllegalAccessLogger.Mode mode = IllegalAccessLogger.Mode.ONESHOT;
|
||||
if (value != null) {
|
||||
switch (value) {
|
||||
case "deny":
|
||||
return;
|
||||
case "permit":
|
||||
break;
|
||||
case "warn":
|
||||
mode = IllegalAccessLogger.Mode.WARN;
|
||||
break;
|
||||
case "debug":
|
||||
mode = IllegalAccessLogger.Mode.DEBUG;
|
||||
break;
|
||||
default:
|
||||
fail("Value specified to --illegal-access not recognized:"
|
||||
+ " '" + value + "'");
|
||||
return;
|
||||
}
|
||||
}
|
||||
IllegalAccessLogger.Builder builder
|
||||
= new IllegalAccessLogger.Builder(mode, System.err);
|
||||
|
||||
Map<String, Set<String>> map1 = SystemModules.concealedPackagesToOpen();
|
||||
Map<String, Set<String>> map2 = SystemModules.exportedPackagesToOpen();
|
||||
if (map1.isEmpty() && map2.isEmpty()) {
|
||||
// need to generate maps when on exploded build
|
||||
IllegalAccessMaps maps = IllegalAccessMaps.generate(limitedFinder());
|
||||
map1 = maps.concealedPackagesToOpen();
|
||||
map2 = maps.exportedPackagesToOpen();
|
||||
}
|
||||
|
||||
// open specific packages in the system modules
|
||||
for (Module m : bootLayer.modules()) {
|
||||
ModuleDescriptor descriptor = m.getDescriptor();
|
||||
String name = m.getName();
|
||||
|
||||
// skip open modules
|
||||
if (descriptor.isOpen()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip modules loaded from the upgrade module path
|
||||
if (upgradeModulePath != null
|
||||
&& upgradeModulePath.find(name).isPresent()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Set<String> concealedPackages = map1.getOrDefault(name, Set.of());
|
||||
Set<String> exportedPackages = map2.getOrDefault(name, Set.of());
|
||||
|
||||
// refresh the set of concealed and exported packages if needed
|
||||
if (extraExportsOrOpens) {
|
||||
concealedPackages = new HashSet<>(concealedPackages);
|
||||
exportedPackages = new HashSet<>(exportedPackages);
|
||||
Iterator<String> iterator = concealedPackages.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
String pn = iterator.next();
|
||||
if (m.isExported(pn, BootLoader.getUnnamedModule())) {
|
||||
// concealed package is exported to ALL-UNNAMED
|
||||
iterator.remove();
|
||||
exportedPackages.add(pn);
|
||||
}
|
||||
}
|
||||
iterator = exportedPackages.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
String pn = iterator.next();
|
||||
if (m.isOpen(pn, BootLoader.getUnnamedModule())) {
|
||||
// exported package is opened to ALL-UNNAMED
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// log reflective access to all types in concealed packages
|
||||
builder.logAccessToConcealedPackages(m, concealedPackages);
|
||||
|
||||
// log reflective access to non-public members/types in exported packages
|
||||
builder.logAccessToExportedPackages(m, exportedPackages);
|
||||
|
||||
// open the packages to unnamed modules
|
||||
JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
|
||||
jla.addOpensToAllUnnamed(m, concat(concealedPackages.iterator(),
|
||||
exportedPackages.iterator()));
|
||||
}
|
||||
|
||||
builder.complete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the values of --add-reads, -add-exports, --add-opens or
|
||||
* --patch-modules options that are encoded in system properties.
|
||||
@ -708,17 +838,16 @@ public final class ModuleBootstrap {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the names and resolution bit of each module in the configuration,
|
||||
* emitting warnings if needed.
|
||||
* Checks incubating status of modules in the configuration
|
||||
*/
|
||||
private static void checkModuleNamesAndStatus(Configuration cf) {
|
||||
private static void checkIncubatingStatus(Configuration cf) {
|
||||
String incubating = null;
|
||||
for (ResolvedModule rm : cf.modules()) {
|
||||
ModuleReference mref = rm.reference();
|
||||
String mn = mref.descriptor().name();
|
||||
for (ResolvedModule resolvedModule : cf.modules()) {
|
||||
ModuleReference mref = resolvedModule.reference();
|
||||
|
||||
// emit warning if the WARN_INCUBATING module resolution bit set
|
||||
if (ModuleResolution.hasIncubatingWarning(mref)) {
|
||||
String mn = mref.descriptor().name();
|
||||
if (incubating == null) {
|
||||
incubating = mn;
|
||||
} else {
|
||||
@ -777,6 +906,21 @@ public final class ModuleBootstrap {
|
||||
}
|
||||
}
|
||||
|
||||
static <T> Iterator<T> concat(Iterator<T> iterator1, Iterator<T> iterator2) {
|
||||
return new Iterator<T>() {
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return iterator1.hasNext() || iterator2.hasNext();
|
||||
}
|
||||
@Override
|
||||
public T next() {
|
||||
if (iterator1.hasNext()) return iterator1.next();
|
||||
if (iterator2.hasNext()) return iterator2.next();
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static class PerfCounters {
|
||||
|
||||
static PerfCounter systemModulesTime
|
||||
@ -791,6 +935,8 @@ public final class ModuleBootstrap {
|
||||
= PerfCounter.newPerfCounter("jdk.module.bootstrap.layerCreateTime");
|
||||
static PerfCounter loadModulesTime
|
||||
= PerfCounter.newPerfCounter("jdk.module.bootstrap.loadModulesTime");
|
||||
static PerfCounter adjustModulesTime
|
||||
= PerfCounter.newPerfCounter("jdk.module.bootstrap.adjustModulesTime");
|
||||
static PerfCounter bootstrapTime
|
||||
= PerfCounter.newPerfCounter("jdk.module.bootstrap.totalTime");
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ import java.io.UncheckedIOException;
|
||||
import java.lang.module.FindException;
|
||||
import java.lang.module.InvalidModuleDescriptorException;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.module.ModuleDescriptor.Builder;
|
||||
import java.lang.module.ModuleFinder;
|
||||
import java.lang.module.ModuleReference;
|
||||
import java.net.URI;
|
||||
@ -404,6 +405,9 @@ public class ModulePath implements ModuleFinder {
|
||||
|
||||
private static final String SERVICES_PREFIX = "META-INF/services/";
|
||||
|
||||
private static final Attributes.Name AUTOMATIC_MODULE_NAME
|
||||
= new Attributes.Name("Automatic-Module-Name");
|
||||
|
||||
/**
|
||||
* Returns the service type corresponding to the name of a services
|
||||
* configuration file if it is a legal type name.
|
||||
@ -445,48 +449,68 @@ public class ModulePath implements ModuleFinder {
|
||||
/**
|
||||
* Treat the given JAR file as a module as follows:
|
||||
*
|
||||
* 1. The module name (and optionally the version) is derived from the file
|
||||
* name of the JAR file
|
||||
* 2. All packages are derived from the .class files in the JAR file
|
||||
* 3. The contents of any META-INF/services configuration files are mapped
|
||||
* 1. The value of the Automatic-Module-Name attribute is the module name
|
||||
* 2. The version, and the module name when the Automatic-Module-Name
|
||||
* attribute is not present, is derived from the file ame of the JAR file
|
||||
* 3. All packages are derived from the .class files in the JAR file
|
||||
* 4. The contents of any META-INF/services configuration files are mapped
|
||||
* to "provides" declarations
|
||||
* 4. The Main-Class attribute in the main attributes of the JAR manifest
|
||||
* 5. The Main-Class attribute in the main attributes of the JAR manifest
|
||||
* is mapped to the module descriptor mainClass if possible
|
||||
*/
|
||||
private ModuleDescriptor deriveModuleDescriptor(JarFile jf)
|
||||
throws IOException
|
||||
{
|
||||
// Derive module name and version from JAR file name
|
||||
// Read Automatic-Module-Name attribute if present
|
||||
Manifest man = jf.getManifest();
|
||||
Attributes attrs = null;
|
||||
String moduleName = null;
|
||||
if (man != null) {
|
||||
attrs = man.getMainAttributes();
|
||||
if (attrs != null) {
|
||||
moduleName = attrs.getValue(AUTOMATIC_MODULE_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
// Derive the version, and the module name if needed, from JAR file name
|
||||
String fn = jf.getName();
|
||||
int i = fn.lastIndexOf(File.separator);
|
||||
if (i != -1)
|
||||
fn = fn.substring(i+1);
|
||||
fn = fn.substring(i + 1);
|
||||
|
||||
// drop .jar
|
||||
String mn = fn.substring(0, fn.length()-4);
|
||||
// drop ".jar"
|
||||
String name = fn.substring(0, fn.length() - 4);
|
||||
String vs = null;
|
||||
|
||||
// find first occurrence of -${NUMBER}. or -${NUMBER}$
|
||||
Matcher matcher = Patterns.DASH_VERSION.matcher(mn);
|
||||
Matcher matcher = Patterns.DASH_VERSION.matcher(name);
|
||||
if (matcher.find()) {
|
||||
int start = matcher.start();
|
||||
|
||||
// attempt to parse the tail as a version string
|
||||
try {
|
||||
String tail = mn.substring(start+1);
|
||||
String tail = name.substring(start + 1);
|
||||
ModuleDescriptor.Version.parse(tail);
|
||||
vs = tail;
|
||||
} catch (IllegalArgumentException ignore) { }
|
||||
|
||||
mn = mn.substring(0, start);
|
||||
name = name.substring(0, start);
|
||||
}
|
||||
|
||||
// finally clean up the module name
|
||||
mn = cleanModuleName(mn);
|
||||
// Create builder, using the name derived from file name when
|
||||
// Automatic-Module-Name not present
|
||||
Builder builder;
|
||||
if (moduleName != null) {
|
||||
try {
|
||||
builder = ModuleDescriptor.newAutomaticModule(moduleName);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new FindException(AUTOMATIC_MODULE_NAME + ": " + e.getMessage());
|
||||
}
|
||||
} else {
|
||||
builder = ModuleDescriptor.newAutomaticModule(cleanModuleName(name));
|
||||
}
|
||||
|
||||
// Builder throws IAE if module name is empty or invalid
|
||||
ModuleDescriptor.Builder builder = ModuleDescriptor.newAutomaticModule(mn);
|
||||
// module version if present
|
||||
if (vs != null)
|
||||
builder.version(vs);
|
||||
|
||||
@ -541,9 +565,7 @@ public class ModulePath implements ModuleFinder {
|
||||
}
|
||||
|
||||
// Main-Class attribute if it exists
|
||||
Manifest man = jf.getManifest();
|
||||
if (man != null) {
|
||||
Attributes attrs = man.getMainAttributes();
|
||||
if (attrs != null) {
|
||||
String mainClass = attrs.getValue(Attributes.Name.MAIN_CLASS);
|
||||
if (mainClass != null) {
|
||||
mainClass = mainClass.replace("/", ".");
|
||||
|
@ -41,6 +41,10 @@ import java.util.function.Supplier;
|
||||
|
||||
public class ModuleReferenceImpl extends ModuleReference {
|
||||
|
||||
// location of module
|
||||
private final URI location;
|
||||
|
||||
// the module reader
|
||||
private final Supplier<ModuleReader> readerSupplier;
|
||||
|
||||
// non-null if the module is patched
|
||||
@ -74,6 +78,7 @@ public class ModuleReferenceImpl extends ModuleReference {
|
||||
ModuleResolution moduleResolution)
|
||||
{
|
||||
super(descriptor, Objects.requireNonNull(location));
|
||||
this.location = location;
|
||||
this.readerSupplier = readerSupplier;
|
||||
this.patcher = patcher;
|
||||
this.target = target;
|
||||
@ -148,7 +153,7 @@ public class ModuleReferenceImpl extends ModuleReference {
|
||||
int hc = hash;
|
||||
if (hc == 0) {
|
||||
hc = descriptor().hashCode();
|
||||
hc = 43 * hc + Objects.hashCode(location());
|
||||
hc = 43 * hc + Objects.hashCode(location);
|
||||
hc = 43 * hc + Objects.hashCode(patcher);
|
||||
if (hc == 0)
|
||||
hc = -1;
|
||||
@ -169,7 +174,7 @@ public class ModuleReferenceImpl extends ModuleReference {
|
||||
// when the modules have equal module descriptors, are at the
|
||||
// same location, and are patched by the same patcher.
|
||||
return Objects.equals(this.descriptor(), that.descriptor())
|
||||
&& Objects.equals(this.location(), that.location())
|
||||
&& Objects.equals(this.location, that.location)
|
||||
&& Objects.equals(this.patcher, that.patcher);
|
||||
}
|
||||
|
||||
@ -179,7 +184,7 @@ public class ModuleReferenceImpl extends ModuleReference {
|
||||
sb.append("[module ");
|
||||
sb.append(descriptor().name());
|
||||
sb.append(", location=");
|
||||
sb.append(location().orElseThrow(() -> new InternalError()));
|
||||
sb.append(location);
|
||||
if (isPatched()) sb.append(" (patched)");
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
|
@ -136,10 +136,12 @@ public class Modules {
|
||||
public static void addProvides(Module m, Class<?> service, Class<?> impl) {
|
||||
ModuleLayer layer = m.getLayer();
|
||||
|
||||
if (layer == null || layer == ModuleLayer.boot()) {
|
||||
PrivilegedAction<ClassLoader> pa = m::getClassLoader;
|
||||
ClassLoader loader = AccessController.doPrivileged(pa);
|
||||
|
||||
ClassLoader platformClassLoader = ClassLoaders.platformClassLoader();
|
||||
if (layer == null || loader == null || loader == platformClassLoader) {
|
||||
// update ClassLoader catalog
|
||||
PrivilegedAction<ClassLoader> pa = m::getClassLoader;
|
||||
ClassLoader loader = AccessController.doPrivileged(pa);
|
||||
ServicesCatalog catalog;
|
||||
if (loader == null) {
|
||||
catalog = BootLoader.getServicesCatalog();
|
||||
|
@ -26,6 +26,9 @@
|
||||
package jdk.internal.module;
|
||||
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* SystemModules class will be generated at link time to create
|
||||
@ -57,8 +60,7 @@ public final class SystemModules {
|
||||
public static int PACKAGES_IN_BOOT_LAYER = 1024;
|
||||
|
||||
/**
|
||||
* @return {@code false} if there are no split packages in the run-time
|
||||
* image, {@code true} if there are or if it's not been checked.
|
||||
* Return true if there are no split packages in the run-time image.
|
||||
*/
|
||||
public static boolean hasSplitPackages() {
|
||||
return true;
|
||||
@ -98,4 +100,20 @@ public final class SystemModules {
|
||||
public static ModuleResolution[] moduleResolutions() {
|
||||
throw new InternalError("expected to be overridden at link time");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the map of module concealed packages to open. The map key is the
|
||||
* module name, the value is the set of concealed packages to open.
|
||||
*/
|
||||
public static Map<String, Set<String>> concealedPackagesToOpen() {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the map of module exported packages to open. The map key is the
|
||||
* module name, the value is the set of exported packages to open.
|
||||
*/
|
||||
public static Map<String, Set<String>> exportedPackagesToOpen() {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -92,7 +92,6 @@ import jdk.internal.misc.VM;
|
||||
import jdk.internal.module.ModuleBootstrap;
|
||||
import jdk.internal.module.Modules;
|
||||
|
||||
|
||||
public final class LauncherHelper {
|
||||
|
||||
// No instantiation
|
||||
@ -492,16 +491,16 @@ public final class LauncherHelper {
|
||||
if (s.length == 2) {
|
||||
String mn = s[0];
|
||||
String pn = s[1];
|
||||
|
||||
ModuleLayer.boot().findModule(mn).ifPresent(m -> {
|
||||
if (m.getDescriptor().packages().contains(pn)) {
|
||||
ModuleLayer.boot()
|
||||
.findModule(mn)
|
||||
.filter(m -> m.getDescriptor().packages().contains(pn))
|
||||
.ifPresent(m -> {
|
||||
if (open) {
|
||||
Modules.addOpensToAllUnnamed(m, pn);
|
||||
} else {
|
||||
Modules.addExportsToAllUnnamed(m, pn);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -618,7 +617,7 @@ public final class LauncherHelper {
|
||||
}
|
||||
} catch (LinkageError le) {
|
||||
abort(null, "java.launcher.module.error3", mainClass, m.getName(),
|
||||
le.getClass().getName() + ": " + le.getLocalizedMessage());
|
||||
le.getClass().getName() + ": " + le.getLocalizedMessage());
|
||||
}
|
||||
if (c == null) {
|
||||
abort(null, "java.launcher.module.error2", mainClass, mainModule);
|
||||
@ -719,7 +718,7 @@ public final class LauncherHelper {
|
||||
mainClass.getName(), mainClass.getModule(),
|
||||
e.getClass().getName(), e.getLocalizedMessage());
|
||||
} else {
|
||||
abort(e,"java.launcher.cls.error7", mainClass.getName(),
|
||||
abort(e, "java.launcher.cls.error7", mainClass.getName(),
|
||||
e.getClass().getName(), e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ java.launcher.opt.footer = \
|
||||
\ ALL-MODULE-PATH.\n\
|
||||
\ --list-modules\n\
|
||||
\ list observable modules and exit\n\
|
||||
\ --d <module name>\n\
|
||||
\ -d <module name>\n\
|
||||
\ --describe-module <module name>\n\
|
||||
\ describe a module and exit\n\
|
||||
\ --dry-run create VM and load main class but do not execute main method.\n\
|
||||
@ -166,10 +166,11 @@ java.launcher.X.usage=\n\
|
||||
\ --add-opens <module>/<package>=<target-module>(,<target-module>)*\n\
|
||||
\ updates <module> to open <package> to\n\
|
||||
\ <target-module>, regardless of module declaration.\n\
|
||||
\ --permit-illegal-access\n\
|
||||
\ permit illegal access to members of types in named modules\n\
|
||||
\ by code in unnamed modules. This compatibility option will\n\
|
||||
\ be removed in the next release.\n\
|
||||
\ --illegal-access=<value>\n\
|
||||
\ permit or deny access to members of types in named modules\n\
|
||||
\ by code in unnamed modules.\n\
|
||||
\ <value> is one of "deny", "permit", "warn", or "debug"\n\
|
||||
\ This option will be removed in a future release.\n\
|
||||
\ --limit-modules <module name>[,<module name>...]\n\
|
||||
\ limit the universe of observable modules\n\
|
||||
\ --patch-module <module>=<file>({0}<file>)*\n\
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -455,14 +455,6 @@ JVM_AddModuleExportsToAll(JNIEnv *env, jobject from_module, const char* package)
|
||||
JNIEXPORT void JNICALL
|
||||
JVM_AddReadsModule(JNIEnv *env, jobject from_module, jobject source_module);
|
||||
|
||||
/*
|
||||
* Add a package to a module.
|
||||
* module: module that will contain the package
|
||||
* package: package to add to the module
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
JVM_AddModulePackage(JNIEnv* env, jobject module, const char* package);
|
||||
|
||||
/*
|
||||
* Reflection support functions
|
||||
*/
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -176,23 +176,3 @@ Java_java_lang_Module_addExportsToAllUnnamed0(JNIEnv *env, jclass cls,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_java_lang_Module_addPackage0(JNIEnv *env, jclass cls, jobject m, jstring pkg)
|
||||
{
|
||||
char buf[128];
|
||||
char* pkg_name;
|
||||
|
||||
if (pkg == NULL) {
|
||||
JNU_ThrowNullPointerException(env, "package is null");
|
||||
return;
|
||||
}
|
||||
|
||||
pkg_name = GetInternalPackageName(env, pkg, buf, (jsize)sizeof(buf));
|
||||
if (pkg_name != NULL) {
|
||||
JVM_AddModulePackage(env, m, pkg_name);
|
||||
if (pkg_name != buf) {
|
||||
free(pkg_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,326 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the Classpath exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2003 Wily Technology, Inc.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provides services that allow Java programming language agents to instrument
|
||||
* programs running on the JVM. The mechanism for instrumentation is modification
|
||||
* of the byte-codes of methods.
|
||||
*
|
||||
* <p> An agent is deployed as a JAR file. An attribute in the JAR file manifest
|
||||
* specifies the agent class which will be loaded to start the agent. Agents can
|
||||
* be started in several ways:
|
||||
*
|
||||
* <ol>
|
||||
* <li><p> For implementations that support a command-line interface, an agent
|
||||
* can be started by specifying an option on the command-line. </p></li>
|
||||
*
|
||||
* <li><p> An implementation may support a mechanism to start agents some time
|
||||
* after the VM has started. For example, an implementation may provide a
|
||||
* mechanism that allows a tool to <i>attach</i> to a running application, and
|
||||
* initiate the loading of the tool's agent into the running application. </p></li>
|
||||
*
|
||||
* <li><p> An agent may be packaged with an application in an executable JAR
|
||||
* file.</p></li>
|
||||
* </ol>
|
||||
*
|
||||
* <p> Each of these ways to start an agent is described below.
|
||||
*
|
||||
*
|
||||
* <h3>Starting an Agent from the Command-Line Interface</h3>
|
||||
*
|
||||
* <p> Where an implementation provides a means to start agents from the
|
||||
* command-line interface, an agent is started by adding the following option
|
||||
* to the command-line:
|
||||
*
|
||||
* <blockquote>{@code
|
||||
* -javaagent:<jarpath>[=<options>]
|
||||
* }</blockquote>
|
||||
*
|
||||
* where <i>{@code <jarpath>}</i> is the path to the agent JAR file and
|
||||
* <i>{@code <options>}</i> is the agent options.
|
||||
*
|
||||
* <p> The manifest of the agent JAR file must contain the attribute {@code
|
||||
* Premain-Class} in its main manifest. The value of this attribute is the
|
||||
* name of the <i>agent class</i>. The agent class must implement a public
|
||||
* static {@code premain} method similar in principle to the {@code main}
|
||||
* application entry point. After the Java Virtual Machine (JVM) has
|
||||
* initialized, the {@code premain} method will be called, then the real
|
||||
* application {@code main} method. The {@code premain} method must return
|
||||
* in order for the startup to proceed.
|
||||
*
|
||||
* <p> The {@code premain} method has one of two possible signatures. The
|
||||
* JVM first attempts to invoke the following method on the agent class:
|
||||
*
|
||||
* <blockquote>{@code
|
||||
* public static void premain(String agentArgs, Instrumentation inst)
|
||||
* }</blockquote>
|
||||
*
|
||||
* <p> If the agent class does not implement this method then the JVM will
|
||||
* attempt to invoke:
|
||||
* <blockquote>{@code
|
||||
* public static void premain(String agentArgs)
|
||||
* }</blockquote>
|
||||
|
||||
* <p> The agent class may also have an {@code agentmain} method for use when
|
||||
* the agent is started after VM startup (see below). When the agent is started
|
||||
* using a command-line option, the {@code agentmain} method is not invoked.
|
||||
*
|
||||
* <p> Each agent is passed its agent options via the {@code agentArgs} parameter.
|
||||
* The agent options are passed as a single string, any additional parsing
|
||||
* should be performed by the agent itself.
|
||||
*
|
||||
* <p> If the agent cannot be started (for example, because the agent class
|
||||
* cannot be loaded, or because the agent class does not have an appropriate
|
||||
* {@code premain} method), the JVM will abort. If a {@code premain} method
|
||||
* throws an uncaught exception, the JVM will abort.
|
||||
*
|
||||
* <p> An implementation is not required to provide a way to start agents
|
||||
* from the command-line interface. When it does, then it supports the
|
||||
* {@code -javaagent} option as specified above. The {@code -javaagent} option
|
||||
* may be used multiple times on the same command-line, thus starting multiple
|
||||
* agents. The {@code premain} methods will be called in the order that the
|
||||
* agents are specified on the command line. More than one agent may use the
|
||||
* same <i>{@code <jarpath>}</i>.
|
||||
*
|
||||
* <p> There are no modeling restrictions on what the agent {@code premain}
|
||||
* method may do. Anything application {@code main} can do, including creating
|
||||
* threads, is legal from {@code premain}.
|
||||
*
|
||||
*
|
||||
* <h3>Starting an Agent After VM Startup</h3>
|
||||
*
|
||||
* <p> An implementation may provide a mechanism to start agents sometime after
|
||||
* the the VM has started. The details as to how this is initiated are
|
||||
* implementation specific but typically the application has already started and
|
||||
* its {@code main} method has already been invoked. In cases where an
|
||||
* implementation supports the starting of agents after the VM has started the
|
||||
* following applies:
|
||||
*
|
||||
* <ol>
|
||||
*
|
||||
* <li><p> The manifest of the agent JAR must contain the attribute {@code
|
||||
* Agent-Class} in its main manfiest. The value of this attribute is the name
|
||||
* of the <i>agent class</i>. </p></li>
|
||||
*
|
||||
* <li><p> The agent class must implement a public static {@code agentmain}
|
||||
* method. </p></li>
|
||||
*
|
||||
* </ol>
|
||||
*
|
||||
* <p> The {@code agentmain} method has one of two possible signatures. The JVM
|
||||
* first attempts to invoke the following method on the agent class:
|
||||
*
|
||||
* <blockquote>{@code
|
||||
* public static void agentmain(String agentArgs, Instrumentation inst)
|
||||
* }</blockquote>
|
||||
*
|
||||
* <p> If the agent class does not implement this method then the JVM will
|
||||
* attempt to invoke:
|
||||
*
|
||||
* <blockquote>{@code
|
||||
* public static void agentmain(String agentArgs)
|
||||
* }</blockquote>
|
||||
*
|
||||
* <p> The agent class may also have a {@code premain} method for use when the
|
||||
* agent is started using a command-line option. When the agent is started after
|
||||
* VM startup the {@code premain} method is not invoked.
|
||||
*
|
||||
* <p> The agent is passed its agent options via the {@code agentArgs}
|
||||
* parameter. The agent options are passed as a single string, any additional
|
||||
* parsing should be performed by the agent itself.
|
||||
*
|
||||
* <p> The {@code agentmain} method should do any necessary initialization
|
||||
* required to start the agent. When startup is complete the method should
|
||||
* return. If the agent cannot be started (for example, because the agent class
|
||||
* cannot be loaded, or because the agent class does not have a conformant
|
||||
* {@code agentmain} method), the JVM will not abort. If the {@code agentmain}
|
||||
* method throws an uncaught exception it will be ignored (but may be logged
|
||||
* by the JVM for troubleshooting purposes).
|
||||
*
|
||||
*
|
||||
* <h3>Including an Agent in an Executable JAR file</h3>
|
||||
*
|
||||
* <p> The JAR File Specification defines manifest attributes for standalone
|
||||
* applications that are packaged as <em>executable JAR files</em>. If an
|
||||
* implementation supports a mechanism to start an application as an executable
|
||||
* JAR then the main manifest may include the {@code Launcher-Agent-Class}
|
||||
* attribute to specify the class name of an agent to start before the application
|
||||
* {@code main} method is invoked. The Java virtual machine attempts to
|
||||
* invoke the following method on the agent class:
|
||||
*
|
||||
* <blockquote>{@code
|
||||
* public static void agentmain(String agentArgs, Instrumentation inst)
|
||||
* }</blockquote>
|
||||
*
|
||||
* <p> If the agent class does not implement this method then the JVM will
|
||||
* attempt to invoke:
|
||||
*
|
||||
* <blockquote>{@code
|
||||
* public static void agentmain(String agentArgs)
|
||||
* }</blockquote>
|
||||
*
|
||||
* <p> The value of the {@code agentArgs} parameter is always the empty string.
|
||||
*
|
||||
* <p> The {@code agentmain} method should do any necessary initialization
|
||||
* required to start the agent and return. If the agent cannot be started, for
|
||||
* example the agent class cannot be loaded, the agent class does not define a
|
||||
* conformant {@code agentmain} method, or the {@code agentmain} method throws
|
||||
* an uncaught exception or error, the JVM will abort.
|
||||
*
|
||||
*
|
||||
* <h3> Loading agent classes and the modules/classes available to the agent
|
||||
* class </h3>
|
||||
*
|
||||
* <p> Classes loaded from the agent JAR file are loaded by the
|
||||
* {@linkplain ClassLoader#getSystemClassLoader() system class loader} and are
|
||||
* members of the system class loader's {@linkplain ClassLoader#getUnnamedModule()
|
||||
* unnamed module}. The system class loader typically defines the class containing
|
||||
* the application {@code main} method too.
|
||||
*
|
||||
* <p> The classes visible to the agent class are the classes visible to the system
|
||||
* class loader and minimally include:
|
||||
*
|
||||
* <ul>
|
||||
*
|
||||
* <li><p> The classes in packages exported by the modules in the {@linkplain
|
||||
* ModuleLayer#boot() boot layer}. Whether the boot layer contains all platform
|
||||
* modules or not will depend on the initial module or how the application was
|
||||
* started. </p></li>
|
||||
*
|
||||
* <li><p> The classes that can be defined by the system class loader (typically
|
||||
* the class path) to be members of its unnamed module. </p></li>
|
||||
*
|
||||
* <li><p> Any classes that the agent arranges to be defined by the bootstrap
|
||||
* class loader to be members of its unnamed module. </p></li>
|
||||
*
|
||||
* </ul>
|
||||
*
|
||||
* <p> If agent classes need to link to classes in platform (or other) modules
|
||||
* that are not in the boot layer then the application may need to be started in
|
||||
* a way that ensures that these modules are in the boot layer. In the JDK
|
||||
* implementation for example, the {@code --add-modules} command line option can
|
||||
* be used to add modules to the set of root modules to resolve at startup. </p>
|
||||
*
|
||||
* <p> Supporting classes that the agent arranges to be loaded by the bootstrap
|
||||
* class loader (by means of {@link Instrumentation#appendToBootstrapClassLoaderSearch
|
||||
* appendToBootstrapClassLoaderSearch} or the {@code Boot-Class-Path} attribute
|
||||
* specified below), must link only to classes defined to the bootstrap class loader.
|
||||
* There is no guarantee that all platform classes can be defined by the boot
|
||||
* class loader.
|
||||
*
|
||||
* <p> If a custom system class loader is configured (by means of the system property
|
||||
* {@code java.system.class.loader} as specified in the {@link
|
||||
* ClassLoader#getSystemClassLoader() getSystemClassLoader} method) then it must
|
||||
* define the {@code appendToClassPathForInstrumentation} method as specified in
|
||||
* {@link Instrumentation#appendToSystemClassLoaderSearch appendToSystemClassLoaderSearch}.
|
||||
* In other words, a custom system class loader must support the mechanism to
|
||||
* add an agent JAR file to the system class loader search.
|
||||
*
|
||||
* <h3>Manifest Attributes</h3>
|
||||
*
|
||||
* <p> The following manifest attributes are defined for an agent JAR file:
|
||||
*
|
||||
* <blockquote><dl>
|
||||
*
|
||||
* <dt>{@code Premain-Class}</dt>
|
||||
* <dd> When an agent is specified at JVM launch time this attribute specifies
|
||||
* the agent class. That is, the class containing the {@code premain} method.
|
||||
* When an agent is specified at JVM launch time this attribute is required. If
|
||||
* the attribute is not present the JVM will abort. Note: this is a class name,
|
||||
* not a file name or path. </dd>
|
||||
*
|
||||
* <dt>{@code Agent-Class}</dt>
|
||||
* <dd> If an implementation supports a mechanism to start agents sometime after
|
||||
* the VM has started then this attribute specifies the agent class. That is,
|
||||
* the class containing the {@code agentmain} method. This attribute is required
|
||||
* if it is not present the agent will not be started. Note: this is a class name,
|
||||
* not a file name or path. </dd>
|
||||
*
|
||||
* <dt>{@code Launcher-Agent-Class}</dt>
|
||||
* <dd> If an implementation supports a mechanism to start an application as an
|
||||
* executable JAR then the main manifest may include this attribute to specify
|
||||
* the class name of an agent to start before the application {@code main}
|
||||
* method is invoked. </dd>
|
||||
*
|
||||
* <dt>{@code Boot-Class-Path}</dt>
|
||||
* <dd> A list of paths to be searched by the bootstrap class loader. Paths
|
||||
* represent directories or libraries (commonly referred to as JAR or zip
|
||||
* libraries on many platforms). These paths are searched by the bootstrap class
|
||||
* loader after the platform specific mechanisms of locating a class have failed.
|
||||
* Paths are searched in the order listed. Paths in the list are separated by one
|
||||
* or more spaces. A path takes the syntax of the path component of a hierarchical
|
||||
* URI. The path is absolute if it begins with a slash character ('/'), otherwise
|
||||
* it is relative. A relative path is resolved against the absolute path of the
|
||||
* agent JAR file. Malformed and non-existent paths are ignored. When an agent is
|
||||
* started sometime after the VM has started then paths that do not represent a
|
||||
* JAR file are ignored. This attribute is optional. </dd>
|
||||
*
|
||||
* <dt>{@code Can-Redefine-Classes}</dt>
|
||||
* <dd> Boolean ({@code true} or {@code false}, case irrelevant). Is the ability
|
||||
* to redefine classes needed by this agent. Values other than {@code true} are
|
||||
* considered {@code false}. This attribute is optional, the default is {@code
|
||||
* false}. </dd>
|
||||
*
|
||||
* <dt>{@code Can-Retransform-Classes}</dt>
|
||||
* <dd> Boolean ({@code true} or {@code false}, case irrelevant). Is the ability
|
||||
* to retransform classes needed by this agent. Values other than {@code true}
|
||||
* are considered {@code false}. This attribute is optional, the default is
|
||||
* {@code false}. </dd>
|
||||
*
|
||||
* <dt>{@code Can-Set-Native-Method-Prefix}</dt>
|
||||
* <dd> Boolean ({@code true} or {@code false}, case irrelevant). Is the ability
|
||||
* to set native method prefix needed by this agent. Values other than {@code
|
||||
* true} are considered {@code false}. This attribute is optional, the default
|
||||
* is {@code false}. </dd>
|
||||
*
|
||||
* </dl></blockquote>
|
||||
*
|
||||
* <p> An agent JAR file may have both the {@code Premain-Class} and {@code
|
||||
* Agent-Class} attributes present in the manifest. When the agent is started
|
||||
* on the command-line using the {@code -javaagent} option then the {@code
|
||||
* Premain-Class} attribute specifies the name of the agent class and the {@code
|
||||
* Agent-Class} attribute is ignored. Similarly, if the agent is started sometime
|
||||
* after the VM has started, then the {@code Agent-Class} attribute specifies
|
||||
* the name of the agent class (the value of {@code Premain-Class} attribute is
|
||||
* ignored).
|
||||
*
|
||||
*
|
||||
* <h3>Instrumenting code in modules</h3>
|
||||
*
|
||||
* <p> As an aid to agents that deploy supporting classes on the search path of
|
||||
* the bootstrap class loader, or the search path of the class loader that loads
|
||||
* the main agent class, the Java virtual machine arranges for the module of
|
||||
* transformed classes to read the unnamed module of both class loaders.
|
||||
*
|
||||
* @since 1.5
|
||||
* @revised 1.6
|
||||
* @revised 9
|
||||
*/
|
||||
|
||||
package java.lang.instrument;
|
@ -1,356 +0,0 @@
|
||||
<!--
|
||||
Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
|
||||
This code is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License version 2 only, as
|
||||
published by the Free Software Foundation. Oracle designates this
|
||||
particular file as subject to the "Classpath" exception as provided
|
||||
by Oracle in the LICENSE file that accompanied this code.
|
||||
|
||||
This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
version 2 for more details (a copy is included in the LICENSE file that
|
||||
accompanied this code).
|
||||
|
||||
You should have received a copy of the GNU General Public License version
|
||||
2 along with this work; if not, write to the Free Software Foundation,
|
||||
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
or visit www.oracle.com if you need additional information or have any
|
||||
questions.
|
||||
-->
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
|
||||
<html>
|
||||
<head>
|
||||
<!--
|
||||
|
||||
Copyright 2003 Wily Technology, Inc.
|
||||
|
||||
-->
|
||||
</head>
|
||||
|
||||
<body bgcolor="white">
|
||||
|
||||
Provides services that allow Java programming language agents to instrument programs running on the JVM.
|
||||
The mechanism for instrumentation is modification of the byte-codes of methods.
|
||||
|
||||
<h2>Package Specification</h2>
|
||||
|
||||
<P>
|
||||
An agent is deployed as a JAR file. An attribute in the JAR file manifest specifies the
|
||||
agent class which will be loaded to start the agent. For implementations that support a command-line
|
||||
interface, an agent is started by specifying an option on the command-line.
|
||||
Implementations may also support a mechanism to start agents some time after the VM has
|
||||
started. For example, an implementation may provide a mechanism that allows a tool to
|
||||
<i>attach</i> to a running application, and initiate the loading of the tool's agent into
|
||||
the running application. The details as to how the load is initiated, is implementation
|
||||
dependent.
|
||||
|
||||
<h3>Command-Line Interface</h3>
|
||||
|
||||
<P>
|
||||
An implementation is not required to provide a way to start agents from the
|
||||
command-line interface. On implementations that do provide a way to start agents
|
||||
from the command-line interface, an agent is started by adding this option to
|
||||
the command-line:
|
||||
<blockquote>
|
||||
<code><b>-javaagent:</b></code><i>jarpath[</i><code><b>=</b></code><i>options]</i>
|
||||
</blockquote>
|
||||
<i>jarpath</i> is the path to the agent JAR file.
|
||||
<i>options</i> is the agent options.
|
||||
This switch may be used multiple times on the same command-line,
|
||||
thus creating multiple agents.
|
||||
More than one agent may use the same <i>jarpath</i>.
|
||||
An agent JAR file must conform to the JAR file specification.
|
||||
|
||||
<P>
|
||||
The manifest of the agent JAR file must contain the attribute <code>Premain-Class</code>. The
|
||||
value of this attribute is the name of the <i>agent class</i>. The agent class must implement a
|
||||
public static <code>premain</code> method similar in principle to the <code>main</code> application
|
||||
entry point. After the Java Virtual Machine (JVM) has initialized, each <code>premain</code> method
|
||||
will be called in the order the agents were specified, then the real application
|
||||
<code>main</code> method will be called.
|
||||
Each <code>premain</code> method must return in order for the startup sequence to proceed.
|
||||
|
||||
<P>
|
||||
The <code>premain</code> method has one of two possible signatures. The JVM first attempts to
|
||||
invoke the following method on the agent class:
|
||||
|
||||
<blockquote>
|
||||
<code>public static void
|
||||
premain(String agentArgs, Instrumentation inst);
|
||||
</code>
|
||||
</blockquote>
|
||||
|
||||
<P>
|
||||
If the agent class does not implement this method then the JVM will attempt to invoke:
|
||||
|
||||
<blockquote>
|
||||
<code>public static void
|
||||
premain(String agentArgs);
|
||||
</code>
|
||||
</blockquote>
|
||||
|
||||
<P>
|
||||
The agent class may also have an <code>agentmain</code> method for use when the agent is started
|
||||
after VM startup. When the agent is started using a command-line option, the <code>agentmain</code>
|
||||
method is not invoked.
|
||||
|
||||
|
||||
<P>
|
||||
The agent class will be loaded by the system class loader
|
||||
(see {@link java.lang.ClassLoader#getSystemClassLoader ClassLoader.getSystemClassLoader}). This is
|
||||
the class loader which typically loads the class containing the application <code>main</code> method.
|
||||
The system class loader must support a mechanism to add an agent JAR file to the system class path.
|
||||
If it is a custom system class loader then it must define the
|
||||
<code>appendToClassPathForInstrumentation</code> method as specified in
|
||||
{@link Instrumentation#appendToSystemClassLoaderSearch appendToSystemClassLoaderSearch}.
|
||||
The <code>premain</code> methods will be run under the same security and classloader
|
||||
rules as the application <code>main</code> method.
|
||||
There are no modeling restrictions on what the agent <code>premain</code> method may do.
|
||||
Anything application <code>main</code> can do, including creating threads, is legal from
|
||||
<code>premain</code>.
|
||||
|
||||
|
||||
<P>
|
||||
Each agent is passed its agent options via the <code>agentArgs</code> parameter.
|
||||
The agent options are passed as a single string,
|
||||
any additional parsing should be performed by the agent itself.
|
||||
|
||||
<P>
|
||||
If the agent cannot be resolved
|
||||
(for example, because the agent class cannot be loaded,
|
||||
or because the agent class does not have an appropriate <code>premain</code> method), the JVM will abort.
|
||||
If a <code>premain</code> method throws an uncaught exception, the JVM will abort.
|
||||
|
||||
|
||||
<h3>Starting Agents After VM Startup</h3>
|
||||
|
||||
<p>
|
||||
An implementation may provide a mechanism to start agents sometime after the
|
||||
the VM has started. The details as to how this is initiated are implementation
|
||||
specific but typically the application has already started and its <code>
|
||||
main</code> method has already been invoked. In cases where an implementation
|
||||
supports the starting of agents after the VM has started the following applies:
|
||||
|
||||
<ol>
|
||||
<li><p>The manifest of the agent JAR must contain the attribute <code>Agent-Class</code>.
|
||||
The value of this attribute is the name of the <i>agent class</i>. </p></li>
|
||||
|
||||
<li><p>The agent class must implement a public static <code>agentmain</code> method. </p></li>
|
||||
|
||||
<li><p>The system class loader (
|
||||
{@link java.lang.ClassLoader#getSystemClassLoader ClassLoader.getSystemClassLoader}) must
|
||||
support a mechanism to add an agent JAR file to the system class path.
|
||||
If it is a custom system class loader then it must define the
|
||||
<code>appendToClassPathForInstrumentation</code> method as specified in
|
||||
{@link Instrumentation#appendToSystemClassLoaderSearch appendToSystemClassLoaderSearch}.</li>
|
||||
</ol>
|
||||
|
||||
<P>
|
||||
The agent JAR is appended to the system class path. This is the class loader that typically loads
|
||||
the class containing the application <code>main</code> method. The agent class is loaded and the
|
||||
JVM attempts to invoke the <code>agentmain</code> method. The JVM first attempts to invoke
|
||||
the following method on the agent class:
|
||||
|
||||
<blockquote>
|
||||
<code>public static void
|
||||
agentmain(String agentArgs, Instrumentation inst);
|
||||
</code>
|
||||
</blockquote>
|
||||
|
||||
<P>
|
||||
If the agent class does not implement this method then the JVM will attempt to invoke:
|
||||
|
||||
<blockquote>
|
||||
<code>public static void
|
||||
agentmain(String agentArgs);
|
||||
</code>
|
||||
</blockquote>
|
||||
|
||||
<P>
|
||||
The agent class may also have an <code>premain</code> method for use when the agent is started
|
||||
using a command-line option. When the agent is started after VM startup the <code>premain</code>
|
||||
method is not invoked.
|
||||
|
||||
|
||||
<P>
|
||||
The agent is passed its agent options via the <code>agentArgs</code> parameter.
|
||||
The agent options are passed as a single string,
|
||||
any additional parsing should be performed by the agent itself.
|
||||
|
||||
<P>
|
||||
The <code>agentmain</code> method should do any necessary initialization
|
||||
required to start the agent. When startup is complete the method should
|
||||
return. If the agent cannot be started
|
||||
(for example, because the agent class cannot be loaded,
|
||||
or because the agent class does not have a conformant <code>agentmain</code> method), the JVM will
|
||||
not abort. If the <code>agentmain</code> method throws an uncaught exception it will be ignored.
|
||||
|
||||
|
||||
<h3>Deploying Agents in Executable JAR file</h3>
|
||||
|
||||
The JAR File Specification defines manifest attributes for standalone applications that are
|
||||
bundled as <em>executable JAR files</em>. If an implementation supports a mechanism to start
|
||||
an application as an executable JAR then the main manifest may include the
|
||||
<code>Launcher-Agent-Class</code> attribute to specify the class name
|
||||
of an agent to start before the application <code>main</code> method is invoked. The Java
|
||||
virtual machine attempts to invoke the following method on the agent class:
|
||||
|
||||
<blockquote>
|
||||
<code>public static void
|
||||
agentmain(String agentArgs, Instrumentation inst);
|
||||
</code>
|
||||
</blockquote>
|
||||
|
||||
<P>
|
||||
If the agent class does not implement this method then the JVM will attempt to invoke:
|
||||
|
||||
<blockquote>
|
||||
<code>public static void
|
||||
agentmain(String agentArgs);
|
||||
</code>
|
||||
</blockquote>
|
||||
|
||||
<p>
|
||||
The value of the <code>agentArgs</code> parameter is always the empty string.
|
||||
|
||||
<P>
|
||||
The <code>agentmain</code> method should do any necessary initialization
|
||||
required to start the agent and return. If the agent cannot be started, for
|
||||
example the agent class cannot be loaded, the agent class does not define a
|
||||
conformant <code>agentmain</code> method, or the <code>agentmain</code> method
|
||||
throws an uncaught exception or error, the JVM will abort.
|
||||
|
||||
|
||||
<h3>Visibility</h3>
|
||||
|
||||
The types visible to the agent class are the types visible to the system class
|
||||
loader. They minimally include the types in packages exported by
|
||||
<a href="{@docRoot}/java.base-summary.html">java.base</a> and
|
||||
<a href="{@docRoot}/java.instrument-summary.html">java.instrument</a>.
|
||||
Whether all {@linkplain ClassLoader#getPlatformClassLoader() platform classes}
|
||||
are visible or not will depend on the initial module or application.
|
||||
|
||||
<p>
|
||||
Supporting classes that the agent makes visible to the bootstrap class loader
|
||||
(by means of {@link Instrumentation#appendToBootstrapClassLoaderSearch
|
||||
appendToBootstrapClassLoaderSearch} or the <code>Boot-Class-Path</code> attribute
|
||||
specified below) can only link to types defined to the bootstrap class loader.
|
||||
There is no guarantee that all platform classes are visible to the boot class
|
||||
loader.
|
||||
|
||||
|
||||
<h3>Manifest Attributes</h3>
|
||||
|
||||
The following manifest attributes are defined for an agent JAR file:
|
||||
<blockquote>
|
||||
<dl>
|
||||
<dt><code>Premain-Class</code></dt>
|
||||
<dd>
|
||||
When an agent is specified at JVM launch time this attribute
|
||||
specifies the agent class.
|
||||
That is, the class containing the <code>premain</code> method.
|
||||
When an agent is specified at JVM launch time this attribute
|
||||
is required. If the attribute is not present the JVM will abort.
|
||||
Note: this is a class name, not a file name or path.
|
||||
</dd>
|
||||
<dt><code>Agent-Class</code></dt>
|
||||
<dd>
|
||||
If an implementation supports a mechanism to start agents
|
||||
sometime after the VM has started then this attribute specifies
|
||||
the agent class.
|
||||
That is, the class containing the <code>agentmain</code> method.
|
||||
This attribute is required, if it is not present the agent
|
||||
will not be started.
|
||||
Note: this is a class name, not a file name or path.
|
||||
</dd>
|
||||
<dt><code>Launcher-Agent-Class</code></dt>
|
||||
<dd>
|
||||
If an implementation supports a mechanism to start an application
|
||||
as an executable JAR then the main manifest may include this
|
||||
attribute to specify the class name of an agent to start before the
|
||||
application <code>main</code> method is invoked.
|
||||
</dd>
|
||||
<dt><code>Boot-Class-Path</code></dt>
|
||||
<dd>
|
||||
A list of paths to be searched by the bootstrap class
|
||||
loader. Paths represent directories or libraries
|
||||
(commonly referred to as JAR or zip libraries on
|
||||
many platforms).
|
||||
These paths are searched by the
|
||||
bootstrap class loader after the platform specific
|
||||
mechanisms of locating a class have failed.
|
||||
Paths are searched in the order listed.
|
||||
Paths in the list are separated by one or more spaces.
|
||||
A path takes the syntax of the path component of a
|
||||
hierarchical URI. The path is
|
||||
absolute if it begins with a slash character ('/'),
|
||||
otherwise it is relative. A relative path is resolved
|
||||
against the absolute path of the agent JAR file.
|
||||
Malformed and non-existent paths are ignored.
|
||||
When an agent is started sometime after the VM has
|
||||
started then paths that do not represent a JAR file
|
||||
are ignored.
|
||||
This attribute is optional.
|
||||
</dd>
|
||||
<dt><code>Can-Redefine-Classes</code></dt>
|
||||
<dd>
|
||||
Boolean (<code>true</code> or <code>false</code>, case irrelevant).
|
||||
Is the ability to redefine classes
|
||||
needed by this agent.
|
||||
Values other than <code>true</code> are considered <code>false</code>.
|
||||
This attribute is optional, the default is <code>false</code>.
|
||||
</dd>
|
||||
<dt><code>Can-Retransform-Classes</code></dt>
|
||||
<dd>
|
||||
Boolean (<code>true</code> or <code>false</code>, case irrelevant).
|
||||
Is the ability to retransform classes
|
||||
needed by this agent.
|
||||
Values other than <code>true</code> are considered <code>false</code>.
|
||||
This attribute is optional, the default is <code>false</code>.
|
||||
</dd>
|
||||
<dt><code>Can-Set-Native-Method-Prefix</code></dt>
|
||||
<dd>
|
||||
Boolean (<code>true</code> or <code>false</code>, case irrelevant).
|
||||
Is the ability to set native method prefix needed by this agent.
|
||||
Values other than <code>true</code> are considered <code>false</code>.
|
||||
This attribute is optional, the default is <code>false</code>.
|
||||
</dd>
|
||||
</dl>
|
||||
</blockquote>
|
||||
|
||||
<p>
|
||||
An agent JAR file may have both the <code>Premain-Class</code> and <code>Agent-Class</code>
|
||||
attributes present in the manifest. When the agent is started on the command-line using
|
||||
the <code>-javaagent</code> option then the <code>Premain-Class</code> attribute
|
||||
specifies the name of the agent class and the <code>Agent-Class</code> attribute is
|
||||
ignored. Similarly, if the agent is started sometime after the VM has started, then
|
||||
the <code>Agent-Class</code> attribute specifies the name of the agent class
|
||||
(the value of <code>Premain-Class</code> attribute is ignored).
|
||||
|
||||
|
||||
<h3>Instrumenting code in modules</h3>
|
||||
|
||||
As an aid to agents that deploy supporting classes on the search path of the
|
||||
bootstrap class loader, or the search path of the class loader that loads
|
||||
the main agent class, the Java virtual machine arranges for the module of
|
||||
transformed classes to read the unnamed module of both class loaders.
|
||||
|
||||
|
||||
<h2>Related Documentation</h2>
|
||||
|
||||
For tool documentation, please see:
|
||||
<ul>
|
||||
<li><a href="{@docRoot}/../technotes/tools/index.html">JDK Tools and Utilities</a>
|
||||
</ul>
|
||||
|
||||
@since 1.5
|
||||
@revised 1.6
|
||||
|
||||
</body>
|
||||
</html>
|
@ -34,6 +34,10 @@ import java.lang.module.ModuleDescriptor.Opens;
|
||||
import java.lang.module.ModuleDescriptor.Provides;
|
||||
import java.lang.module.ModuleDescriptor.Requires;
|
||||
import java.lang.module.ModuleDescriptor.Version;
|
||||
import java.lang.module.ModuleFinder;
|
||||
import java.lang.module.ModuleReader;
|
||||
import java.lang.module.ModuleReference;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.EnumSet;
|
||||
@ -41,13 +45,17 @@ import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.function.IntSupplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jdk.internal.module.Checks;
|
||||
import jdk.internal.module.ClassFileAttributes;
|
||||
import jdk.internal.module.ClassFileConstants;
|
||||
import jdk.internal.module.IllegalAccessMaps;
|
||||
import jdk.internal.module.ModuleHashes;
|
||||
import jdk.internal.module.ModuleInfo.Attributes;
|
||||
import jdk.internal.module.ModuleInfoExtender;
|
||||
@ -601,6 +609,10 @@ public final class SystemModulesPlugin implements Plugin {
|
||||
// generate SystemModules::moduleResolutions
|
||||
genModuleResolutionsMethod();
|
||||
|
||||
// generate SystemModules::concealedPackagesToOpen and
|
||||
// SystemModules::exportedPackagesToOpen
|
||||
genXXXPackagesToOpenMethods();
|
||||
|
||||
return cw;
|
||||
}
|
||||
|
||||
@ -733,6 +745,96 @@ public final class SystemModulesPlugin implements Plugin {
|
||||
mresmv.visitEnd();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate SystemModules::concealedPackagesToOpen and
|
||||
* SystemModules::exportedPackagesToOpen methods.
|
||||
*/
|
||||
private void genXXXPackagesToOpenMethods() {
|
||||
List<ModuleDescriptor> descriptors = moduleInfos.stream()
|
||||
.map(ModuleInfo::descriptor)
|
||||
.collect(Collectors.toList());
|
||||
ModuleFinder finder = finderOf(descriptors);
|
||||
IllegalAccessMaps maps = IllegalAccessMaps.generate(finder);
|
||||
generate("concealedPackagesToOpen", maps.concealedPackagesToOpen());
|
||||
generate("exportedPackagesToOpen", maps.exportedPackagesToOpen());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate SystemModules:XXXPackagesToOpen
|
||||
*/
|
||||
private void generate(String methodName, Map<String, Set<String>> map) {
|
||||
// Map<String, Set<String>> XXXPackagesToOpen()
|
||||
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC+ACC_STATIC,
|
||||
methodName,
|
||||
"()Ljava/util/Map;",
|
||||
"()Ljava/util/Map;",
|
||||
null);
|
||||
mv.visitCode();
|
||||
|
||||
// new Map$Entry[moduleCount]
|
||||
pushInt(mv, map.size());
|
||||
mv.visitTypeInsn(ANEWARRAY, "java/util/Map$Entry");
|
||||
|
||||
int index = 0;
|
||||
for (Map.Entry<String, Set<String>> e : map.entrySet()) {
|
||||
String moduleName = e.getKey();
|
||||
Set<String> packages = e.getValue();
|
||||
int packageCount = packages.size();
|
||||
|
||||
mv.visitInsn(DUP);
|
||||
pushInt(mv, index);
|
||||
mv.visitLdcInsn(moduleName);
|
||||
|
||||
// use Set.of(Object[]) when there are more than 2 packages
|
||||
// use Set.of(Object) or Set.of(Object, Object) when fewer packages
|
||||
if (packageCount > 2) {
|
||||
pushInt(mv, packageCount);
|
||||
mv.visitTypeInsn(ANEWARRAY, "java/lang/String");
|
||||
int i = 0;
|
||||
for (String pn : packages) {
|
||||
mv.visitInsn(DUP);
|
||||
pushInt(mv, i);
|
||||
mv.visitLdcInsn(pn);
|
||||
mv.visitInsn(AASTORE);
|
||||
i++;
|
||||
}
|
||||
mv.visitMethodInsn(INVOKESTATIC,
|
||||
"java/util/Set",
|
||||
"of",
|
||||
"([Ljava/lang/Object;)Ljava/util/Set;",
|
||||
true);
|
||||
} else {
|
||||
StringBuilder sb = new StringBuilder("(");
|
||||
for (String pn : packages) {
|
||||
mv.visitLdcInsn(pn);
|
||||
sb.append("Ljava/lang/Object;");
|
||||
}
|
||||
sb.append(")Ljava/util/Set;");
|
||||
mv.visitMethodInsn(INVOKESTATIC,
|
||||
"java/util/Set",
|
||||
"of",
|
||||
sb.toString(),
|
||||
true);
|
||||
}
|
||||
|
||||
String desc = "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/util/Map$Entry;";
|
||||
mv.visitMethodInsn(INVOKESTATIC,
|
||||
"java/util/Map",
|
||||
"entry",
|
||||
desc,
|
||||
true);
|
||||
mv.visitInsn(AASTORE);
|
||||
index++;
|
||||
}
|
||||
|
||||
// invoke Map.ofEntries(Map$Entry[])
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/util/Map", "ofEntries",
|
||||
"([Ljava/util/Map$Entry;)Ljava/util/Map;", true);
|
||||
mv.visitInsn(ARETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
public boolean isOverriddenClass(String path) {
|
||||
return path.equals("/java.base/" + CLASSNAME + ".class");
|
||||
}
|
||||
@ -1461,4 +1563,31 @@ public final class SystemModulesPlugin implements Plugin {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static ModuleFinder finderOf(Iterable<ModuleDescriptor> descriptors) {
|
||||
Map<String, ModuleReference> namesToReference = new HashMap<>();
|
||||
for (ModuleDescriptor descriptor : descriptors) {
|
||||
String name = descriptor.name();
|
||||
URI uri = URI.create("module:/" + name);
|
||||
ModuleReference mref = new ModuleReference(descriptor, uri) {
|
||||
@Override
|
||||
public ModuleReader open() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
namesToReference.putIfAbsent(name, mref);
|
||||
}
|
||||
|
||||
return new ModuleFinder() {
|
||||
@Override
|
||||
public Optional<ModuleReference> find(String name) {
|
||||
Objects.requireNonNull(name);
|
||||
return Optional.ofNullable(namesToReference.get(name));
|
||||
}
|
||||
@Override
|
||||
public Set<ModuleReference> findAll() {
|
||||
return new HashSet<>(namesToReference.values());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -295,6 +295,8 @@ sun/tools/jstat/jstatClassloadOutput1.sh 8173942 generic-
|
||||
|
||||
sun/jvmstat/monitor/MonitoredVm/MonitorVmStartTerminate.java 8057732 generic-all
|
||||
|
||||
com/sun/tools/attach/StartManagementAgent.java 8179700 generic-all
|
||||
|
||||
############################################################################
|
||||
|
||||
# jdk_other
|
||||
|
@ -1054,8 +1054,6 @@ public class BasicLayerTest {
|
||||
|
||||
/**
|
||||
* Attempt to create a layer with a module containing a "java" package.
|
||||
* This should only be allowed when the module is defined to the platform
|
||||
* class loader.
|
||||
*/
|
||||
@Test(dataProvider = "javaPackages")
|
||||
public void testLayerWithJavaPackage(String mn, String pn) {
|
||||
@ -1067,7 +1065,6 @@ public class BasicLayerTest {
|
||||
.resolve(finder, ModuleFinder.of(), Set.of(mn));
|
||||
assertTrue(cf.modules().size() == 1);
|
||||
|
||||
ClassLoader pcl = ClassLoader.getPlatformClassLoader();
|
||||
ClassLoader scl = ClassLoader.getSystemClassLoader();
|
||||
|
||||
try {
|
||||
@ -1084,15 +1081,6 @@ public class BasicLayerTest {
|
||||
ModuleLayer.boot().defineModulesWithManyLoaders(cf, scl);
|
||||
assertTrue(false);
|
||||
} catch (LayerInstantiationException e) { }
|
||||
|
||||
// create layer with module defined to platform class loader
|
||||
ModuleLayer layer = ModuleLayer.boot().defineModules(cf, _mn -> pcl);
|
||||
Optional<Module> om = layer.findModule(mn);
|
||||
assertTrue(om.isPresent());
|
||||
Module foo = om.get();
|
||||
assertTrue(foo.getClassLoader() == pcl);
|
||||
assertTrue(foo.getPackages().size() == 1);
|
||||
assertTrue(foo.getPackages().iterator().next().equals(pn));
|
||||
}
|
||||
|
||||
|
||||
@ -1101,8 +1089,7 @@ public class BasicLayerTest {
|
||||
*/
|
||||
@Test(expectedExceptions = { LayerInstantiationException.class })
|
||||
public void testLayerWithBootLoader() {
|
||||
ModuleDescriptor descriptor = newBuilder("m1")
|
||||
.build();
|
||||
ModuleDescriptor descriptor = newBuilder("m1").build();
|
||||
|
||||
ModuleFinder finder = ModuleUtils.finderOf(descriptor);
|
||||
|
||||
@ -1115,6 +1102,25 @@ public class BasicLayerTest {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Attempt to create a layer with a module defined to the platform loader
|
||||
*/
|
||||
@Test(expectedExceptions = { LayerInstantiationException.class })
|
||||
public void testLayerWithPlatformLoader() {
|
||||
ModuleDescriptor descriptor = newBuilder("m1").build();
|
||||
|
||||
ModuleFinder finder = ModuleUtils.finderOf(descriptor);
|
||||
|
||||
Configuration cf = ModuleLayer.boot()
|
||||
.configuration()
|
||||
.resolve(finder, ModuleFinder.of(), Set.of("m1"));
|
||||
assertTrue(cf.modules().size() == 1);
|
||||
|
||||
ClassLoader cl = ClassLoader.getPlatformClassLoader();
|
||||
ModuleLayer.boot().defineModules(cf, mn -> cl );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parent of configuration != configuration of parent layer
|
||||
*/
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -32,17 +32,21 @@
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.lang.module.Configuration;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.module.ModuleFinder;
|
||||
import java.lang.module.ModuleReference;
|
||||
import java.lang.module.ResolvedModule;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.ServiceLoader;
|
||||
@ -63,7 +67,6 @@ public class LayerAndLoadersTest {
|
||||
|
||||
@BeforeTest
|
||||
public void setup() throws Exception {
|
||||
|
||||
// javac -d mods --module-source-path src src/**
|
||||
assertTrue(CompilerUtils.compile(SRC_DIR, MODS_DIR,
|
||||
"--module-source-path", SRC_DIR.toString()));
|
||||
@ -77,7 +80,6 @@ public class LayerAndLoadersTest {
|
||||
* m1 requires m2 and m3
|
||||
*/
|
||||
public void testWithOneLoader() throws Exception {
|
||||
|
||||
Configuration cf = resolve("m1");
|
||||
|
||||
ClassLoader scl = ClassLoader.getSystemClassLoader();
|
||||
@ -95,7 +97,6 @@ public class LayerAndLoadersTest {
|
||||
assertTrue(cl3 == cl1);
|
||||
|
||||
invoke(layer, "m1", "p.Main");
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -106,7 +107,6 @@ public class LayerAndLoadersTest {
|
||||
* m1 requires m2 and m3
|
||||
*/
|
||||
public void testWithManyLoaders() throws Exception {
|
||||
|
||||
Configuration cf = resolve("m1");
|
||||
|
||||
ClassLoader scl = ClassLoader.getSystemClassLoader();
|
||||
@ -127,7 +127,6 @@ public class LayerAndLoadersTest {
|
||||
assertTrue(cl3 != cl2);
|
||||
|
||||
invoke(layer, "m1", "p.Main");
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -141,7 +140,6 @@ public class LayerAndLoadersTest {
|
||||
* m4 provides S with ...
|
||||
*/
|
||||
public void testServicesWithOneLoader() throws Exception {
|
||||
|
||||
Configuration cf = resolveAndBind("m1");
|
||||
|
||||
ClassLoader scl = ClassLoader.getSystemClassLoader();
|
||||
@ -168,7 +166,6 @@ public class LayerAndLoadersTest {
|
||||
assertTrue(serviceType.isInstance(provider));
|
||||
assertTrue(provider.getClass().getClassLoader() == cl1);
|
||||
assertFalse(iter.hasNext());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -182,7 +179,6 @@ public class LayerAndLoadersTest {
|
||||
* m4 provides S with ...
|
||||
*/
|
||||
public void testServicesWithManyLoaders() throws Exception {
|
||||
|
||||
Configuration cf = resolveAndBind("m1");
|
||||
|
||||
ClassLoader scl = ClassLoader.getSystemClassLoader();
|
||||
@ -220,7 +216,6 @@ public class LayerAndLoadersTest {
|
||||
assertTrue(provider.getClass().getClassLoader() == cl4);
|
||||
assertFalse(iter.hasNext());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -229,7 +224,6 @@ public class LayerAndLoadersTest {
|
||||
* to the given parent class loader.
|
||||
*/
|
||||
public void testDelegationToParent() throws Exception {
|
||||
|
||||
Configuration cf = resolve("m1");
|
||||
|
||||
ClassLoader parent = this.getClass().getClassLoader();
|
||||
@ -250,7 +244,6 @@ public class LayerAndLoadersTest {
|
||||
// many loader with boot loader as parent
|
||||
layer = ModuleLayer.boot().defineModulesWithManyLoaders(cf, null);
|
||||
testLoadFail(layer, cn);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -262,7 +255,6 @@ public class LayerAndLoadersTest {
|
||||
* m2 exports p
|
||||
*/
|
||||
public void testOverlappingPackages() {
|
||||
|
||||
ModuleDescriptor descriptor1
|
||||
= ModuleDescriptor.newModule("m1").exports("p").build();
|
||||
|
||||
@ -284,7 +276,6 @@ public class LayerAndLoadersTest {
|
||||
// should be okay to have one module per class loader
|
||||
ModuleLayer layer = ModuleLayer.boot().defineModulesWithManyLoaders(cf, null);
|
||||
checkLayer(layer, "m1", "m2");
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -296,7 +287,6 @@ public class LayerAndLoadersTest {
|
||||
* layer2: m3 reads m1, m4 reads m2
|
||||
*/
|
||||
public void testSplitDelegation() {
|
||||
|
||||
ModuleDescriptor descriptor1
|
||||
= ModuleDescriptor.newModule("m1").exports("p").build();
|
||||
|
||||
@ -332,7 +322,6 @@ public class LayerAndLoadersTest {
|
||||
// no split delegation when modules have their own class loader
|
||||
ModuleLayer layer2 = layer1.defineModulesWithManyLoaders(cf2, null);
|
||||
checkLayer(layer2, "m3", "m4");
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -345,7 +334,6 @@ public class LayerAndLoadersTest {
|
||||
* layer2: m1, m2, m4 => same loader
|
||||
*/
|
||||
public void testOverriding1() throws Exception {
|
||||
|
||||
Configuration cf1 = resolve("m1");
|
||||
|
||||
ModuleLayer layer1 = ModuleLayer.boot().defineModulesWithOneLoader(cf1, null);
|
||||
@ -381,7 +369,6 @@ public class LayerAndLoadersTest {
|
||||
assertTrue(loader4.loadClass("p.Main").getClassLoader() == loader4);
|
||||
assertTrue(loader4.loadClass("q.Hello").getClassLoader() == loader4);
|
||||
assertTrue(loader4.loadClass("w.Hello").getClassLoader() == loader4);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -394,7 +381,6 @@ public class LayerAndLoadersTest {
|
||||
* layer2: m1, m2, m3 => loader pool
|
||||
*/
|
||||
public void testOverriding2() throws Exception {
|
||||
|
||||
Configuration cf1 = resolve("m1");
|
||||
|
||||
ModuleLayer layer1 = ModuleLayer.boot().defineModulesWithManyLoaders(cf1, null);
|
||||
@ -476,7 +462,6 @@ public class LayerAndLoadersTest {
|
||||
loader6.loadClass("q.Hello");
|
||||
assertTrue(false);
|
||||
} catch (ClassNotFoundException expected) { }
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -488,7 +473,6 @@ public class LayerAndLoadersTest {
|
||||
* layer2: m1, m3 => same loader
|
||||
*/
|
||||
public void testOverriding3() throws Exception {
|
||||
|
||||
Configuration cf1 = resolve("m1");
|
||||
|
||||
ModuleLayer layer1 = ModuleLayer.boot().defineModulesWithOneLoader(cf1, null);
|
||||
@ -513,7 +497,6 @@ public class LayerAndLoadersTest {
|
||||
assertTrue(loader2.loadClass("p.Main").getClassLoader() == loader2);
|
||||
assertTrue(loader2.loadClass("q.Hello").getClassLoader() == loader1);
|
||||
assertTrue(loader2.loadClass("w.Hello").getClassLoader() == loader2);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -525,7 +508,6 @@ public class LayerAndLoadersTest {
|
||||
* layer2: m1, m3 => loader pool
|
||||
*/
|
||||
public void testOverriding4() throws Exception {
|
||||
|
||||
Configuration cf1 = resolve("m1");
|
||||
|
||||
ModuleLayer layer1 = ModuleLayer.boot().defineModulesWithManyLoaders(cf1, null);
|
||||
@ -565,49 +547,133 @@ public class LayerAndLoadersTest {
|
||||
assertTrue(loader4.loadClass("w.Hello").getClassLoader() == loader6);
|
||||
|
||||
assertTrue(loader6.loadClass("w.Hello").getClassLoader() == loader6);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Basic test of resource loading with a class loader created by
|
||||
* Layer.defineModulesWithOneLoader.
|
||||
* Basic test for locating resources with a class loader created by
|
||||
* defineModulesWithOneLoader.
|
||||
*/
|
||||
public void testResourcesOneLoader() throws Exception {
|
||||
public void testResourcesWithOneLoader() throws Exception {
|
||||
Configuration cf = resolve("m1");
|
||||
ClassLoader scl = ClassLoader.getSystemClassLoader();
|
||||
ModuleLayer layer = ModuleLayer.boot().defineModulesWithOneLoader(cf, scl);
|
||||
|
||||
ClassLoader loader = layer.findLoader("m1");
|
||||
testResourceLoading(loader, "p/Main.class");
|
||||
assertNotNull(loader);
|
||||
|
||||
// check that getResource and getResources are consistent
|
||||
URL url1 = loader.getResource("module-info.class");
|
||||
URL url2 = loader.getResources("module-info.class").nextElement();
|
||||
assertEquals(url1.toURI(), url2.toURI());
|
||||
|
||||
// use getResources to find module-info.class resources
|
||||
Enumeration<URL> urls = loader.getResources("module-info.class");
|
||||
List<String> list = readModuleNames(urls);
|
||||
|
||||
// m1, m2, ... should be first (order not specified)
|
||||
int count = cf.modules().size();
|
||||
cf.modules().stream()
|
||||
.map(ResolvedModule::name)
|
||||
.forEach(mn -> assertTrue(list.indexOf(mn) < count));
|
||||
|
||||
// java.base should be after m1, m2, ...
|
||||
assertTrue(list.indexOf("java.base") >= count);
|
||||
|
||||
// check resources(String)
|
||||
List<String> list2 = loader.resources("module-info.class")
|
||||
.map(this::readModuleName)
|
||||
.collect(Collectors.toList());
|
||||
assertEquals(list2, list);
|
||||
|
||||
// check nulls
|
||||
try {
|
||||
loader.getResource(null);
|
||||
assertTrue(false);
|
||||
} catch (NullPointerException e) { }
|
||||
try {
|
||||
loader.getResources(null);
|
||||
assertTrue(false);
|
||||
} catch (NullPointerException e) { }
|
||||
try {
|
||||
loader.resources(null);
|
||||
assertTrue(false);
|
||||
} catch (NullPointerException e) { }
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic test of resource loading with a class loader created by
|
||||
* Layer.defineModulesWithOneLoader.
|
||||
* Basic test for locating resources with class loaders created by
|
||||
* defineModulesWithManyLoaders.
|
||||
*/
|
||||
public void testResourcesManyLoaders() throws Exception {
|
||||
public void testResourcesWithManyLoaders() throws Exception {
|
||||
Configuration cf = resolve("m1");
|
||||
ClassLoader scl = ClassLoader.getSystemClassLoader();
|
||||
ModuleLayer layer = ModuleLayer.boot().defineModulesWithManyLoaders(cf, scl);
|
||||
ClassLoader loader = layer.findLoader("m1");
|
||||
testResourceLoading(loader, "p/Main.class");
|
||||
|
||||
for (Module m : layer.modules()) {
|
||||
String name = m.getName();
|
||||
ClassLoader loader = m.getClassLoader();
|
||||
assertNotNull(loader);
|
||||
|
||||
// getResource should find the module-info.class for the module
|
||||
URL url = loader.getResource("module-info.class");
|
||||
assertEquals(readModuleName(url), name);
|
||||
|
||||
// list of modules names read from module-info.class
|
||||
Enumeration<URL> urls = loader.getResources("module-info.class");
|
||||
List<String> list = readModuleNames(urls);
|
||||
|
||||
// module should be the first element
|
||||
assertTrue(list.indexOf(name) == 0);
|
||||
|
||||
// the module-info.class for the other modules in the layer
|
||||
// should not be found
|
||||
layer.modules().stream()
|
||||
.map(Module::getName)
|
||||
.filter(mn -> !mn.equals(name))
|
||||
.forEach(mn -> assertTrue(list.indexOf(mn) < 0));
|
||||
|
||||
// java.base cannot be the first element
|
||||
assertTrue(list.indexOf("java.base") > 0);
|
||||
|
||||
// check resources(String)
|
||||
List<String> list2 = loader.resources("module-info.class")
|
||||
.map(this::readModuleName)
|
||||
.collect(Collectors.toList());
|
||||
assertEquals(list2, list);
|
||||
|
||||
// check nulls
|
||||
try {
|
||||
loader.getResource(null);
|
||||
assertTrue(false);
|
||||
} catch (NullPointerException e) { }
|
||||
try {
|
||||
loader.getResources(null);
|
||||
assertTrue(false);
|
||||
} catch (NullPointerException e) { }
|
||||
try {
|
||||
loader.resources(null);
|
||||
assertTrue(false);
|
||||
} catch (NullPointerException e) { }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that a resource is located by a class loader.
|
||||
*/
|
||||
private void testResourceLoading(ClassLoader loader, String name)
|
||||
throws IOException
|
||||
{
|
||||
URL url = loader.getResource(name);
|
||||
assertNotNull(url);
|
||||
|
||||
try (InputStream in = loader.getResourceAsStream(name)) {
|
||||
assertNotNull(in);
|
||||
private List<String> readModuleNames(Enumeration<URL> e) {
|
||||
List<String> list = new ArrayList<>();
|
||||
while (e.hasMoreElements()) {
|
||||
URL url = e.nextElement();
|
||||
list.add(readModuleName(url));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
Enumeration<URL> urls = loader.getResources(name);
|
||||
assertTrue(urls.hasMoreElements());
|
||||
private String readModuleName(URL url) {
|
||||
try (InputStream in = url.openStream()) {
|
||||
ModuleDescriptor descriptor = ModuleDescriptor.read(in);
|
||||
return descriptor.name();
|
||||
} catch (IOException ioe) {
|
||||
throw new UncheckedIOException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -59,11 +59,17 @@ public class LayerControllerTest {
|
||||
.packages(Set.of("p2"))
|
||||
.build();
|
||||
|
||||
ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2);
|
||||
ModuleDescriptor descriptor3
|
||||
= ModuleDescriptor.newModule("m3")
|
||||
.requires("java.base")
|
||||
.packages(Set.of("p3"))
|
||||
.build();
|
||||
|
||||
ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2, descriptor3);
|
||||
ModuleLayer bootLayer = ModuleLayer.boot();
|
||||
|
||||
Configuration cf = bootLayer.configuration()
|
||||
.resolve(finder, ModuleFinder.of(), Set.of("m1", "m2"));
|
||||
.resolve(finder, ModuleFinder.of(), Set.of("m1", "m2", "m3"));
|
||||
|
||||
ClassLoader scl = ClassLoader.getSystemClassLoader();
|
||||
|
||||
@ -72,9 +78,10 @@ public class LayerControllerTest {
|
||||
|
||||
ModuleLayer layer = controller.layer();
|
||||
|
||||
assertTrue(layer.modules().size() == 2);
|
||||
assertTrue(layer.modules().size() == 3);
|
||||
assertTrue(layer.findModule("m1").isPresent());
|
||||
assertTrue(layer.findModule("m2").isPresent());
|
||||
assertTrue(layer.findModule("m3").isPresent());
|
||||
|
||||
return controller;
|
||||
}
|
||||
@ -88,18 +95,34 @@ public class LayerControllerTest {
|
||||
ModuleLayer layer = controller.layer();
|
||||
Module m1 = layer.findModule("m1").orElseThrow(RuntimeException::new);
|
||||
Module m2 = layer.findModule("m2").orElseThrow(RuntimeException::new);
|
||||
Module m3 = layer.findModule("m3").orElseThrow(RuntimeException::new);
|
||||
|
||||
assertFalse(m1.canRead(m2));
|
||||
assertFalse(m1.canRead(m3));
|
||||
assertFalse(m1.isExported("p1"));
|
||||
assertFalse(m1.isOpen("p1"));
|
||||
assertFalse(m1.isExported("p1", m2));
|
||||
assertFalse(m1.isExported("p1", m3));
|
||||
assertFalse(m1.isOpen("p1", m2));
|
||||
assertFalse(m1.isOpen("p1", m3));
|
||||
|
||||
assertFalse(m2.canRead(m1));
|
||||
assertFalse(m2.canRead(m3));
|
||||
assertFalse(m2.isExported("p2"));
|
||||
assertFalse(m2.isOpen("p2"));
|
||||
assertFalse(m2.isExported("p2", m1));
|
||||
assertFalse(m2.isExported("p2", m3));
|
||||
assertFalse(m2.isOpen("p2", m1));
|
||||
assertFalse(m2.isOpen("p2", m3));
|
||||
|
||||
assertFalse(m3.canRead(m1));
|
||||
assertFalse(m3.canRead(m2));
|
||||
assertFalse(m3.isExported("p3"));
|
||||
assertFalse(m3.isOpen("p3"));
|
||||
assertFalse(m3.isExported("p3", m1));
|
||||
assertFalse(m3.isExported("p3", m2));
|
||||
assertFalse(m3.isOpen("p3", m1));
|
||||
assertFalse(m3.isOpen("p3", m2));
|
||||
|
||||
// update m1 to read m2
|
||||
assertTrue(controller.addReads(m1, m2) == controller);
|
||||
@ -111,19 +134,33 @@ public class LayerControllerTest {
|
||||
assertTrue(m1.canRead(m2));
|
||||
assertTrue(m1.canRead(m1));
|
||||
|
||||
// update m1 to open p1 to m2
|
||||
assertTrue(controller.addOpens(m1, "p1", m2) == controller);
|
||||
// update m1 to export p1 to m2
|
||||
assertTrue(controller.addExports(m1, "p1", m2) == controller);
|
||||
assertTrue(m1.isExported("p1", m2));
|
||||
assertTrue(m1.isOpen("p1", m2));
|
||||
assertFalse(m1.isOpen("p1", m2));
|
||||
assertFalse(m1.isExported("p1"));
|
||||
assertFalse(m1.isOpen("p1"));
|
||||
|
||||
// update m2 to open p2 to m1
|
||||
assertTrue(controller.addOpens(m2, "p2", m1) == controller);
|
||||
assertTrue(m2.isExported("p2", m1));
|
||||
assertTrue(m2.isOpen("p2", m1));
|
||||
assertFalse(m2.isExported("p2"));
|
||||
assertFalse(m2.isOpen("p2"));
|
||||
// update m3 to open p3 to m1
|
||||
assertTrue(controller.addExports(m3, "p3", m1) == controller);
|
||||
assertTrue(m3.isExported("p3", m1));
|
||||
assertFalse(m3.isOpen("p3", m1));
|
||||
assertFalse(m3.isExported("p3"));
|
||||
assertFalse(m3.isOpen("p3"));
|
||||
|
||||
// update m1 to open p1 to m3
|
||||
assertTrue(controller.addOpens(m1, "p1", m3) == controller);
|
||||
assertTrue(m1.isExported("p1", m3));
|
||||
assertTrue(m1.isOpen("p1", m3));
|
||||
assertFalse(m1.isExported("p1"));
|
||||
assertFalse(m1.isOpen("p1"));
|
||||
|
||||
// update m3 to open p3 to m1
|
||||
assertTrue(controller.addOpens(m3, "p3", m1) == controller);
|
||||
assertTrue(m3.isExported("p3", m1));
|
||||
assertTrue(m3.isOpen("p3", m1));
|
||||
assertFalse(m3.isExported("p3"));
|
||||
assertFalse(m3.isOpen("p3"));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -142,6 +179,18 @@ public class LayerControllerTest {
|
||||
assertTrue(false);
|
||||
} catch (IllegalArgumentException expected) { }
|
||||
|
||||
// java.base is not in layer
|
||||
try {
|
||||
controller.addExports(base, "java.lang", m2);
|
||||
assertTrue(false);
|
||||
} catch (IllegalArgumentException expected) { }
|
||||
|
||||
// m1 does not contain java.lang
|
||||
try {
|
||||
controller.addExports(m1, "java.lang", m2);
|
||||
assertTrue(false);
|
||||
} catch (IllegalArgumentException expected) { }
|
||||
|
||||
// java.base is not in layer
|
||||
try {
|
||||
controller.addOpens(base, "java.lang", m2);
|
||||
@ -176,6 +225,21 @@ public class LayerControllerTest {
|
||||
assertTrue(false);
|
||||
} catch (NullPointerException expected) { }
|
||||
|
||||
try {
|
||||
controller.addExports(null, "p1", m2);
|
||||
assertTrue(false);
|
||||
} catch (NullPointerException expected) { }
|
||||
|
||||
try {
|
||||
controller.addExports(m1, null, m2);
|
||||
assertTrue(false);
|
||||
} catch (NullPointerException expected) { }
|
||||
|
||||
try {
|
||||
controller.addExports(m1, "p1", null);
|
||||
assertTrue(false);
|
||||
} catch (NullPointerException expected) { }
|
||||
|
||||
try {
|
||||
controller.addOpens(null, "p1", m2);
|
||||
assertTrue(false);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -62,6 +62,7 @@ public class AnnotationsTest {
|
||||
public void testUnnamedModule() {
|
||||
Module module = this.getClass().getModule();
|
||||
assertTrue(module.getAnnotations().length == 0);
|
||||
assertTrue(module.getDeclaredAnnotations().length == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -88,6 +89,7 @@ public class AnnotationsTest {
|
||||
Annotation[] a = module.getAnnotations();
|
||||
assertTrue(a.length == 1);
|
||||
assertTrue(a[0] instanceof Deprecated);
|
||||
assertEquals(module.getDeclaredAnnotations(), a);
|
||||
}
|
||||
|
||||
|
||||
|
@ -35,7 +35,7 @@ import static org.testng.Assert.*;
|
||||
* @test
|
||||
* @summary Basic test of java.lang.Module
|
||||
* @modules java.desktop java.xml
|
||||
* @run testng BasicModuleTest
|
||||
* @run testng/othervm --illegal-access=deny BasicModuleTest
|
||||
*/
|
||||
|
||||
public class BasicModuleTest {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -29,7 +29,7 @@
|
||||
* @summary Basic test for annotations on modules
|
||||
*/
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
import p.annotation.Foo;
|
||||
import p.annotation.Bar;
|
||||
@ -71,6 +71,28 @@ public class Basic {
|
||||
Baz baz = module.getAnnotation(Baz.class);
|
||||
assertNotNull(baz);
|
||||
String[] expected = { "one", "two", "three" };
|
||||
assertTrue(Arrays.equals(baz.value(), expected));
|
||||
assertEquals(baz.value(), expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test annotations with RUNTIME retention policy
|
||||
*/
|
||||
@Test
|
||||
public void testRuntimeAnnotations() {
|
||||
Annotation[] a = module.getAnnotations();
|
||||
assertEquals(a, module.getDeclaredAnnotations());
|
||||
assertTrue(a.length == 2);
|
||||
Bar bar;
|
||||
Baz baz;
|
||||
if (a[0] instanceof Bar) {
|
||||
bar = (Bar)a[0];
|
||||
baz = (Baz)a[1];
|
||||
} else {
|
||||
bar = (Bar)a[1];
|
||||
baz = (Baz)a[0];
|
||||
}
|
||||
assertEquals(bar.value(), "bar");
|
||||
String[] expected = { "one", "two", "three"};
|
||||
assertEquals(baz.value(), expected);
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@
|
||||
* java.base/jdk.internal.test.TestProviderImpl1
|
||||
* java.base/jdk.internal.test.TestProviderImpl2
|
||||
* @run shell MakeJAR3.sh RedefineModuleAgent
|
||||
* @run testng/othervm -javaagent:RedefineModuleAgent.jar RedefineModuleTest
|
||||
* @run testng/othervm --illegal-access=deny -javaagent:RedefineModuleAgent.jar RedefineModuleTest
|
||||
*/
|
||||
|
||||
import java.lang.TestProvider;
|
||||
|
@ -58,8 +58,8 @@ public class AutomaticModulesTest {
|
||||
private static final Path USER_DIR
|
||||
= Paths.get(System.getProperty("user.dir"));
|
||||
|
||||
@DataProvider(name = "names")
|
||||
public Object[][] createNames() {
|
||||
@DataProvider(name = "jarnames")
|
||||
public Object[][] createJarNames() {
|
||||
return new Object[][] {
|
||||
|
||||
// JAR file name module-name[/version]
|
||||
@ -100,7 +100,7 @@ public class AutomaticModulesTest {
|
||||
}
|
||||
|
||||
// JAR file names that do not map to a legal module name
|
||||
@DataProvider(name = "badnames")
|
||||
@DataProvider(name = "badjarnames")
|
||||
public Object[][] createBadNames() {
|
||||
return new Object[][]{
|
||||
|
||||
@ -117,7 +117,7 @@ public class AutomaticModulesTest {
|
||||
/**
|
||||
* Test mapping of JAR file names to module names
|
||||
*/
|
||||
@Test(dataProvider = "names")
|
||||
@Test(dataProvider = "jarnames")
|
||||
public void testNames(String fn, String mid) throws IOException {
|
||||
String[] s = mid.split("/");
|
||||
String mn = s[0];
|
||||
@ -146,11 +146,10 @@ public class AutomaticModulesTest {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test impossible mapping of JAR files to modules names
|
||||
*/
|
||||
@Test(dataProvider = "badnames", expectedExceptions = FindException.class)
|
||||
@Test(dataProvider = "badjarnames", expectedExceptions = FindException.class)
|
||||
public void testBadNames(String fn, String ignore) throws IOException {
|
||||
Path dir = Files.createTempDirectory(USER_DIR, "mods");
|
||||
Path jf = dir.resolve(fn);
|
||||
@ -163,6 +162,76 @@ public class AutomaticModulesTest {
|
||||
}
|
||||
|
||||
|
||||
@DataProvider(name = "modulenames")
|
||||
public Object[][] createModuleNames() {
|
||||
return new Object[][] {
|
||||
{ "foo", null },
|
||||
{ "foo", "1.0" },
|
||||
{ "foo.bar", null },
|
||||
{ "foo.bar", "1.0" },
|
||||
{ "class_", null },
|
||||
{ "class_", "1.0" },
|
||||
};
|
||||
}
|
||||
|
||||
@DataProvider(name = "badmodulenames")
|
||||
public Object[][] createBadModuleNames() {
|
||||
return new Object[][] {
|
||||
{ "", null },
|
||||
{ "", "1.0" },
|
||||
{ "666", null },
|
||||
{ "666", "1.0" },
|
||||
{ "foo.class", null },
|
||||
{ "foo.class", "1.0" },
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Test JAR files with the Automatic-Module-Name attribute
|
||||
*/
|
||||
@Test(dataProvider = "modulenames")
|
||||
public void testAutomaticModuleNameAttribute(String name, String vs)
|
||||
throws IOException
|
||||
{
|
||||
Manifest man = new Manifest();
|
||||
Attributes attrs = man.getMainAttributes();
|
||||
attrs.put(Attributes.Name.MANIFEST_VERSION, "1.0.0");
|
||||
attrs.put(new Attributes.Name("Automatic-Module-Name"), name);
|
||||
|
||||
Path dir = Files.createTempDirectory(USER_DIR, "mods");
|
||||
String jar;
|
||||
if (vs == null) {
|
||||
jar = "m.jar";
|
||||
} else {
|
||||
jar = "m-" + vs + ".jar";
|
||||
}
|
||||
createDummyJarFile(dir.resolve(jar), man);
|
||||
|
||||
ModuleFinder finder = ModuleFinder.of(dir);
|
||||
|
||||
assertTrue(finder.findAll().size() == 1);
|
||||
assertTrue(finder.find(name).isPresent());
|
||||
|
||||
ModuleReference mref = finder.find(name).get();
|
||||
ModuleDescriptor descriptor = mref.descriptor();
|
||||
assertEquals(descriptor.name(), name);
|
||||
assertEquals(descriptor.version()
|
||||
.map(ModuleDescriptor.Version::toString)
|
||||
.orElse(null), vs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test JAR files with the Automatic-Module-Name attribute with a value
|
||||
* that is not a legal module name.
|
||||
*/
|
||||
@Test(dataProvider = "badmodulenames", expectedExceptions = FindException.class)
|
||||
public void testBadAutomaticModuleNameAttribute(String name, String ignore)
|
||||
throws IOException
|
||||
{
|
||||
// should throw FindException
|
||||
testAutomaticModuleNameAttribute(name, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test all packages are exported
|
||||
*/
|
||||
@ -277,7 +346,6 @@ public class AutomaticModulesTest {
|
||||
assertTrue(provides.providers().contains((provider)));
|
||||
}
|
||||
|
||||
|
||||
// META-INF/services files that don't map to legal service names
|
||||
@DataProvider(name = "badservices")
|
||||
public Object[][] createBadServices() {
|
||||
@ -310,7 +378,6 @@ public class AutomaticModulesTest {
|
||||
assertTrue(descriptor.provides().isEmpty());
|
||||
}
|
||||
|
||||
|
||||
// META-INF/services configuration file entries that are not legal
|
||||
@DataProvider(name = "badproviders")
|
||||
public Object[][] createBadProviders() {
|
||||
@ -370,7 +437,6 @@ public class AutomaticModulesTest {
|
||||
ModuleFinder.of(dir).findAll();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test that a JAR file with a Main-Class attribute results
|
||||
* in a module with a main class.
|
||||
@ -398,7 +464,6 @@ public class AutomaticModulesTest {
|
||||
assertEquals(descriptor.mainClass().get(), mainClass);
|
||||
}
|
||||
|
||||
|
||||
// Main-Class files that do not map to a legal qualified type name
|
||||
@DataProvider(name = "badmainclass")
|
||||
public Object[][] createBadMainClass() {
|
||||
@ -450,7 +515,6 @@ public class AutomaticModulesTest {
|
||||
assertFalse(descriptor.mainClass().isPresent());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Basic test of a configuration created with automatic modules.
|
||||
* a requires b*
|
||||
@ -583,7 +647,6 @@ public class AutomaticModulesTest {
|
||||
testReadAllBootModules(cf, "d"); // d reads all modules in boot layer
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Basic test of a configuration created with automatic modules
|
||||
* a requires b
|
||||
@ -662,7 +725,6 @@ public class AutomaticModulesTest {
|
||||
testReadAllBootModules(cf, "d"); // d reads all modules in boot layer
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Basic test to ensure that no automatic modules are resolved when
|
||||
* an automatic module is not a root or required by other modules.
|
||||
@ -692,7 +754,6 @@ public class AutomaticModulesTest {
|
||||
assertTrue(cf.findModule("m1").isPresent());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Basic test to ensure that if an automatic module is resolved then
|
||||
* all observable automatic modules are resolved.
|
||||
@ -770,7 +831,6 @@ public class AutomaticModulesTest {
|
||||
assertTrue(auto3.reads().contains(base));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Basic test of automatic modules in a child configuration. All automatic
|
||||
* modules that are found with the before finder should be resolved. The
|
||||
@ -845,7 +905,6 @@ public class AutomaticModulesTest {
|
||||
assertTrue(auto3.reads().contains(base));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Basic test of a configuration created with automatic modules
|
||||
* a requires b* and c*
|
||||
@ -874,7 +933,6 @@ public class AutomaticModulesTest {
|
||||
resolve(parent, finder, "a");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Basic test of a configuration created with automatic modules
|
||||
* a contains p, requires b*
|
||||
@ -901,7 +959,6 @@ public class AutomaticModulesTest {
|
||||
resolve(parent, finder, "a");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Basic test of layer containing automatic modules
|
||||
*/
|
||||
@ -943,7 +1000,6 @@ public class AutomaticModulesTest {
|
||||
testsReadsAll(c, layer);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test miscellaneous methods.
|
||||
*/
|
||||
@ -961,7 +1017,6 @@ public class AutomaticModulesTest {
|
||||
assertFalse(m.modifiers().contains(ModuleDescriptor.Modifier.SYNTHETIC));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Invokes parent.resolve to resolve the given root modules.
|
||||
*/
|
||||
@ -1055,7 +1110,7 @@ public class AutomaticModulesTest {
|
||||
* in the resulting JAR file.
|
||||
*/
|
||||
static Path createDummyJarFile(Path jarfile, String... entries)
|
||||
throws IOException
|
||||
throws IOException
|
||||
{
|
||||
return createDummyJarFile(jarfile, null, entries);
|
||||
}
|
||||
|
@ -25,7 +25,7 @@
|
||||
* @test
|
||||
* @build CanAccessTest
|
||||
* @modules java.base/jdk.internal.misc:+open
|
||||
* @run testng CanAccessTest
|
||||
* @run testng/othervm --illegal-access=deny CanAccessTest
|
||||
* @summary Test AccessibleObject::canAccess method
|
||||
*/
|
||||
|
||||
|
@ -26,7 +26,7 @@
|
||||
* @build ModuleSetAccessibleTest
|
||||
* @modules java.base/java.lang:open
|
||||
* java.base/jdk.internal.misc:+open
|
||||
* @run testng ModuleSetAccessibleTest
|
||||
* @run testng/othervm --illegal-access=deny ModuleSetAccessibleTest
|
||||
* @summary Test java.lang.reflect.AccessibleObject with modules
|
||||
*/
|
||||
|
||||
|
@ -27,7 +27,7 @@
|
||||
* @modules java.base/java.lang:open
|
||||
* java.base/jdk.internal.perf
|
||||
* java.base/jdk.internal.misc:+open
|
||||
* @run testng TrySetAccessibleTest
|
||||
* @run testng/othervm --illegal-access=deny TrySetAccessibleTest
|
||||
* @summary Test AccessibleObject::trySetAccessible method
|
||||
*/
|
||||
|
||||
|
@ -89,6 +89,7 @@ public class CacheTest {
|
||||
assertTrue(executeTestJava("--class-path", MODS_DIR.resolve(TEST_MODULE).toString(),
|
||||
"--module-path", MODS_DIR.resolve(MAIN_BUNDLES_MODULE).toString(),
|
||||
"--add-modules", MAIN_BUNDLES_MODULE,
|
||||
"--illegal-access=deny",
|
||||
MAIN_CLASS, "cache")
|
||||
.outputTo(System.out)
|
||||
.errorTo(System.out)
|
||||
@ -111,6 +112,7 @@ public class CacheTest {
|
||||
assertTrue(executeTestJava("--class-path", MODS_DIR.resolve(TEST_MODULE).toString(),
|
||||
"--module-path", MODS_DIR.resolve(MAIN_BUNDLES_MODULE).toString(),
|
||||
"--add-modules", MAIN_BUNDLES_MODULE,
|
||||
"--illegal-access=deny",
|
||||
MAIN_CLASS)
|
||||
.outputTo(System.out)
|
||||
.errorTo(System.out)
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -24,7 +24,8 @@
|
||||
/**
|
||||
* @test
|
||||
* @library /lib/testlibrary
|
||||
* @modules jdk.compiler
|
||||
* @modules java.base/jdk.internal.org.objectweb.asm
|
||||
* jdk.compiler
|
||||
* @build CompilerUtils
|
||||
* @run testng/othervm BadProvidersTest
|
||||
* @summary Basic test of ServiceLoader with bad provider and bad provider
|
||||
@ -44,6 +45,10 @@ import java.util.ServiceLoader.Provider;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import static org.testng.Assert.*;
|
||||
@ -191,5 +196,68 @@ public class BadProvidersTest {
|
||||
loadProviders(mods, TEST2_MODULE).forEach(Provider::get);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test a service provider that defines more than one no-args
|
||||
* public static "provider" method.
|
||||
*/
|
||||
@Test(expectedExceptions = ServiceConfigurationError.class)
|
||||
public void testWithTwoFactoryMethods() throws Exception {
|
||||
Path mods = compileTest(TEST1_MODULE);
|
||||
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
|
||||
+ ClassWriter.COMPUTE_FRAMES);
|
||||
cw.visit(V1_9,
|
||||
ACC_PUBLIC + ACC_SUPER,
|
||||
"p/ProviderFactory",
|
||||
null,
|
||||
"java/lang/Object",
|
||||
null);
|
||||
|
||||
// public static p.Service provider()
|
||||
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC,
|
||||
"provider",
|
||||
"()Lp/Service;",
|
||||
null,
|
||||
null);
|
||||
mv.visitTypeInsn(NEW, "p/ProviderFactory$1");
|
||||
mv.visitInsn(DUP);
|
||||
mv.visitMethodInsn(INVOKESPECIAL,
|
||||
"p/ProviderFactory$1",
|
||||
"<init>", "()V",
|
||||
false);
|
||||
mv.visitInsn(ARETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
|
||||
// public static p.ProviderFactory$1 provider()
|
||||
mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC,
|
||||
"provider",
|
||||
"()Lp/ProviderFactory$1;",
|
||||
null,
|
||||
null);
|
||||
mv.visitTypeInsn(NEW, "p/ProviderFactory$1");
|
||||
mv.visitInsn(DUP);
|
||||
mv.visitMethodInsn(INVOKESPECIAL,
|
||||
"p/ProviderFactory$1",
|
||||
"<init>",
|
||||
"()V",
|
||||
false);
|
||||
mv.visitInsn(ARETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
|
||||
cw.visitEnd();
|
||||
|
||||
// write the class bytes into the compiled module directory
|
||||
Path classFile = mods.resolve(TEST1_MODULE)
|
||||
.resolve("p")
|
||||
.resolve("ProviderFactory.class");
|
||||
Files.write(classFile, cw.toByteArray());
|
||||
|
||||
// load providers and instantiate each one
|
||||
loadProviders(mods, TEST1_MODULE).forEach(Provider::get);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -23,12 +23,13 @@
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @library modules
|
||||
* @modules java.scripting
|
||||
* @library modules /lib/testlibrary
|
||||
* @build bananascript/*
|
||||
* @compile src/pearscript/org/pear/PearScriptEngineFactory.java
|
||||
* src/pearscript/org/pear/PearScript.java
|
||||
* @run testng/othervm Basic
|
||||
* @build JarUtils
|
||||
* @compile classpath/pearscript/org/pear/PearScriptEngineFactory.java
|
||||
* classpath/pearscript/org/pear/PearScript.java
|
||||
* @run testng/othervm ModulesTest
|
||||
* @summary Basic test for ServiceLoader with a provider deployed as a module.
|
||||
*/
|
||||
|
||||
@ -38,8 +39,15 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.ServiceLoader.Provider;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.script.ScriptEngineFactory;
|
||||
|
||||
@ -56,16 +64,16 @@ import static org.testng.Assert.*;
|
||||
* with a service configuration file.
|
||||
*/
|
||||
|
||||
public class Basic {
|
||||
public class ModulesTest {
|
||||
|
||||
// Copy the services configuration file for "pearscript" into place.
|
||||
@BeforeTest
|
||||
public void setup() throws Exception {
|
||||
Path src = Paths.get(System.getProperty("test.src", ""));
|
||||
Path classes = Paths.get(System.getProperty("test.classes", ""));
|
||||
Path src = Paths.get(System.getProperty("test.src"));
|
||||
Path classes = Paths.get(System.getProperty("test.classes"));
|
||||
String st = ScriptEngineFactory.class.getName();
|
||||
Path config = Paths.get("META-INF", "services", st);
|
||||
Path source = src.resolve("src").resolve("pearscript").resolve(config);
|
||||
Path source = src.resolve("classpath").resolve("pearscript").resolve(config);
|
||||
Path target = classes.resolve(config);
|
||||
Files.createDirectories(target.getParent());
|
||||
Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
|
||||
@ -204,7 +212,7 @@ public class Basic {
|
||||
|
||||
// iterator
|
||||
ServiceLoader<ScriptEngineFactory> loader
|
||||
= ServiceLoader.load(ScriptEngineFactory.class, pcl);
|
||||
= ServiceLoader.load(ScriptEngineFactory.class, pcl);
|
||||
Set<String> names = collectAll(loader)
|
||||
.stream()
|
||||
.map(ScriptEngineFactory::getEngineName)
|
||||
@ -222,6 +230,44 @@ public class Basic {
|
||||
assertFalse(names.contains("PearScriptEngine"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic test of ServiceLoader.load where the service provider module is an
|
||||
* automatic module.
|
||||
*/
|
||||
@Test
|
||||
public void testWithAutomaticModule() throws Exception {
|
||||
Path classes = Paths.get(System.getProperty("test.classes"));
|
||||
Path jar = Files.createTempDirectory("lib").resolve("pearscript.jar");
|
||||
JarUtils.createJarFile(jar, classes, "META-INF", "org");
|
||||
|
||||
ModuleFinder finder = ModuleFinder.of(jar);
|
||||
ModuleLayer bootLayer = ModuleLayer.boot();
|
||||
Configuration parent = bootLayer.configuration();
|
||||
Configuration cf = parent.resolveAndBind(finder, ModuleFinder.of(), Set.of());
|
||||
assertTrue(cf.modules().size() == 1);
|
||||
|
||||
ClassLoader scl = ClassLoader.getSystemClassLoader();
|
||||
ModuleLayer layer = bootLayer.defineModulesWithOneLoader(cf, scl);
|
||||
assertTrue(layer.modules().size() == 1);
|
||||
|
||||
ClassLoader loader = layer.findLoader("pearscript");
|
||||
ScriptEngineFactory factory;
|
||||
|
||||
// load using the class loader as context
|
||||
factory = ServiceLoader.load(ScriptEngineFactory.class, loader)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
assertNotNull(factory);
|
||||
assertTrue(factory.getClass().getClassLoader() == loader);
|
||||
|
||||
// load using the layer as context
|
||||
factory = ServiceLoader.load(layer, ScriptEngineFactory.class)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
assertNotNull(factory);
|
||||
assertTrue(factory.getClass().getClassLoader() == loader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic test of ServiceLoader.load, using the class loader for
|
||||
* a module in a custom layer as the context.
|
151
jdk/test/java/util/ServiceLoader/NoInterferenceTest.java
Normal file
151
jdk/test/java/util/ServiceLoader/NoInterferenceTest.java
Normal file
@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @library /lib/testlibrary
|
||||
* @modules jdk.compiler
|
||||
* @build CompilerUtils
|
||||
* @run testng NoInterferenceTest
|
||||
* @summary Basic test of ServiceLoader that ensures there is no interference
|
||||
* when there are two service interfaces of the same name in a layer
|
||||
* or overridden in a child layer.
|
||||
*/
|
||||
|
||||
import java.lang.module.Configuration;
|
||||
import java.lang.module.ModuleFinder;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.Set;
|
||||
|
||||
import org.testng.annotations.BeforeTest;
|
||||
import org.testng.annotations.Test;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
public class NoInterferenceTest {
|
||||
|
||||
private static final String TEST_SRC = System.getProperty("test.src");
|
||||
private static final Path SRC_DIR = Paths.get(TEST_SRC, "modules");
|
||||
private static final Path MODS_DIR = Paths.get("mods");
|
||||
private static final List<String> MODULES = Arrays.asList("s1", "p1", "s2", "p2");
|
||||
|
||||
@BeforeTest
|
||||
void compile() throws Exception {
|
||||
Files.createDirectory(MODS_DIR);
|
||||
for (String name : MODULES) {
|
||||
Path src = SRC_DIR.resolve(name);
|
||||
Path output = Files.createDirectory(MODS_DIR.resolve(name));
|
||||
assertTrue(CompilerUtils.compile(src, output, "-p", MODS_DIR.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() throws Exception {
|
||||
ModuleFinder empty = ModuleFinder.of();
|
||||
ModuleFinder finder = ModuleFinder.of(MODS_DIR);
|
||||
|
||||
ModuleLayer bootLayer = ModuleLayer.boot();
|
||||
|
||||
Configuration cf0 = bootLayer.configuration();
|
||||
Configuration cf1 = cf0.resolveAndBind(finder, empty, Set.of("s1", "s2"));
|
||||
Configuration cf2 = cf1.resolveAndBind(finder, empty, Set.of("s1", "s2"));
|
||||
|
||||
// cf1 contains s1, p1, s2, p2
|
||||
assertTrue(cf1.modules().size() == 4);
|
||||
|
||||
// cf1 contains s1, p1, s2, p2
|
||||
assertTrue(cf2.modules().size() == 4);
|
||||
|
||||
ClassLoader scl = ClassLoader.getSystemClassLoader();
|
||||
|
||||
ModuleLayer layer1 = bootLayer.defineModulesWithManyLoaders(cf1, scl);
|
||||
testLayer(layer1);
|
||||
|
||||
ModuleLayer layer2 = layer1.defineModulesWithManyLoaders(cf2, scl);
|
||||
testLayer(layer2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the layer contains s1, p1, s2, and p2.
|
||||
*
|
||||
* Tests loading instances of s1/p.S and s2/p.S.
|
||||
*/
|
||||
private void testLayer(ModuleLayer layer) throws Exception {
|
||||
assertTrue(layer.modules().size() == 4);
|
||||
Module s1 = layer.findModule("s1").get();
|
||||
Module p1 = layer.findModule("p1").get();
|
||||
Module s2 = layer.findModule("s2").get();
|
||||
Module p2 = layer.findModule("p2").get();
|
||||
|
||||
// p1 reads s1
|
||||
assertTrue(p1.canRead(s1));
|
||||
assertFalse(p1.canRead(s2));
|
||||
|
||||
// p2 reads s2
|
||||
assertTrue(p2.canRead(s2));
|
||||
assertFalse(p2.canRead(s1));
|
||||
|
||||
// iterate over implementations of s1/p.S
|
||||
{
|
||||
ClassLoader loader = layer.findLoader("s1");
|
||||
Class<?> service = loader.loadClass("p.S");
|
||||
|
||||
List<?> list = collectAll(ServiceLoader.load(service, loader));
|
||||
assertTrue(list.size() == 1);
|
||||
assertTrue(list.get(0).getClass().getModule() == p1);
|
||||
|
||||
list = collectAll(ServiceLoader.load(layer, service));
|
||||
assertTrue(list.size() == 1);
|
||||
assertTrue(list.get(0).getClass().getModule() == p1);
|
||||
}
|
||||
|
||||
// iterate over implementations of s2/p.S
|
||||
{
|
||||
ClassLoader loader = layer.findLoader("s2");
|
||||
Class<?> service = loader.loadClass("p.S");
|
||||
|
||||
List<?> list = collectAll(ServiceLoader.load(service, loader));
|
||||
assertTrue(list.size() == 1);
|
||||
assertTrue(list.get(0).getClass().getModule() == p2);
|
||||
|
||||
list = collectAll(ServiceLoader.load(layer, service));
|
||||
assertTrue(list.size() == 1);
|
||||
assertTrue(list.get(0).getClass().getModule() == p2);
|
||||
}
|
||||
}
|
||||
|
||||
private <E> List<E> collectAll(ServiceLoader<E> loader) {
|
||||
List<E> list = new ArrayList<>();
|
||||
Iterator<E> iterator = loader.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
list.add(iterator.next());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
98
jdk/test/java/util/ServiceLoader/ReloadTest.java
Normal file
98
jdk/test/java/util/ServiceLoader/ReloadTest.java
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @library modules
|
||||
* @modules java.scripting
|
||||
* @run testng/othervm ReloadTest
|
||||
* @summary Basic test of ServiceLoader.reload
|
||||
*/
|
||||
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.ServiceLoader.Provider;
|
||||
import java.util.Spliterator;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import static java.util.ServiceLoader.*;
|
||||
import javax.script.ScriptEngineFactory;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
@Test
|
||||
public class ReloadTest {
|
||||
|
||||
public void testReload() {
|
||||
ServiceLoader<ScriptEngineFactory> sl = load(ScriptEngineFactory.class);
|
||||
List<String> names1 = sl.stream()
|
||||
.map(Provider::get)
|
||||
.map(ScriptEngineFactory::getEngineName)
|
||||
.collect(Collectors.toList());
|
||||
assertFalse(names1.isEmpty());
|
||||
sl.reload();
|
||||
List<String> names2 = sl.stream()
|
||||
.map(Provider::get)
|
||||
.map(ScriptEngineFactory::getEngineName)
|
||||
.collect(Collectors.toList());
|
||||
assertEquals(names1, names2);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = { ConcurrentModificationException.class })
|
||||
public void testIteratorHasNext() {
|
||||
ServiceLoader<ScriptEngineFactory> sl = load(ScriptEngineFactory.class);
|
||||
Iterator<ScriptEngineFactory> iterator = sl.iterator();
|
||||
sl.reload();
|
||||
iterator.hasNext();
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = { ConcurrentModificationException.class })
|
||||
public void testIteratorNext() {
|
||||
ServiceLoader<ScriptEngineFactory> sl = load(ScriptEngineFactory.class);
|
||||
Iterator<ScriptEngineFactory> iterator = sl.iterator();
|
||||
assertTrue(iterator.hasNext());
|
||||
sl.reload();
|
||||
iterator.next();
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = { ConcurrentModificationException.class })
|
||||
public void testStreamFindAny() {
|
||||
ServiceLoader<ScriptEngineFactory> sl = load(ScriptEngineFactory.class);
|
||||
Stream<Provider<ScriptEngineFactory>> stream = sl.stream();
|
||||
sl.reload();
|
||||
stream.findAny();
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = { ConcurrentModificationException.class })
|
||||
public void testSpliteratorTryAdvance() {
|
||||
ServiceLoader<ScriptEngineFactory> sl = load(ScriptEngineFactory.class);
|
||||
Stream<Provider<ScriptEngineFactory>> stream = sl.stream();
|
||||
Spliterator<Provider<ScriptEngineFactory>> spliterator = stream.spliterator();
|
||||
sl.reload();
|
||||
spliterator.tryAdvance(System.out::println);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @build test/*
|
||||
* @run main/othervm test/p.Main
|
||||
* @summary Basic test of ServiceLoader to ensure that static provider methods
|
||||
* are explicitly declared and not inherited from super classes
|
||||
*/
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import p.Main.*;
|
||||
|
||||
module test {
|
||||
exports p;
|
||||
uses S;
|
||||
provides S with P1, P2, P3;
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package p;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Basic test of ServiceLoader with a provider interface and 3 provider
|
||||
* implementations.
|
||||
*
|
||||
* The provider interface (test.Main.Provider) defines a static factory method
|
||||
* name "provider" that locates a provider implementation. At least one of the
|
||||
* provider implementations does not define a static "provider" method.
|
||||
*/
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
List<S> providers = ServiceLoader.load(S.class).stream()
|
||||
.map(ServiceLoader.Provider::get)
|
||||
.collect(Collectors.toList());
|
||||
if (providers.size() != 3)
|
||||
throw new RuntimeException("Expected 3 providers");
|
||||
}
|
||||
|
||||
/**
|
||||
* Service type
|
||||
*/
|
||||
public static interface S {
|
||||
}
|
||||
|
||||
/**
|
||||
* Base implementation, its static provider method should never be called
|
||||
*/
|
||||
public static class BaseProvider implements S {
|
||||
protected BaseProvider() { }
|
||||
public static S provider() {
|
||||
throw new RuntimeException("Should not get here");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provider implementation with public constructor.
|
||||
*/
|
||||
public static class P1 extends BaseProvider {
|
||||
public P1() { }
|
||||
}
|
||||
|
||||
/**
|
||||
* Provider implementation with static factory method.
|
||||
*/
|
||||
public static class P2 extends BaseProvider {
|
||||
private P2() { }
|
||||
public static P2 provider() {
|
||||
return new P2();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provider implementation with static factory method and public
|
||||
* constructor.
|
||||
*/
|
||||
public static class P3 extends BaseProvider {
|
||||
public P3() {
|
||||
throw new RuntimeException("Should not get here");
|
||||
}
|
||||
private P3(int x) { }
|
||||
public static S provider() {
|
||||
return new P3(0);
|
||||
}
|
||||
}
|
||||
}
|
26
jdk/test/java/util/ServiceLoader/modules/p1/module-info.java
Normal file
26
jdk/test/java/util/ServiceLoader/modules/p1/module-info.java
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
module p1 {
|
||||
requires s1;
|
||||
provides p.S with q.P;
|
||||
}
|
27
jdk/test/java/util/ServiceLoader/modules/p1/q/P.java
Normal file
27
jdk/test/java/util/ServiceLoader/modules/p1/q/P.java
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package q;
|
||||
|
||||
import p.S;
|
||||
|
||||
public class P implements S { }
|
26
jdk/test/java/util/ServiceLoader/modules/p2/module-info.java
Normal file
26
jdk/test/java/util/ServiceLoader/modules/p2/module-info.java
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
module p2 {
|
||||
requires s2;
|
||||
provides p.S with q.P;
|
||||
}
|
27
jdk/test/java/util/ServiceLoader/modules/p2/q/P.java
Normal file
27
jdk/test/java/util/ServiceLoader/modules/p2/q/P.java
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package q;
|
||||
|
||||
import p.S;
|
||||
|
||||
public class P implements S { }
|
26
jdk/test/java/util/ServiceLoader/modules/s1/module-info.java
Normal file
26
jdk/test/java/util/ServiceLoader/modules/s1/module-info.java
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
module s1 {
|
||||
exports p;
|
||||
uses p.S;
|
||||
}
|
25
jdk/test/java/util/ServiceLoader/modules/s1/p/S.java
Normal file
25
jdk/test/java/util/ServiceLoader/modules/s1/p/S.java
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package p;
|
||||
|
||||
public interface S { }
|
25
jdk/test/java/util/ServiceLoader/modules/s2/module-info.java
Normal file
25
jdk/test/java/util/ServiceLoader/modules/s2/module-info.java
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
module s2 {
|
||||
exports p;
|
||||
}
|
25
jdk/test/java/util/ServiceLoader/modules/s2/p/S.java
Normal file
25
jdk/test/java/util/ServiceLoader/modules/s2/p/S.java
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package p;
|
||||
|
||||
public interface S { }
|
31
jdk/test/java/util/ServiceLoader/nouses/NoUsesTest.java
Normal file
31
jdk/test/java/util/ServiceLoader/nouses/NoUsesTest.java
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @modules java.scripting
|
||||
* @build test/*
|
||||
* @run main/othervm test/p.Main
|
||||
* @summary Basic test of ServiceLoader.load from named modules that does
|
||||
* does delcare the use
|
||||
*/
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
module test {
|
||||
exports p;
|
||||
requires java.scripting;
|
||||
}
|
55
jdk/test/java/util/ServiceLoader/nouses/test/p/Main.java
Normal file
55
jdk/test/java/util/ServiceLoader/nouses/test/p/Main.java
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package p;
|
||||
|
||||
import java.util.ServiceConfigurationError;
|
||||
import java.util.ServiceLoader;
|
||||
import javax.script.ScriptEngineFactory;
|
||||
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
Module thisModule = Main.class.getModule();
|
||||
assertTrue(thisModule.isNamed());
|
||||
|
||||
// this module does not declare that it uses ScriptEngineFactory
|
||||
assertFalse(thisModule.canUse(ScriptEngineFactory.class));
|
||||
try {
|
||||
ServiceLoader.load(ScriptEngineFactory.class);
|
||||
assertTrue(false);
|
||||
} catch (ServiceConfigurationError expected) { }
|
||||
|
||||
// invoke addUses and retry
|
||||
thisModule.addUses(ScriptEngineFactory.class);
|
||||
ServiceLoader.load(ScriptEngineFactory.class).findFirst();
|
||||
}
|
||||
|
||||
static void assertFalse(boolean value) {
|
||||
if (value) throw new RuntimeException();
|
||||
}
|
||||
|
||||
static void assertTrue(boolean value) {
|
||||
if (!value) throw new RuntimeException();
|
||||
}
|
||||
}
|
29
jdk/test/java/util/ServiceLoader/security/SecurityTest.java
Normal file
29
jdk/test/java/util/ServiceLoader/security/SecurityTest.java
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @build test/*
|
||||
* @run testng/othervm test/p.Tests
|
||||
* @summary Tests to exercise ServiceLoader with a security manager
|
||||
*/
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import p.Tests.*;
|
||||
|
||||
module test {
|
||||
uses S1;
|
||||
uses S2;
|
||||
provides S1 with P1;
|
||||
provides S2 with P2;
|
||||
requires testng;
|
||||
exports p to testng;
|
||||
}
|
185
jdk/test/java/util/ServiceLoader/security/test/p/Tests.java
Normal file
185
jdk/test/java/util/ServiceLoader/security/test/p/Tests.java
Normal file
@ -0,0 +1,185 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package p;
|
||||
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessControlException;
|
||||
import java.security.AccessController;
|
||||
import java.security.AllPermission;
|
||||
import java.security.CodeSource;
|
||||
import java.security.Permission;
|
||||
import java.security.PermissionCollection;
|
||||
import java.security.Permissions;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.ServiceConfigurationError;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.ServiceLoader.Provider;
|
||||
import static java.security.AccessController.doPrivileged;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
import org.testng.annotations.BeforeTest;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
/**
|
||||
* Basic tests with a security manager to ensure that the provider code
|
||||
* is run with permissions restricted by whatever created the ServiceLoader
|
||||
* object.
|
||||
*/
|
||||
|
||||
public class Tests {
|
||||
|
||||
static final Permission PERM = new RuntimePermission("eatMuffin");
|
||||
|
||||
static <T> PrivilegedAction<ServiceLoader<T>> loadAction(Class<T> service) {
|
||||
return () -> ServiceLoader.load(service);
|
||||
}
|
||||
|
||||
static AccessControlContext withPermissions(Permission... perms) {
|
||||
Permissions p = new Permissions();
|
||||
for (Permission perm : perms) {
|
||||
p.add(perm);
|
||||
}
|
||||
ProtectionDomain pd = new ProtectionDomain(null, p);
|
||||
return new AccessControlContext(new ProtectionDomain[]{ pd });
|
||||
}
|
||||
|
||||
static AccessControlContext noPermissions() {
|
||||
return withPermissions(/*empty*/);
|
||||
}
|
||||
|
||||
@BeforeTest
|
||||
public void setSecurityManager() {
|
||||
class Policy extends java.security.Policy {
|
||||
private final Permissions perms;
|
||||
public Policy(Permission... permissions) {
|
||||
perms = new Permissions();
|
||||
for (Permission permission : permissions) {
|
||||
perms.add(permission);
|
||||
}
|
||||
}
|
||||
public PermissionCollection getPermissions(CodeSource cs) {
|
||||
return perms;
|
||||
}
|
||||
public PermissionCollection getPermissions(ProtectionDomain pd) {
|
||||
return perms;
|
||||
}
|
||||
public boolean implies(ProtectionDomain pd, Permission p) {
|
||||
return perms.implies(p);
|
||||
}
|
||||
public void refresh() { }
|
||||
}
|
||||
Policy policy = new Policy(new AllPermission());
|
||||
Policy.setPolicy(policy);
|
||||
System.setSecurityManager(new SecurityManager());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstructorUsingIteratorWithPermission() {
|
||||
ServiceLoader<S1> sl = doPrivileged(loadAction(S1.class), withPermissions(PERM));
|
||||
S1 obj = sl.iterator().next();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstructorUsingStreamWithPermission() {
|
||||
ServiceLoader<S1> sl = doPrivileged(loadAction(S1.class), withPermissions(PERM));
|
||||
assertTrue(sl.stream().map(Provider::get).count() == 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstructorUsingIteratorNoPermission() {
|
||||
ServiceLoader<S1> sl = doPrivileged(loadAction(S1.class), noPermissions());
|
||||
try {
|
||||
sl.iterator().next();
|
||||
assertTrue(false);
|
||||
} catch (ServiceConfigurationError e) {
|
||||
assertTrue(e.getCause() instanceof AccessControlException);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstructorUsingStreamNoPermission() {
|
||||
ServiceLoader<S1> sl = doPrivileged(loadAction(S1.class), noPermissions());
|
||||
try {
|
||||
sl.stream().map(Provider::get).count();
|
||||
assertTrue(false);
|
||||
} catch (ServiceConfigurationError e) {
|
||||
assertTrue(e.getCause() instanceof AccessControlException);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFactoryMethodUsingIteratorWithPermission() {
|
||||
ServiceLoader<S2> sl = doPrivileged(loadAction(S2.class), withPermissions(PERM));
|
||||
S2 obj = sl.iterator().next();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFactoryMethodUsingStreamWithPermission() {
|
||||
ServiceLoader<S2> sl = doPrivileged(loadAction(S2.class), withPermissions(PERM));
|
||||
assertTrue(sl.stream().map(Provider::get).count() == 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFactoryMethodUsingIteratorNoPermission() {
|
||||
ServiceLoader<S2> sl = doPrivileged(loadAction(S2.class), noPermissions());
|
||||
try {
|
||||
sl.iterator().next();
|
||||
assertTrue(false);
|
||||
} catch (ServiceConfigurationError e) {
|
||||
assertTrue(e.getCause() instanceof AccessControlException);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFactoryMethodUsingStreamNoPermission() {
|
||||
ServiceLoader<S2> sl = doPrivileged(loadAction(S2.class), noPermissions());
|
||||
try {
|
||||
sl.stream().map(Provider::get).count();
|
||||
assertTrue(false);
|
||||
} catch (ServiceConfigurationError e) {
|
||||
assertTrue(e.getCause() instanceof AccessControlException);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// service types and implementations
|
||||
|
||||
public static interface S1 { }
|
||||
public static interface S2 { }
|
||||
|
||||
public static class P1 implements S1 {
|
||||
public P1() {
|
||||
AccessController.getContext().checkPermission(PERM);
|
||||
}
|
||||
}
|
||||
public static class P2 implements S2 {
|
||||
private P2() {
|
||||
AccessController.getContext().checkPermission(PERM);
|
||||
}
|
||||
public static S2 provider() {
|
||||
return new P2();
|
||||
}
|
||||
}
|
||||
}
|
@ -25,7 +25,7 @@
|
||||
* @test
|
||||
* @library modules
|
||||
* @build m1/* m2/*
|
||||
* @run testng/othervm --add-modules=m1,m2 Basic
|
||||
* @run testng/othervm --add-modules=m1,m2 --illegal-access=deny Basic
|
||||
* @summary Basic test of open modules and open packages
|
||||
*/
|
||||
|
||||
|
@ -75,7 +75,8 @@ public class AddExportsAndOpensInManifest {
|
||||
Paths.get("Test1.class"), Paths.get("Test2.class"));
|
||||
|
||||
// java -jar test.jar
|
||||
return ProcessTools.executeTestJava("-jar", jarfile.toString())
|
||||
return ProcessTools.executeTestJava("--illegal-access=deny",
|
||||
"-jar", jarfile.toString())
|
||||
.outputTo(System.out)
|
||||
.errorTo(System.out);
|
||||
}
|
||||
|
@ -0,0 +1,491 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* java.base/sun.security.x509
|
||||
* java.activation
|
||||
* @library /lib/testlibrary modules
|
||||
* @build IllegalAccessTest TryAccess JarUtils CompilerUtils jdk.testlibrary.*
|
||||
* @build m/*
|
||||
* @run testng/othervm/timeout=180 IllegalAccessTest
|
||||
* @summary Basic test for java --illegal-access=$VALUE
|
||||
*/
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.Manifest;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jdk.testlibrary.ProcessTools;
|
||||
import jdk.testlibrary.OutputAnalyzer;
|
||||
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
/**
|
||||
* Basic test of --illegal-access=value to deny or permit access to JDK internals.
|
||||
*/
|
||||
|
||||
@Test
|
||||
public class IllegalAccessTest {
|
||||
|
||||
static final String TEST_SRC = System.getProperty("test.src");
|
||||
static final String TEST_CLASSES = System.getProperty("test.classes");
|
||||
static final String MODULE_PATH = System.getProperty("jdk.module.path");
|
||||
|
||||
/**
|
||||
* Represents the expected result of a test.
|
||||
*/
|
||||
static final class Result {
|
||||
private final boolean success;
|
||||
private final List<String> expectedOutput = new ArrayList<>();
|
||||
private final List<String> notExpectedOutput = new ArrayList<>();
|
||||
|
||||
Result(boolean success) {
|
||||
this.success = success;
|
||||
}
|
||||
|
||||
Result expect(String msg) {
|
||||
expectedOutput.add(msg);
|
||||
return this;
|
||||
}
|
||||
|
||||
Result doNotExpect(String msg) {
|
||||
notExpectedOutput.add(msg);
|
||||
return this;
|
||||
}
|
||||
|
||||
boolean shouldSucceed() {
|
||||
return success;
|
||||
}
|
||||
|
||||
Stream<String> expectedOutput() {
|
||||
return expectedOutput.stream();
|
||||
}
|
||||
|
||||
Stream<String> notExpectedOutput() {
|
||||
return notExpectedOutput.stream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String s = (success) ? "success" : "failure";
|
||||
for (String msg : expectedOutput) {
|
||||
s += "/" + msg;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
static Result success() {
|
||||
return new Result(true);
|
||||
}
|
||||
|
||||
static Result successNoWarning() {
|
||||
return success().doNotExpect("WARNING");
|
||||
}
|
||||
|
||||
static Result successWithWarning() {
|
||||
return success().expect("WARNING");
|
||||
}
|
||||
|
||||
static Result fail(String expectedOutput) {
|
||||
return new Result(false).expect(expectedOutput).doNotExpect("WARNING");
|
||||
}
|
||||
|
||||
@DataProvider(name = "denyCases")
|
||||
public Object[][] denyCases() {
|
||||
return new Object[][] {
|
||||
{ "accessPublicClassNonExportedPackage", fail("IllegalAccessError") },
|
||||
{ "accessPublicClassJdk9NonExportedPackage", fail("IllegalAccessError") },
|
||||
|
||||
{ "reflectPublicMemberExportedPackage", successNoWarning() },
|
||||
{ "reflectNonPublicMemberExportedPackage", fail("IllegalAccessException") },
|
||||
{ "reflectPublicMemberNonExportedPackage", fail("IllegalAccessException") },
|
||||
{ "reflectNonPublicMemberNonExportedPackage", fail("IllegalAccessException") },
|
||||
{ "reflectPublicMemberJdk9NonExportedPackage", fail("IllegalAccessException") },
|
||||
{ "reflectPublicMemberApplicationModule", successNoWarning() },
|
||||
|
||||
{ "setAccessiblePublicMemberExportedPackage", successNoWarning() },
|
||||
{ "setAccessibleNonPublicMemberExportedPackage", fail("InaccessibleObjectException") },
|
||||
{ "setAccessiblePublicMemberNonExportedPackage", fail("InaccessibleObjectException") },
|
||||
{ "setAccessibleNonPublicMemberNonExportedPackage", fail("InaccessibleObjectException") },
|
||||
{ "setAccessiblePublicMemberJdk9NonExportedPackage", fail("InaccessibleObjectException") },
|
||||
{ "setAccessiblePublicMemberApplicationModule", successNoWarning() },
|
||||
{ "setAccessibleNotPublicMemberApplicationModule", fail("InaccessibleObjectException") },
|
||||
|
||||
{ "privateLookupPublicClassExportedPackage", fail("IllegalAccessException") },
|
||||
{ "privateLookupNonPublicClassExportedPackage", fail("IllegalAccessException") },
|
||||
{ "privateLookupPublicClassNonExportedPackage", fail("IllegalAccessException") },
|
||||
{ "privateLookupNonPublicClassNonExportedPackage", fail("IllegalAccessException") },
|
||||
{ "privateLookupPublicClassJdk9NonExportedPackage", fail("IllegalAccessException") },
|
||||
};
|
||||
}
|
||||
|
||||
@DataProvider(name = "permitCases")
|
||||
public Object[][] permitCases() {
|
||||
return new Object[][] {
|
||||
{ "accessPublicClassNonExportedPackage", successNoWarning() },
|
||||
{ "accessPublicClassJdk9NonExportedPackage", fail("IllegalAccessError") },
|
||||
|
||||
{ "reflectPublicMemberExportedPackage", successNoWarning() },
|
||||
{ "reflectNonPublicMemberExportedPackage", fail("IllegalAccessException") },
|
||||
{ "reflectPublicMemberNonExportedPackage", successWithWarning() },
|
||||
{ "reflectNonPublicMemberNonExportedPackage", fail("IllegalAccessException") },
|
||||
{ "reflectPublicMemberJdk9NonExportedPackage", fail("IllegalAccessException") },
|
||||
|
||||
{ "setAccessiblePublicMemberExportedPackage", successNoWarning()},
|
||||
{ "setAccessibleNonPublicMemberExportedPackage", successWithWarning() },
|
||||
{ "setAccessiblePublicMemberNonExportedPackage", successWithWarning() },
|
||||
{ "setAccessibleNonPublicMemberNonExportedPackage", successWithWarning() },
|
||||
{ "setAccessiblePublicMemberJdk9NonExportedPackage", fail("InaccessibleObjectException") },
|
||||
{ "setAccessiblePublicMemberApplicationModule", successNoWarning() },
|
||||
{ "setAccessibleNotPublicMemberApplicationModule", fail("InaccessibleObjectException") },
|
||||
|
||||
{ "privateLookupPublicClassExportedPackage", successWithWarning() },
|
||||
{ "privateLookupNonPublicClassExportedPackage", successWithWarning() },
|
||||
{ "privateLookupPublicClassNonExportedPackage", successWithWarning() },
|
||||
{ "privateLookupNonPublicClassNonExportedPackage", successWithWarning() },
|
||||
{ "privateLookupPublicClassJdk9NonExportedPackage", fail("IllegalAccessException") },
|
||||
{ "privateLookupPublicClassApplicationModule", fail("IllegalAccessException") },
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks an expected result with the output captured by the given
|
||||
* OutputAnalyzer.
|
||||
*/
|
||||
void checkResult(Result expectedResult, OutputAnalyzer outputAnalyzer) {
|
||||
expectedResult.expectedOutput().forEach(outputAnalyzer::shouldContain);
|
||||
expectedResult.notExpectedOutput().forEach(outputAnalyzer::shouldNotContain);
|
||||
int exitValue = outputAnalyzer.getExitValue();
|
||||
if (expectedResult.shouldSucceed()) {
|
||||
assertTrue(exitValue == 0);
|
||||
} else {
|
||||
assertTrue(exitValue != 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the test to execute the given test action. The VM is run with the
|
||||
* given VM options and the output checked to see that it matches the
|
||||
* expected result.
|
||||
*/
|
||||
OutputAnalyzer run(String action, Result expectedResult, String... vmopts)
|
||||
throws Exception
|
||||
{
|
||||
Stream<String> s1 = Stream.of(vmopts);
|
||||
Stream<String> s2 = Stream.of("-p", MODULE_PATH, "--add-modules=m",
|
||||
"-cp", TEST_CLASSES, "TryAccess", action);
|
||||
String[] opts = Stream.concat(s1, s2).toArray(String[]::new);
|
||||
OutputAnalyzer outputAnalyzer = ProcessTools
|
||||
.executeTestJava(opts)
|
||||
.outputTo(System.out)
|
||||
.errorTo(System.out);
|
||||
if (expectedResult != null)
|
||||
checkResult(expectedResult, outputAnalyzer);
|
||||
return outputAnalyzer;
|
||||
}
|
||||
|
||||
OutputAnalyzer run(String action, String... vmopts) throws Exception {
|
||||
return run(action, null, vmopts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs an executable JAR to execute the given test action. The VM is run
|
||||
* with the given VM options and the output checked to see that it matches
|
||||
* the expected result.
|
||||
*/
|
||||
void run(Path jarFile, String action, Result expectedResult, String... vmopts)
|
||||
throws Exception
|
||||
{
|
||||
Stream<String> s1 = Stream.of(vmopts);
|
||||
Stream<String> s2 = Stream.of("-jar", jarFile.toString(), action);
|
||||
String[] opts = Stream.concat(s1, s2).toArray(String[]::new);
|
||||
checkResult(expectedResult, ProcessTools.executeTestJava(opts)
|
||||
.outputTo(System.out)
|
||||
.errorTo(System.out));
|
||||
}
|
||||
|
||||
@Test(dataProvider = "denyCases")
|
||||
public void testDeny(String action, Result expectedResult) throws Exception {
|
||||
run(action, expectedResult, "--illegal-access=deny");
|
||||
}
|
||||
|
||||
@Test(dataProvider = "permitCases")
|
||||
public void testDefault(String action, Result expectedResult) throws Exception {
|
||||
run(action, expectedResult);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "permitCases")
|
||||
public void testPermit(String action, Result expectedResult) throws Exception {
|
||||
run(action, expectedResult, "--illegal-access=permit");
|
||||
}
|
||||
|
||||
@Test(dataProvider = "permitCases")
|
||||
public void testWarn(String action, Result expectedResult) throws Exception {
|
||||
run(action, expectedResult, "--illegal-access=warn");
|
||||
}
|
||||
|
||||
@Test(dataProvider = "permitCases")
|
||||
public void testDebug(String action, Result expectedResult) throws Exception {
|
||||
// expect stack trace with WARNING
|
||||
if (expectedResult.expectedOutput().anyMatch("WARNING"::equals)) {
|
||||
expectedResult.expect("TryAccess.main");
|
||||
}
|
||||
run(action, expectedResult, "--illegal-access=debug");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test accessing internals of upgradeable module
|
||||
*/
|
||||
public void testWithUpgradedModule() throws Exception {
|
||||
// upgradeable module loaded from run-time image
|
||||
run("setAccessibleNotPublicMemberUpgradeableModule", successWithWarning(),
|
||||
"--add-modules=java.activation");
|
||||
|
||||
// upgradeable module loaded from upgrade module path
|
||||
Path upgradesrc = Paths.get(TEST_SRC, "upgradesrc");
|
||||
Path upgrademods = Files.createDirectory(Paths.get("upgrademods"));
|
||||
Path output = upgrademods.resolve("java.activation");
|
||||
assertTrue(CompilerUtils.compile(upgradesrc, output));
|
||||
run("setAccessibleNotPublicMemberUpgradeableModule",
|
||||
fail("InaccessibleObjectException"),
|
||||
"--upgrade-module-path=" + upgrademods,
|
||||
"--add-modules=java.activation");
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify --add-exports to export a package
|
||||
*/
|
||||
public void testWithAddExportsOption() throws Exception {
|
||||
// warning
|
||||
run("reflectPublicMemberNonExportedPackage", successWithWarning());
|
||||
|
||||
// no warning due to --add-exports
|
||||
run("reflectPublicMemberNonExportedPackage", successNoWarning(),
|
||||
"--add-exports", "java.base/sun.security.x509=ALL-UNNAMED");
|
||||
|
||||
// attempt two illegal accesses, one allowed by --add-exports
|
||||
run("reflectPublicMemberNonExportedPackage"
|
||||
+ ",setAccessibleNonPublicMemberExportedPackage",
|
||||
successWithWarning(),
|
||||
"--add-exports", "java.base/sun.security.x509=ALL-UNNAMED");
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify --add-open to open a package
|
||||
*/
|
||||
public void testWithAddOpensOption() throws Exception {
|
||||
// warning
|
||||
run("setAccessibleNonPublicMemberExportedPackage", successWithWarning());
|
||||
|
||||
// no warning due to --add-opens
|
||||
run("setAccessibleNonPublicMemberExportedPackage", successNoWarning(),
|
||||
"--add-opens", "java.base/java.lang=ALL-UNNAMED");
|
||||
|
||||
// attempt two illegal accesses, one allowed by --add-opens
|
||||
run("reflectPublicMemberNonExportedPackage"
|
||||
+ ",setAccessibleNonPublicMemberExportedPackage",
|
||||
successWithWarning(),
|
||||
"--add-opens", "java.base/java.lang=ALL-UNNAMED");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test reflective API to export a package
|
||||
*/
|
||||
public void testWithReflectiveExports() throws Exception {
|
||||
// compile patch for java.base
|
||||
Path src = Paths.get(TEST_SRC, "patchsrc", "java.base");
|
||||
Path patch = Files.createDirectories(Paths.get("patches", "java.base"));
|
||||
assertTrue(CompilerUtils.compile(src, patch,
|
||||
"--patch-module", "java.base=" + src));
|
||||
|
||||
// reflectively export, then access
|
||||
run("exportNonExportedPackages,reflectPublicMemberNonExportedPackage",
|
||||
successNoWarning(),
|
||||
"--patch-module", "java.base=" + patch);
|
||||
|
||||
// access, reflectively export, access again
|
||||
List<String> output = run("reflectPublicMemberNonExportedPackage,"
|
||||
+ "exportNonExportedPackages,"
|
||||
+ "reflectPublicMemberNonExportedPackage",
|
||||
"--patch-module", "java.base="+patch,
|
||||
"--illegal-access=warn").asLines();
|
||||
assertTrue(count(output, "WARNING") == 1); // one warning
|
||||
}
|
||||
|
||||
/**
|
||||
* Test reflective API to open a package
|
||||
*/
|
||||
public void testWithReflectiveOpens() throws Exception {
|
||||
// compile patch for java.base
|
||||
Path src = Paths.get(TEST_SRC, "patchsrc", "java.base");
|
||||
Path patch = Files.createDirectories(Paths.get("patches", "java.base"));
|
||||
assertTrue(CompilerUtils.compile(src, patch,
|
||||
"--patch-module", "java.base=" + src));
|
||||
|
||||
// reflectively open exported package, then access
|
||||
run("openExportedPackage,setAccessibleNonPublicMemberExportedPackage",
|
||||
successNoWarning(),
|
||||
"--patch-module", "java.base=" + patch);
|
||||
|
||||
// access, reflectively open exported package, access again
|
||||
List<String> output1 = run("setAccessibleNonPublicMemberExportedPackage"
|
||||
+ ",openExportedPackage"
|
||||
+ ",setAccessibleNonPublicMemberExportedPackage",
|
||||
"--patch-module", "java.base=" + patch,
|
||||
"--illegal-access=warn").asLines();
|
||||
assertTrue(count(output1, "WARNING") == 1); // one warning
|
||||
|
||||
// reflectively open non-exported packages, then access
|
||||
run("openNonExportedPackages,setAccessibleNonPublicMemberNonExportedPackage",
|
||||
successNoWarning(),
|
||||
"--patch-module", "java.base=" + patch);
|
||||
|
||||
// access, reflectively open non-exported package, access again
|
||||
List<String> output2 = run("setAccessibleNonPublicMemberNonExportedPackage"
|
||||
+ ",openNonExportedPackages"
|
||||
+ ",setAccessibleNonPublicMemberNonExportedPackage",
|
||||
"--patch-module", "java.base=" + patch,
|
||||
"--illegal-access=warn").asLines();
|
||||
assertTrue(count(output2, "WARNING") == 1); // one warning
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify Add-Exports in JAR file manifest
|
||||
*/
|
||||
public void testWithAddExportsInManifest() throws Exception {
|
||||
Manifest man = new Manifest();
|
||||
Attributes attrs = man.getMainAttributes();
|
||||
attrs.put(Attributes.Name.MANIFEST_VERSION, "1.0");
|
||||
attrs.put(Attributes.Name.MAIN_CLASS, "TryAccess");
|
||||
attrs.put(new Attributes.Name("Add-Exports"), "java.base/sun.security.x509");
|
||||
Path jarfile = Paths.get("x.jar");
|
||||
Path classes = Paths.get(TEST_CLASSES);
|
||||
JarUtils.createJarFile(jarfile, man, classes, Paths.get("TryAccess.class"));
|
||||
|
||||
run(jarfile, "reflectPublicMemberNonExportedPackage", successNoWarning());
|
||||
|
||||
run(jarfile, "setAccessibleNonPublicMemberExportedPackage", successWithWarning());
|
||||
|
||||
// attempt two illegal accesses, one allowed by Add-Exports
|
||||
run(jarfile, "reflectPublicMemberNonExportedPackage,"
|
||||
+ "setAccessibleNonPublicMemberExportedPackage",
|
||||
successWithWarning());
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify Add-Opens in JAR file manifest
|
||||
*/
|
||||
public void testWithAddOpensInManifest() throws Exception {
|
||||
Manifest man = new Manifest();
|
||||
Attributes attrs = man.getMainAttributes();
|
||||
attrs.put(Attributes.Name.MANIFEST_VERSION, "1.0");
|
||||
attrs.put(Attributes.Name.MAIN_CLASS, "TryAccess");
|
||||
attrs.put(new Attributes.Name("Add-Opens"), "java.base/java.lang");
|
||||
Path jarfile = Paths.get("x.jar");
|
||||
Path classes = Paths.get(TEST_CLASSES);
|
||||
JarUtils.createJarFile(jarfile, man, classes, Paths.get("TryAccess.class"));
|
||||
|
||||
run(jarfile, "setAccessibleNonPublicMemberExportedPackage", successNoWarning());
|
||||
|
||||
run(jarfile, "reflectPublicMemberNonExportedPackage", successWithWarning());
|
||||
|
||||
// attempt two illegal accesses, one allowed by Add-Opens
|
||||
run(jarfile, "reflectPublicMemberNonExportedPackage,"
|
||||
+ "setAccessibleNonPublicMemberExportedPackage",
|
||||
successWithWarning());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that default behavior is to print a warning on the first illegal
|
||||
* access only.
|
||||
*/
|
||||
public void testWarnOnFirstIllegalAccess() throws Exception {
|
||||
String action1 = "reflectPublicMemberNonExportedPackage";
|
||||
String action2 = "setAccessibleNonPublicMemberExportedPackage";
|
||||
int warningCount = count(run(action1).asLines(), "WARNING");
|
||||
|
||||
// same illegal access
|
||||
List<String> output1 = run(action1 + "," + action1).asLines();
|
||||
assertTrue(count(output1, "WARNING") == warningCount);
|
||||
|
||||
// different illegal access
|
||||
List<String> output2 = run(action1 + "," + action2).asLines();
|
||||
assertTrue(count(output2, "WARNING") == warningCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that --illegal-access=warn prints a one-line warning per each unique
|
||||
* illegal access.
|
||||
*/
|
||||
public void testWarnPerIllegalAccess() throws Exception {
|
||||
String action1 = "reflectPublicMemberNonExportedPackage";
|
||||
String action2 = "setAccessibleNonPublicMemberExportedPackage";
|
||||
|
||||
// same illegal access
|
||||
String repeatedActions = action1 + "," + action1;
|
||||
List<String> output1 = run(repeatedActions, "--illegal-access=warn").asLines();
|
||||
assertTrue(count(output1, "WARNING") == 1);
|
||||
|
||||
// different illegal access
|
||||
String differentActions = action1 + "," + action2;
|
||||
List<String> output2 = run(differentActions, "--illegal-access=warn").asLines();
|
||||
assertTrue(count(output2, "WARNING") == 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify --illegal-access more than once, last one wins
|
||||
*/
|
||||
public void testRepeatedOption() throws Exception {
|
||||
run("accessPublicClassNonExportedPackage", successNoWarning(),
|
||||
"--illegal-access=deny", "--illegal-access=permit");
|
||||
run("accessPublicClassNonExportedPackage", fail("IllegalAccessError"),
|
||||
"--illegal-access=permit", "--illegal-access=deny");
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify bad value to --illegal-access
|
||||
*/
|
||||
public void testBadValue() throws Exception {
|
||||
run("accessPublicClassNonExportedPackage",
|
||||
fail("Value specified to --illegal-access not recognized"),
|
||||
"--illegal-access=BAD");
|
||||
}
|
||||
|
||||
private int count(Iterable<String> lines, CharSequence cs) {
|
||||
int count = 0;
|
||||
for (String line : lines) {
|
||||
if (line.contains(cs)) count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
194
jdk/test/tools/launcher/modules/illegalaccess/TryAccess.java
Normal file
194
jdk/test/tools/launcher/modules/illegalaccess/TryAccess.java
Normal file
@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import javax.activation.MimeTypeParameterList;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.channels.SocketChannel;
|
||||
|
||||
/**
|
||||
* Launched by IllegalAccessTest to attempt illegal access.
|
||||
*/
|
||||
|
||||
public class TryAccess {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String[] methodNames = args[0].split(",");
|
||||
for (String methodName : methodNames) {
|
||||
Method m = TryAccess.class.getDeclaredMethod(methodName);
|
||||
m.invoke(null);
|
||||
}
|
||||
}
|
||||
|
||||
// -- static access --
|
||||
|
||||
static void accessPublicClassNonExportedPackage() throws Exception {
|
||||
Object obj = new sun.security.x509.X500Name("CN=name");
|
||||
}
|
||||
|
||||
static void accessPublicClassJdk9NonExportedPackage() {
|
||||
Object obj = jdk.internal.misc.Unsafe.getUnsafe();
|
||||
}
|
||||
|
||||
// -- reflective access --
|
||||
|
||||
static void reflectPublicMemberExportedPackage() throws Exception {
|
||||
Constructor<?> ctor = String.class.getConstructor(String.class);
|
||||
Object name = ctor.newInstance("value");
|
||||
}
|
||||
|
||||
static void reflectNonPublicMemberExportedPackage() throws Exception {
|
||||
Field f = String.class.getDeclaredField("value");
|
||||
Object obj = f.get("foo");
|
||||
}
|
||||
|
||||
static void reflectPublicMemberNonExportedPackage() throws Exception {
|
||||
Class<?> clazz = Class.forName("sun.security.x509.X500Name");
|
||||
Constructor<?> ctor = clazz.getConstructor(String.class);
|
||||
Object obj = ctor.newInstance("CN=user");
|
||||
}
|
||||
|
||||
static void reflectNonPublicMemberNonExportedPackage() throws Exception {
|
||||
SocketChannel sc = SocketChannel.open();
|
||||
Field f = sc.getClass().getDeclaredField("fd");
|
||||
Object obj = f.get(sc);
|
||||
}
|
||||
|
||||
static void reflectPublicMemberJdk9NonExportedPackage() throws Exception {
|
||||
Class<?> clazz = Class.forName("jdk.internal.misc.Unsafe");
|
||||
Method m = clazz.getMethod("getUnsafe");
|
||||
Object obj = m.invoke(null);
|
||||
}
|
||||
|
||||
static void reflectPublicMemberApplicationModule() throws Exception {
|
||||
Class<?> clazz = Class.forName("p.Type");
|
||||
Constructor<?> ctor = clazz.getConstructor(int.class);
|
||||
Object obj = ctor.newInstance(1);
|
||||
}
|
||||
|
||||
// -- setAccessible --
|
||||
|
||||
static void setAccessiblePublicMemberExportedPackage() throws Exception {
|
||||
Constructor<?> ctor = String.class.getConstructor(String.class);
|
||||
ctor.setAccessible(true);
|
||||
}
|
||||
|
||||
static void setAccessibleNonPublicMemberExportedPackage() throws Exception {
|
||||
Method method = ClassLoader.class.getDeclaredMethod("defineClass",
|
||||
byte[].class, int.class, int.class);
|
||||
method.setAccessible(true);
|
||||
}
|
||||
|
||||
static void setAccessiblePublicMemberNonExportedPackage() throws Exception {
|
||||
Class<?> clazz = Class.forName("sun.security.x509.X500Name");
|
||||
Constructor<?> ctor = clazz.getConstructor(String.class);
|
||||
ctor.setAccessible(true);
|
||||
}
|
||||
|
||||
static void setAccessibleNonPublicMemberNonExportedPackage() throws Exception {
|
||||
SocketChannel sc = SocketChannel.open();
|
||||
Field f = sc.getClass().getDeclaredField("fd");
|
||||
f.setAccessible(true);
|
||||
}
|
||||
|
||||
static void setAccessiblePublicMemberJdk9NonExportedPackage() throws Exception {
|
||||
Class<?> clazz = Class.forName("jdk.internal.misc.Unsafe");
|
||||
Method m = clazz.getMethod("getUnsafe");
|
||||
m.setAccessible(true);
|
||||
}
|
||||
|
||||
static void setAccessiblePublicMemberApplicationModule() throws Exception {
|
||||
Class<?> clazz = Class.forName("p.Type");
|
||||
Constructor<?> ctor = clazz.getConstructor(int.class);
|
||||
ctor.setAccessible(true);
|
||||
}
|
||||
|
||||
static void setAccessibleNotPublicMemberUpgradeableModule() throws Exception {
|
||||
Method method = MimeTypeParameterList.class.getDeclaredMethod("parse",
|
||||
String.class);
|
||||
method.setAccessible(true);
|
||||
}
|
||||
|
||||
static void setAccessibleNotPublicMemberApplicationModule() throws Exception {
|
||||
Class<?> clazz = Class.forName("p.Type");
|
||||
Constructor<?> ctor = clazz.getDeclaredConstructor(int.class, int.class);
|
||||
ctor.setAccessible(true);
|
||||
}
|
||||
|
||||
|
||||
// -- privateLookupIn --
|
||||
|
||||
static void privateLookupPublicClassExportedPackage() throws Exception {
|
||||
MethodHandles.privateLookupIn(String.class, MethodHandles.lookup());
|
||||
}
|
||||
|
||||
static void privateLookupNonPublicClassExportedPackage() throws Exception {
|
||||
Class<?> clazz = Class.forName("java.lang.WeakPairMap");
|
||||
MethodHandles.privateLookupIn(clazz, MethodHandles.lookup());
|
||||
}
|
||||
|
||||
static void privateLookupPublicClassNonExportedPackage() throws Exception {
|
||||
Class<?> clazz = Class.forName("sun.security.x509.X500Name");
|
||||
MethodHandles.privateLookupIn(clazz, MethodHandles.lookup());
|
||||
}
|
||||
|
||||
static void privateLookupNonPublicClassNonExportedPackage() throws Exception {
|
||||
Class<?> clazz = Class.forName("sun.nio.ch.SocketChannelImpl");
|
||||
MethodHandles.privateLookupIn(clazz, MethodHandles.lookup());
|
||||
}
|
||||
|
||||
static void privateLookupPublicClassJdk9NonExportedPackage() throws Exception {
|
||||
Class<?> clazz = Class.forName("jdk.internal.misc.Unsafe");
|
||||
MethodHandles.privateLookupIn(clazz, MethodHandles.lookup());
|
||||
}
|
||||
|
||||
static void privateLookupPublicClassApplicationModule() throws Exception {
|
||||
Class<?> clazz = Class.forName("p.Type");
|
||||
MethodHandles.privateLookupIn(clazz, MethodHandles.lookup());
|
||||
}
|
||||
|
||||
|
||||
// -- export/open packages to this unnamed module --
|
||||
|
||||
static void exportNonExportedPackages() throws Exception {
|
||||
Class<?> helper = Class.forName("java.lang.Helper");
|
||||
Method m = helper.getMethod("export", String.class, Module.class);
|
||||
m.invoke(null, "sun.security.x509", TryAccess.class.getModule());
|
||||
m.invoke(null, "sun.nio.ch", TryAccess.class.getModule());
|
||||
}
|
||||
|
||||
static void openExportedPackage() throws Exception {
|
||||
Class<?> helper = Class.forName("java.lang.Helper");
|
||||
Method m = helper.getMethod("open", String.class, Module.class);
|
||||
m.invoke(null, "java.lang", TryAccess.class.getModule());
|
||||
}
|
||||
|
||||
static void openNonExportedPackages() throws Exception {
|
||||
Class<?> helper = Class.forName("java.lang.Helper");
|
||||
Method m = helper.getMethod("open", String.class, Module.class);
|
||||
m.invoke(null, "sun.security.x509", TryAccess.class.getModule());
|
||||
m.invoke(null, "sun.nio.ch", TryAccess.class.getModule());
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
module m {
|
||||
exports p;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user