This commit is contained in:
Henry Jen 2017-01-19 07:02:33 -08:00
commit d7509c02b3
50 changed files with 1520 additions and 905 deletions

View File

@ -39,7 +39,6 @@ TOOL_GENGRAPHS := $(BUILD_JAVA) -esa -ea -cp $(TOOLS_CLASSES_DIR) \
build.tools.jigsaw.GenGraphs build.tools.jigsaw.GenGraphs
TOOL_MODULESUMMARY := $(BUILD_JAVA) -esa -ea -cp $(TOOLS_CLASSES_DIR) \ TOOL_MODULESUMMARY := $(BUILD_JAVA) -esa -ea -cp $(TOOLS_CLASSES_DIR) \
--add-exports jdk.jdeps/com.sun.tools.classfile=ALL-UNNAMED \
build.tools.jigsaw.ModuleSummary build.tools.jigsaw.ModuleSummary
TOOL_ADD_PACKAGES_ATTRIBUTE := $(BUILD_JAVA) $(JAVA_FLAGS_SMALL) \ TOOL_ADD_PACKAGES_ATTRIBUTE := $(BUILD_JAVA) $(JAVA_FLAGS_SMALL) \

View File

@ -508,8 +508,9 @@ public final class Class<T> implements java.io.Serializable,
public T newInstance() public T newInstance()
throws InstantiationException, IllegalAccessException throws InstantiationException, IllegalAccessException
{ {
if (System.getSecurityManager() != null) { SecurityManager sm = System.getSecurityManager();
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), false); if (sm != null) {
checkMemberAccess(sm, Member.PUBLIC, Reflection.getCallerClass(), false);
} }
// NOTE: the following code may not be strictly correct under // NOTE: the following code may not be strictly correct under
@ -1223,38 +1224,27 @@ public final class Class<T> implements java.io.Serializable,
// Perform access check // Perform access check
final Class<?> enclosingCandidate = enclosingInfo.getEnclosingClass(); final Class<?> enclosingCandidate = enclosingInfo.getEnclosingClass();
enclosingCandidate.checkMemberAccess(Member.DECLARED, SecurityManager sm = System.getSecurityManager();
Reflection.getCallerClass(), true); if (sm != null) {
// Client is ok to access declared methods but j.l.Class might not be. enclosingCandidate.checkMemberAccess(sm, Member.DECLARED,
Method[] candidates = AccessController.doPrivileged( Reflection.getCallerClass(), true);
new PrivilegedAction<>() { }
@Override Method[] candidates = enclosingCandidate.privateGetDeclaredMethods(false);
public Method[] run() {
return enclosingCandidate.getDeclaredMethods();
}
});
/* /*
* Loop over all declared methods; match method name, * Loop over all declared methods; match method name,
* number of and type of parameters, *and* return * number of and type of parameters, *and* return
* type. Matching return type is also necessary * type. Matching return type is also necessary
* because of covariant returns, etc. * because of covariant returns, etc.
*/ */
for(Method m: candidates) { ReflectionFactory fact = getReflectionFactory();
if (m.getName().equals(enclosingInfo.getName()) ) { for (Method m : candidates) {
Class<?>[] candidateParamClasses = m.getParameterTypes(); if (m.getName().equals(enclosingInfo.getName()) &&
if (candidateParamClasses.length == parameterClasses.length) { arrayContentsEq(parameterClasses,
boolean matches = true; fact.getExecutableSharedParameterTypes(m))) {
for(int i = 0; i < candidateParamClasses.length; i++) { // finally, check return type
if (!candidateParamClasses[i].equals(parameterClasses[i])) { if (m.getReturnType().equals(returnType)) {
matches = false; return fact.copyMethod(m);
break;
}
}
if (matches) { // finally, check return type
if (m.getReturnType().equals(returnType) )
return m;
}
} }
} }
} }
@ -1390,33 +1380,23 @@ public final class Class<T> implements java.io.Serializable,
// Perform access check // Perform access check
final Class<?> enclosingCandidate = enclosingInfo.getEnclosingClass(); final Class<?> enclosingCandidate = enclosingInfo.getEnclosingClass();
enclosingCandidate.checkMemberAccess(Member.DECLARED, SecurityManager sm = System.getSecurityManager();
Reflection.getCallerClass(), true); if (sm != null) {
// Client is ok to access declared methods but j.l.Class might not be. enclosingCandidate.checkMemberAccess(sm, Member.DECLARED,
Constructor<?>[] candidates = AccessController.doPrivileged( Reflection.getCallerClass(), true);
new PrivilegedAction<>() { }
@Override
public Constructor<?>[] run() { Constructor<?>[] candidates = enclosingCandidate
return enclosingCandidate.getDeclaredConstructors(); .privateGetDeclaredConstructors(false);
}
});
/* /*
* Loop over all declared constructors; match number * Loop over all declared constructors; match number
* of and type of parameters. * of and type of parameters.
*/ */
for(Constructor<?> c: candidates) { ReflectionFactory fact = getReflectionFactory();
Class<?>[] candidateParamClasses = c.getParameterTypes(); for (Constructor<?> c : candidates) {
if (candidateParamClasses.length == parameterClasses.length) { if (arrayContentsEq(parameterClasses,
boolean matches = true; fact.getExecutableSharedParameterTypes(c))) {
for(int i = 0; i < candidateParamClasses.length; i++) { return fact.copyConstructor(c);
if (!candidateParamClasses[i].equals(parameterClasses[i])) {
matches = false;
break;
}
}
if (matches)
return c;
} }
} }
@ -1446,9 +1426,13 @@ public final class Class<T> implements java.io.Serializable,
public Class<?> getDeclaringClass() throws SecurityException { public Class<?> getDeclaringClass() throws SecurityException {
final Class<?> candidate = getDeclaringClass0(); final Class<?> candidate = getDeclaringClass0();
if (candidate != null) if (candidate != null) {
candidate.checkPackageAccess( SecurityManager sm = System.getSecurityManager();
if (sm != null) {
candidate.checkPackageAccess(sm,
ClassLoader.getClassLoader(Reflection.getCallerClass()), true); ClassLoader.getClassLoader(Reflection.getCallerClass()), true);
}
}
return candidate; return candidate;
} }
@ -1496,9 +1480,13 @@ public final class Class<T> implements java.io.Serializable,
enclosingCandidate = enclosingClass; enclosingCandidate = enclosingClass;
} }
if (enclosingCandidate != null) if (enclosingCandidate != null) {
enclosingCandidate.checkPackageAccess( SecurityManager sm = System.getSecurityManager();
if (sm != null) {
enclosingCandidate.checkPackageAccess(sm,
ClassLoader.getClassLoader(Reflection.getCallerClass()), true); ClassLoader.getClassLoader(Reflection.getCallerClass()), true);
}
}
return enclosingCandidate; return enclosingCandidate;
} }
@ -1688,7 +1676,10 @@ public final class Class<T> implements java.io.Serializable,
*/ */
@CallerSensitive @CallerSensitive
public Class<?>[] getClasses() { public Class<?>[] getClasses() {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), false); SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkMemberAccess(sm, Member.PUBLIC, Reflection.getCallerClass(), false);
}
// Privileged so this implementation can look at DECLARED classes, // Privileged so this implementation can look at DECLARED classes,
// something the caller might not have privilege to do. The code here // something the caller might not have privilege to do. The code here
@ -1754,7 +1745,10 @@ public final class Class<T> implements java.io.Serializable,
*/ */
@CallerSensitive @CallerSensitive
public Field[] getFields() throws SecurityException { public Field[] getFields() throws SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true); SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkMemberAccess(sm, Member.PUBLIC, Reflection.getCallerClass(), true);
}
return copyFields(privateGetPublicFields(null)); return copyFields(privateGetPublicFields(null));
} }
@ -1841,7 +1835,10 @@ public final class Class<T> implements java.io.Serializable,
*/ */
@CallerSensitive @CallerSensitive
public Method[] getMethods() throws SecurityException { public Method[] getMethods() throws SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true); SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkMemberAccess(sm, Member.PUBLIC, Reflection.getCallerClass(), true);
}
return copyMethods(privateGetPublicMethods()); return copyMethods(privateGetPublicMethods());
} }
@ -1877,7 +1874,10 @@ public final class Class<T> implements java.io.Serializable,
*/ */
@CallerSensitive @CallerSensitive
public Constructor<?>[] getConstructors() throws SecurityException { public Constructor<?>[] getConstructors() throws SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true); SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkMemberAccess(sm, Member.PUBLIC, Reflection.getCallerClass(), true);
}
return copyConstructors(privateGetDeclaredConstructors(true)); return copyConstructors(privateGetDeclaredConstructors(true));
} }
@ -1928,7 +1928,10 @@ public final class Class<T> implements java.io.Serializable,
public Field getField(String name) public Field getField(String name)
throws NoSuchFieldException, SecurityException { throws NoSuchFieldException, SecurityException {
Objects.requireNonNull(name); Objects.requireNonNull(name);
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true); SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkMemberAccess(sm, Member.PUBLIC, Reflection.getCallerClass(), true);
}
Field field = getField0(name); Field field = getField0(name);
if (field == null) { if (field == null) {
throw new NoSuchFieldException(name); throw new NoSuchFieldException(name);
@ -2034,10 +2037,13 @@ public final class Class<T> implements java.io.Serializable,
public Method getMethod(String name, Class<?>... parameterTypes) public Method getMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException { throws NoSuchMethodException, SecurityException {
Objects.requireNonNull(name); Objects.requireNonNull(name);
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true); SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkMemberAccess(sm, Member.PUBLIC, Reflection.getCallerClass(), true);
}
Method method = getMethod0(name, parameterTypes); Method method = getMethod0(name, parameterTypes);
if (method == null) { if (method == null) {
throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes)); throw new NoSuchMethodException(methodToString(name, parameterTypes));
} }
return getReflectionFactory().copyMethod(method); return getReflectionFactory().copyMethod(method);
} }
@ -2092,8 +2098,12 @@ public final class Class<T> implements java.io.Serializable,
*/ */
@CallerSensitive @CallerSensitive
public Constructor<T> getConstructor(Class<?>... parameterTypes) public Constructor<T> getConstructor(Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException { throws NoSuchMethodException, SecurityException
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true); {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkMemberAccess(sm, Member.PUBLIC, Reflection.getCallerClass(), true);
}
return getReflectionFactory().copyConstructor( return getReflectionFactory().copyConstructor(
getConstructor0(parameterTypes, Member.PUBLIC)); getConstructor0(parameterTypes, Member.PUBLIC));
} }
@ -2136,7 +2146,10 @@ public final class Class<T> implements java.io.Serializable,
*/ */
@CallerSensitive @CallerSensitive
public Class<?>[] getDeclaredClasses() throws SecurityException { public Class<?>[] getDeclaredClasses() throws SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), false); SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkMemberAccess(sm, Member.DECLARED, Reflection.getCallerClass(), false);
}
return getDeclaredClasses0(); return getDeclaredClasses0();
} }
@ -2185,7 +2198,10 @@ public final class Class<T> implements java.io.Serializable,
*/ */
@CallerSensitive @CallerSensitive
public Field[] getDeclaredFields() throws SecurityException { public Field[] getDeclaredFields() throws SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true); SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkMemberAccess(sm, Member.DECLARED, Reflection.getCallerClass(), true);
}
return copyFields(privateGetDeclaredFields(false)); return copyFields(privateGetDeclaredFields(false));
} }
@ -2244,7 +2260,10 @@ public final class Class<T> implements java.io.Serializable,
*/ */
@CallerSensitive @CallerSensitive
public Method[] getDeclaredMethods() throws SecurityException { public Method[] getDeclaredMethods() throws SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true); SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkMemberAccess(sm, Member.DECLARED, Reflection.getCallerClass(), true);
}
return copyMethods(privateGetDeclaredMethods(false)); return copyMethods(privateGetDeclaredMethods(false));
} }
@ -2289,7 +2308,10 @@ public final class Class<T> implements java.io.Serializable,
*/ */
@CallerSensitive @CallerSensitive
public Constructor<?>[] getDeclaredConstructors() throws SecurityException { public Constructor<?>[] getDeclaredConstructors() throws SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true); SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkMemberAccess(sm, Member.DECLARED, Reflection.getCallerClass(), true);
}
return copyConstructors(privateGetDeclaredConstructors(false)); return copyConstructors(privateGetDeclaredConstructors(false));
} }
@ -2338,7 +2360,10 @@ public final class Class<T> implements java.io.Serializable,
public Field getDeclaredField(String name) public Field getDeclaredField(String name)
throws NoSuchFieldException, SecurityException { throws NoSuchFieldException, SecurityException {
Objects.requireNonNull(name); Objects.requireNonNull(name);
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true); SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkMemberAccess(sm, Member.DECLARED, Reflection.getCallerClass(), true);
}
Field field = searchFields(privateGetDeclaredFields(false), name); Field field = searchFields(privateGetDeclaredFields(false), name);
if (field == null) { if (field == null) {
throw new NoSuchFieldException(name); throw new NoSuchFieldException(name);
@ -2399,10 +2424,13 @@ public final class Class<T> implements java.io.Serializable,
public Method getDeclaredMethod(String name, Class<?>... parameterTypes) public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException { throws NoSuchMethodException, SecurityException {
Objects.requireNonNull(name); Objects.requireNonNull(name);
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true); SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkMemberAccess(sm, Member.DECLARED, Reflection.getCallerClass(), true);
}
Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes); Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);
if (method == null) { if (method == null) {
throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes)); throw new NoSuchMethodException(methodToString(name, parameterTypes));
} }
return getReflectionFactory().copyMethod(method); return getReflectionFactory().copyMethod(method);
} }
@ -2448,8 +2476,13 @@ public final class Class<T> implements java.io.Serializable,
*/ */
@CallerSensitive @CallerSensitive
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException { throws NoSuchMethodException, SecurityException
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true); {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkMemberAccess(sm, Member.DECLARED, Reflection.getCallerClass(), true);
}
return getReflectionFactory().copyConstructor( return getReflectionFactory().copyConstructor(
getConstructor0(parameterTypes, Member.DECLARED)); getConstructor0(parameterTypes, Member.DECLARED));
} }
@ -2697,51 +2730,49 @@ public final class Class<T> implements java.io.Serializable,
* *
* <p> Default policy: allow all clients access with normal Java access * <p> Default policy: allow all clients access with normal Java access
* control. * control.
*
* <p> NOTE: should only be called if a SecurityManager is installed
*/ */
private void checkMemberAccess(int which, Class<?> caller, boolean checkProxyInterfaces) { private void checkMemberAccess(SecurityManager sm, int which,
final SecurityManager s = System.getSecurityManager(); Class<?> caller, boolean checkProxyInterfaces) {
if (s != null) { /* Default policy allows access to all {@link Member#PUBLIC} members,
/* Default policy allows access to all {@link Member#PUBLIC} members, * as well as access to classes that have the same class loader as the caller.
* as well as access to classes that have the same class loader as the caller. * In all other cases, it requires RuntimePermission("accessDeclaredMembers")
* In all other cases, it requires RuntimePermission("accessDeclaredMembers") * permission.
* permission. */
*/ final ClassLoader ccl = caller.getClassLoader0();
final ClassLoader ccl = ClassLoader.getClassLoader(caller); if (which != Member.PUBLIC) {
final ClassLoader cl = getClassLoader0(); final ClassLoader cl = getClassLoader0();
if (which != Member.PUBLIC) { if (ccl != cl) {
if (ccl != cl) { sm.checkPermission(SecurityConstants.CHECK_MEMBER_ACCESS_PERMISSION);
s.checkPermission(SecurityConstants.CHECK_MEMBER_ACCESS_PERMISSION);
}
} }
this.checkPackageAccess(ccl, checkProxyInterfaces);
} }
this.checkPackageAccess(sm, ccl, checkProxyInterfaces);
} }
/* /*
* Checks if a client loaded in ClassLoader ccl is allowed to access this * Checks if a client loaded in ClassLoader ccl is allowed to access this
* class under the current package access policy. If access is denied, * class under the current package access policy. If access is denied,
* throw a SecurityException. * throw a SecurityException.
*
* NOTE: this method should only be called if a SecurityManager is active
*/ */
private void checkPackageAccess(final ClassLoader ccl, boolean checkProxyInterfaces) { private void checkPackageAccess(SecurityManager sm, final ClassLoader ccl,
final SecurityManager s = System.getSecurityManager(); boolean checkProxyInterfaces) {
if (s != null) { final ClassLoader cl = getClassLoader0();
final ClassLoader cl = getClassLoader0();
if (ReflectUtil.needsPackageAccessCheck(ccl, cl)) { if (ReflectUtil.needsPackageAccessCheck(ccl, cl)) {
String name = this.getName(); String pkg = this.getPackageName();
int i = name.lastIndexOf('.'); if (pkg != null && !pkg.isEmpty()) {
if (i != -1) { // skip the package access check on a proxy class in default proxy package
// skip the package access check on a proxy class in default proxy package if (!Proxy.isProxyClass(this) || ReflectUtil.isNonPublicProxyClass(this)) {
String pkg = name.substring(0, i); sm.checkPackageAccess(pkg);
if (!Proxy.isProxyClass(this) || ReflectUtil.isNonPublicProxyClass(this)) {
s.checkPackageAccess(pkg);
}
} }
} }
// check package access on the proxy interfaces }
if (checkProxyInterfaces && Proxy.isProxyClass(this)) { // check package access on the proxy interfaces
ReflectUtil.checkProxyPackageAccess(ccl, this.getInterfaces()); if (checkProxyInterfaces && Proxy.isProxyClass(this)) {
} ReflectUtil.checkProxyPackageAccess(ccl, this.getInterfaces());
} }
} }
@ -2755,11 +2786,9 @@ public final class Class<T> implements java.io.Serializable,
while (c.isArray()) { while (c.isArray()) {
c = c.getComponentType(); c = c.getComponentType();
} }
String baseName = c.getName(); String baseName = c.getPackageName();
int index = baseName.lastIndexOf('.'); if (baseName != null && !baseName.isEmpty()) {
if (index != -1) { name = baseName.replace('.', '/') + "/" + name;
name = baseName.substring(0, index).replace('.', '/')
+"/"+name;
} }
} else { } else {
name = name.substring(1); name = name.substring(1);
@ -3233,7 +3262,7 @@ public final class Class<T> implements java.io.Serializable,
return constructor; return constructor;
} }
} }
throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes)); throw new NoSuchMethodException(methodToString("<init>", parameterTypes));
} }
// //
@ -3294,8 +3323,11 @@ public final class Class<T> implements java.io.Serializable,
private native Constructor<T>[] getDeclaredConstructors0(boolean publicOnly); private native Constructor<T>[] getDeclaredConstructors0(boolean publicOnly);
private native Class<?>[] getDeclaredClasses0(); private native Class<?>[] getDeclaredClasses0();
private static String argumentTypesToString(Class<?>[] argTypes) { /**
StringJoiner sj = new StringJoiner(", ", "(", ")"); * Helper method to get the method name from arguments.
*/
private String methodToString(String name, Class<?>[] argTypes) {
StringJoiner sj = new StringJoiner(", ", getName() + "." + name + "(", ")");
if (argTypes != null) { if (argTypes != null) {
for (int i = 0; i < argTypes.length; i++) { for (int i = 0; i < argTypes.length; i++) {
Class<?> c = argTypes[i]; Class<?> c = argTypes[i];

View File

@ -28,6 +28,8 @@ package java.lang.invoke;
import static java.lang.invoke.MethodHandleStatics.*; import static java.lang.invoke.MethodHandleStatics.*;
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
import jdk.internal.vm.annotation.Stable;
/** /**
* A {@code CallSite} is a holder for a variable {@link MethodHandle}, * A {@code CallSite} is a holder for a variable {@link MethodHandle},
* which is called its {@code target}. * which is called its {@code target}.
@ -215,19 +217,36 @@ public class CallSite {
public abstract MethodHandle dynamicInvoker(); public abstract MethodHandle dynamicInvoker();
/*non-public*/ MethodHandle makeDynamicInvoker() { /*non-public*/ MethodHandle makeDynamicInvoker() {
MethodHandle getTarget = GET_TARGET.bindArgumentL(0, this); MethodHandle getTarget = getTargetHandle().bindArgumentL(0, this);
MethodHandle invoker = MethodHandles.exactInvoker(this.type()); MethodHandle invoker = MethodHandles.exactInvoker(this.type());
return MethodHandles.foldArguments(invoker, getTarget); return MethodHandles.foldArguments(invoker, getTarget);
} }
private static final MethodHandle GET_TARGET; private static @Stable MethodHandle GET_TARGET;
private static final MethodHandle THROW_UCS; private static MethodHandle getTargetHandle() {
static { MethodHandle handle = GET_TARGET;
if (handle != null) {
return handle;
}
try { try {
GET_TARGET = IMPL_LOOKUP. return GET_TARGET = IMPL_LOOKUP.
findVirtual(CallSite.class, "getTarget", MethodType.methodType(MethodHandle.class)); findVirtual(CallSite.class, "getTarget",
THROW_UCS = IMPL_LOOKUP. MethodType.methodType(MethodHandle.class));
findStatic(CallSite.class, "uninitializedCallSite", MethodType.methodType(Object.class, Object[].class)); } catch (ReflectiveOperationException e) {
throw newInternalError(e);
}
}
private static @Stable MethodHandle THROW_UCS;
private static MethodHandle uninitializedCallSiteHandle() {
MethodHandle handle = THROW_UCS;
if (handle != null) {
return handle;
}
try {
return THROW_UCS = IMPL_LOOKUP.
findStatic(CallSite.class, "uninitializedCallSite",
MethodType.methodType(Object.class, Object[].class));
} catch (ReflectiveOperationException e) { } catch (ReflectiveOperationException e) {
throw newInternalError(e); throw newInternalError(e);
} }
@ -242,7 +261,7 @@ public class CallSite {
MethodType basicType = targetType.basicType(); MethodType basicType = targetType.basicType();
MethodHandle invoker = basicType.form().cachedMethodHandle(MethodTypeForm.MH_UNINIT_CS); MethodHandle invoker = basicType.form().cachedMethodHandle(MethodTypeForm.MH_UNINIT_CS);
if (invoker == null) { if (invoker == null) {
invoker = THROW_UCS.asType(basicType); invoker = uninitializedCallSiteHandle().asType(basicType);
invoker = basicType.form().setCachedMethodHandle(MethodTypeForm.MH_UNINIT_CS, invoker); invoker = basicType.form().setCachedMethodHandle(MethodTypeForm.MH_UNINIT_CS, invoker);
} }
// unchecked view is OK since no values will be received or returned // unchecked view is OK since no values will be received or returned
@ -250,12 +269,16 @@ public class CallSite {
} }
// unsafe stuff: // unsafe stuff:
private static final long TARGET_OFFSET; private static @Stable long TARGET_OFFSET;
private static final long CONTEXT_OFFSET; private static long getTargetOffset() {
static { long offset = TARGET_OFFSET;
if (offset > 0) {
return offset;
}
try { try {
TARGET_OFFSET = UNSAFE.objectFieldOffset(CallSite.class.getDeclaredField("target")); offset = TARGET_OFFSET = UNSAFE.objectFieldOffset(CallSite.class.getDeclaredField("target"));
CONTEXT_OFFSET = UNSAFE.objectFieldOffset(CallSite.class.getDeclaredField("context")); assert(offset > 0);
return offset;
} catch (Exception ex) { throw newInternalError(ex); } } catch (Exception ex) { throw newInternalError(ex); }
} }
@ -265,7 +288,7 @@ public class CallSite {
} }
/*package-private*/ /*package-private*/
MethodHandle getTargetVolatile() { MethodHandle getTargetVolatile() {
return (MethodHandle) UNSAFE.getObjectVolatile(this, TARGET_OFFSET); return (MethodHandle) UNSAFE.getObjectVolatile(this, getTargetOffset());
} }
/*package-private*/ /*package-private*/
void setTargetVolatile(MethodHandle newTarget) { void setTargetVolatile(MethodHandle newTarget) {
@ -324,7 +347,7 @@ public class CallSite {
final int NON_SPREAD_ARG_COUNT = 3; // (caller, name, type) final int NON_SPREAD_ARG_COUNT = 3; // (caller, name, type)
if (NON_SPREAD_ARG_COUNT + argv.length > MethodType.MAX_MH_ARITY) if (NON_SPREAD_ARG_COUNT + argv.length > MethodType.MAX_MH_ARITY)
throw new BootstrapMethodError("too many bootstrap method arguments"); throw new BootstrapMethodError("too many bootstrap method arguments");
MethodType bsmType = bootstrapMethod.type();
MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argv.length); MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argv.length);
MethodHandle typedBSM = bootstrapMethod.asType(invocationType); MethodHandle typedBSM = bootstrapMethod.asType(invocationType);
MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT); MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT);

View File

@ -1128,7 +1128,7 @@ class MethodType implements java.io.Serializable {
public String toMethodDescriptorString() { public String toMethodDescriptorString() {
String desc = methodDescriptor; String desc = methodDescriptor;
if (desc == null) { if (desc == null) {
desc = BytecodeDescriptor.unparse(this); desc = BytecodeDescriptor.unparseMethod(this.rtype, this.ptypes);
methodDescriptor = desc; methodDescriptor = desc;
} }
return desc; return desc;
@ -1256,7 +1256,7 @@ s.writeObject(this.parameterArray());
private final ReferenceQueue<T> stale; private final ReferenceQueue<T> stale;
public ConcurrentWeakInternSet() { public ConcurrentWeakInternSet() {
this.map = new ConcurrentHashMap<>(); this.map = new ConcurrentHashMap<>(512);
this.stale = new ReferenceQueue<>(); this.stale = new ReferenceQueue<>();
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -2134,7 +2134,7 @@ public abstract class ResourceBundle {
/** /**
* Removes all resource bundles from the cache that have been loaded * Removes all resource bundles from the cache that have been loaded
* by the caller's module using the caller's class loader. * by the caller's module.
* *
* @since 1.6 * @since 1.6
* @see ResourceBundle.Control#getTimeToLive(String,Locale) * @see ResourceBundle.Control#getTimeToLive(String,Locale)
@ -2142,50 +2142,26 @@ public abstract class ResourceBundle {
@CallerSensitive @CallerSensitive
public static final void clearCache() { public static final void clearCache() {
Class<?> caller = Reflection.getCallerClass(); Class<?> caller = Reflection.getCallerClass();
clearCacheImpl(caller.getModule(), caller.getClassLoader()); cacheList.keySet().removeIf(
key -> key.getCallerModule() == caller.getModule()
);
} }
/** /**
* Removes all resource bundles from the cache that have been loaded * Removes all resource bundles from the cache that have been loaded
* by the caller's module using the given class loader. * by the given class loader.
* *
* @param loader the class loader * @param loader the class loader
* @exception NullPointerException if <code>loader</code> is null * @exception NullPointerException if <code>loader</code> is null
* @since 1.6 * @since 1.6
* @see ResourceBundle.Control#getTimeToLive(String,Locale) * @see ResourceBundle.Control#getTimeToLive(String,Locale)
*/ */
@CallerSensitive
public static final void clearCache(ClassLoader loader) { public static final void clearCache(ClassLoader loader) {
Objects.requireNonNull(loader); Objects.requireNonNull(loader);
Class<?> caller = Reflection.getCallerClass();
clearCacheImpl(caller.getModule(), loader);
}
/**
* Removes all resource bundles from the cache that have been loaded by the
* given {@code module}.
*
* @param module the module
* @throws NullPointerException
* if {@code module} is {@code null}
* @throws SecurityException
* if the caller doesn't have the permission to
* {@linkplain Module#getClassLoader() get the class loader}
* of the given {@code module}
* @since 9
* @see ResourceBundle.Control#getTimeToLive(String,Locale)
*/
public static final void clearCache(Module module) {
Objects.requireNonNull(module);
clearCacheImpl(module, module.getClassLoader());
}
private static void clearCacheImpl(Module callerModule, ClassLoader loader) {
cacheList.keySet().removeIf( cacheList.keySet().removeIf(
key -> { key -> {
Module m; Module m;
return key.getCallerModule() == callerModule && return (m = key.getModule()) != null &&
(m = key.getModule()) != null &&
getLoader(m) == loader; getLoader(m) == loader;
} }
); );

View File

@ -96,7 +96,7 @@ public final class ReflectUtil {
final Class<?> declaringClass = m.getDeclaringClass(); final Class<?> declaringClass = m.getDeclaringClass();
checkPackageAccess(declaringClass); privateCheckPackageAccess(sm, declaringClass);
if (Modifier.isPublic(m.getModifiers()) && if (Modifier.isPublic(m.getModifiers()) &&
Modifier.isPublic(declaringClass.getModifiers())) Modifier.isPublic(declaringClass.getModifiers()))
@ -114,9 +114,27 @@ public final class ReflectUtil {
* also check the package access on the proxy interfaces. * also check the package access on the proxy interfaces.
*/ */
public static void checkPackageAccess(Class<?> clazz) { public static void checkPackageAccess(Class<?> clazz) {
checkPackageAccess(clazz.getName()); SecurityManager s = System.getSecurityManager();
if (s != null) {
privateCheckPackageAccess(s, clazz);
}
}
/**
* NOTE: should only be called if a SecurityManager is installed
*/
private static void privateCheckPackageAccess(SecurityManager s, Class<?> clazz) {
while (clazz.isArray()) {
clazz = clazz.getComponentType();
}
String pkg = clazz.getPackageName();
if (pkg != null && !pkg.isEmpty()) {
s.checkPackageAccess(pkg);
}
if (isNonPublicProxyClass(clazz)) { if (isNonPublicProxyClass(clazz)) {
checkProxyPackageAccess(clazz); privateCheckProxyPackageAccess(s, clazz);
} }
} }
@ -195,15 +213,21 @@ public final class ReflectUtil {
public static void checkProxyPackageAccess(Class<?> clazz) { public static void checkProxyPackageAccess(Class<?> clazz) {
SecurityManager s = System.getSecurityManager(); SecurityManager s = System.getSecurityManager();
if (s != null) { if (s != null) {
// check proxy interfaces if the given class is a proxy class privateCheckProxyPackageAccess(s, clazz);
if (Proxy.isProxyClass(clazz)) {
for (Class<?> intf : clazz.getInterfaces()) {
checkPackageAccess(intf);
}
}
} }
} }
/**
* NOTE: should only be called if a SecurityManager is installed
*/
private static void privateCheckProxyPackageAccess(SecurityManager s, Class<?> clazz) {
// check proxy interfaces if the given class is a proxy class
if (Proxy.isProxyClass(clazz)) {
for (Class<?> intf : clazz.getInterfaces()) {
privateCheckPackageAccess(s, intf);
}
}
}
/** /**
* Access check on the interfaces that a proxy class implements and throw * Access check on the interfaces that a proxy class implements and throw
* {@code SecurityException} if it accesses a restricted package from * {@code SecurityException} if it accesses a restricted package from
@ -220,7 +244,7 @@ public final class ReflectUtil {
for (Class<?> intf : interfaces) { for (Class<?> intf : interfaces) {
ClassLoader cl = intf.getClassLoader(); ClassLoader cl = intf.getClassLoader();
if (needsPackageAccessCheck(ccl, cl)) { if (needsPackageAccessCheck(ccl, cl)) {
checkPackageAccess(intf); privateCheckPackageAccess(sm, intf);
} }
} }
} }
@ -236,10 +260,11 @@ public final class ReflectUtil {
* package that bypasses checkPackageAccess. * package that bypasses checkPackageAccess.
*/ */
public static boolean isNonPublicProxyClass(Class<?> cls) { public static boolean isNonPublicProxyClass(Class<?> cls) {
String name = cls.getName(); if (!Proxy.isProxyClass(cls)) {
int i = name.lastIndexOf('.'); return false;
String pkg = (i != -1) ? name.substring(0, i) : ""; }
return Proxy.isProxyClass(cls) && !pkg.startsWith(PROXY_PACKAGE); String pkg = cls.getPackageName();
return pkg == null || !pkg.startsWith(PROXY_PACKAGE);
} }
/** /**
@ -255,7 +280,7 @@ public final class ReflectUtil {
// check if it is a valid proxy instance // check if it is a valid proxy instance
if (proxy == null || !Proxy.isProxyClass(proxy.getClass())) { if (proxy == null || !Proxy.isProxyClass(proxy.getClass())) {
throw new IllegalArgumentException("Not a Proxy instance"); throw new IllegalArgumentException("Not a Proxy instance");
} }
if (Modifier.isStatic(method.getModifiers())) { if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalArgumentException("Can't handle static method"); throw new IllegalArgumentException("Can't handle static method");
} }

View File

@ -165,18 +165,9 @@ public final class DefaultImageBuilder implements ImageBuilder {
throw new PluginException("TargetPlatform attribute is missing for java.base module"); throw new PluginException("TargetPlatform attribute is missing for java.base module");
} }
Path bin = root.resolve(BIN_DIRNAME); checkResourcePool(files);
// check any duplicated resource files Path bin = root.resolve(BIN_DIRNAME);
Map<Path, Set<String>> duplicates = new HashMap<>();
files.entries()
.filter(f -> f.type() != ResourcePoolEntry.Type.CLASS_OR_RESOURCE)
.collect(groupingBy(this::entryToImagePath,
mapping(ResourcePoolEntry::moduleName, toSet())))
.entrySet()
.stream()
.filter(e -> e.getValue().size() > 1)
.forEach(e -> duplicates.put(e.getKey(), e.getValue()));
// write non-classes resource files to the image // write non-classes resource files to the image
files.entries() files.entries()
@ -185,13 +176,8 @@ public final class DefaultImageBuilder implements ImageBuilder {
try { try {
accept(f); accept(f);
} catch (FileAlreadyExistsException e) { } catch (FileAlreadyExistsException e) {
// error for duplicated entries // Should not happen! Duplicates checking already done!
Path path = entryToImagePath(f); throw new AssertionError("Duplicate entry!", e);
UncheckedIOException x =
new UncheckedIOException(path + " duplicated in " +
duplicates.get(path), e);
x.addSuppressed(e);
throw x;
} catch (IOException ioExp) { } catch (IOException ioExp) {
throw new UncheckedIOException(ioExp); throw new UncheckedIOException(ioExp);
} }
@ -242,6 +228,27 @@ public final class DefaultImageBuilder implements ImageBuilder {
} }
} }
private void checkResourcePool(ResourcePool pool) {
// For now, only duplicate resources check. Add more checks here (if any)
checkDuplicateResources(pool);
}
private void checkDuplicateResources(ResourcePool pool) {
// check any duplicated resources
Map<Path, Set<String>> duplicates = new HashMap<>();
pool.entries()
.filter(f -> f.type() != ResourcePoolEntry.Type.CLASS_OR_RESOURCE)
.collect(groupingBy(this::entryToImagePath,
mapping(ResourcePoolEntry::moduleName, toSet())))
.entrySet()
.stream()
.filter(e -> e.getValue().size() > 1)
.forEach(e -> duplicates.put(e.getKey(), e.getValue()));
if (!duplicates.isEmpty()) {
throw new PluginException("Duplicate resources: " + duplicates);
}
}
/** /**
* Generates launcher scripts. * Generates launcher scripts.
* *

View File

@ -24,8 +24,6 @@
*/ */
module jdk.jlink { module jdk.jlink {
exports jdk.tools.jlink.plugin;
requires jdk.internal.opt; requires jdk.internal.opt;
requires jdk.jdeps; requires jdk.jdeps;

View File

@ -135,7 +135,7 @@ class JarFileSystem extends ZipFileSystem {
TreeMap<Integer,IndexNode> map = new TreeMap<>(); TreeMap<Integer,IndexNode> map = new TreeMap<>();
IndexNode child = metaInfVersions.child; IndexNode child = metaInfVersions.child;
while (child != null) { while (child != null) {
Integer key = getVersion(child.name, metaInfVersions.name.length); Integer key = getVersion(child.name, metaInfVersions.name.length + 1);
if (key != null && key <= version) { if (key != null && key <= version) {
map.put(key, child); map.put(key, child);
} }
@ -149,7 +149,7 @@ class JarFileSystem extends ZipFileSystem {
*/ */
private Integer getVersion(byte[] name, int offset) { private Integer getVersion(byte[] name, int offset) {
try { try {
return Integer.parseInt(getString(Arrays.copyOfRange(name, offset, name.length-1))); return Integer.parseInt(getString(Arrays.copyOfRange(name, offset, name.length)));
} catch (NumberFormatException x) { } catch (NumberFormatException x) {
// ignore this even though it might indicate issues with the JAR structure // ignore this even though it might indicate issues with the JAR structure
return null; return null;
@ -176,7 +176,7 @@ class JarFileSystem extends ZipFileSystem {
* returns foo/bar.class * returns foo/bar.class
*/ */
private byte[] getRootName(IndexNode prefix, IndexNode inode) { private byte[] getRootName(IndexNode prefix, IndexNode inode) {
int offset = prefix.name.length - 1; int offset = prefix.name.length;
byte[] fullName = inode.name; byte[] fullName = inode.name;
return Arrays.copyOfRange(fullName, offset, fullName.length); return Arrays.copyOfRange(fullName, offset, fullName.length);
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -34,33 +34,64 @@ import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction; import java.nio.charset.CodingErrorAction;
import java.util.Arrays; import java.util.Arrays;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.nio.charset.StandardCharsets.ISO_8859_1;
/** /**
* Utility class for zipfile name and comment decoding and encoding * Utility class for zipfile name and comment decoding and encoding
* *
* @author Xueming Shen * @author Xueming Shen
*/ */
final class ZipCoder { class ZipCoder {
String toString(byte[] ba, int length) { static class UTF8 extends ZipCoder {
CharsetDecoder cd = decoder().reset(); UTF8() {
int len = (int)(length * cd.maxCharsPerByte()); super(UTF_8);
char[] ca = new char[len]; }
if (len == 0)
return new String(ca); @Override
ByteBuffer bb = ByteBuffer.wrap(ba, 0, length); byte[] getBytes(String s) { // fast pass for ascii
CharBuffer cb = CharBuffer.wrap(ca); for (int i = 0; i < s.length(); i++) {
CoderResult cr = cd.decode(bb, cb, true); if (s.charAt(i) > 0x7f) return super.getBytes(s);
if (!cr.isUnderflow()) }
throw new IllegalArgumentException(cr.toString()); return s.getBytes(ISO_8859_1);
cr = cd.flush(cb); }
if (!cr.isUnderflow())
throw new IllegalArgumentException(cr.toString()); @Override
return new String(ca, 0, cb.position()); String toString(byte[] ba) {
for (byte b : ba) {
if (b < 0) return super.toString(ba);
}
return new String(ba, ISO_8859_1);
}
}
private static ZipCoder utf8 = new UTF8();
public static ZipCoder get(String csn) {
Charset cs = Charset.forName(csn);
if (cs.name().equals("UTF-8")) {
return utf8;
}
return new ZipCoder(cs);
} }
String toString(byte[] ba) { String toString(byte[] ba) {
return toString(ba, ba.length); CharsetDecoder cd = decoder().reset();
int clen = (int)(ba.length * cd.maxCharsPerByte());
char[] ca = new char[clen];
if (clen == 0)
return new String(ca);
ByteBuffer bb = ByteBuffer.wrap(ba, 0, ba.length);
CharBuffer cb = CharBuffer.wrap(ca);
CoderResult cr = cd.decode(bb, cb, true);
if (!cr.isUnderflow())
throw new IllegalArgumentException(cr.toString());
cr = cd.flush(cb);
if (!cr.isUnderflow())
throw new IllegalArgumentException(cr.toString());
return new String(ca, 0, cb.position());
} }
byte[] getBytes(String s) { byte[] getBytes(String s) {
@ -69,62 +100,29 @@ final class ZipCoder {
int len = (int)(ca.length * ce.maxBytesPerChar()); int len = (int)(ca.length * ce.maxBytesPerChar());
byte[] ba = new byte[len]; byte[] ba = new byte[len];
if (len == 0) if (len == 0)
return ba; return ba;
ByteBuffer bb = ByteBuffer.wrap(ba); ByteBuffer bb = ByteBuffer.wrap(ba);
CharBuffer cb = CharBuffer.wrap(ca); CharBuffer cb = CharBuffer.wrap(ca);
CoderResult cr = ce.encode(cb, bb, true); CoderResult cr = ce.encode(cb, bb, true);
if (!cr.isUnderflow()) if (!cr.isUnderflow())
throw new IllegalArgumentException(cr.toString()); throw new IllegalArgumentException(cr.toString());
cr = ce.flush(bb); cr = ce.flush(bb);
if (!cr.isUnderflow()) if (!cr.isUnderflow())
throw new IllegalArgumentException(cr.toString()); throw new IllegalArgumentException(cr.toString());
if (bb.position() == ba.length) // defensive copy? if (bb.position() == ba.length) // defensive copy?
return ba; return ba;
else else
return Arrays.copyOf(ba, bb.position()); return Arrays.copyOf(ba, bb.position());
}
// assume invoked only if "this" is not utf8
byte[] getBytesUTF8(String s) {
if (isutf8)
return getBytes(s);
if (utf8 == null)
utf8 = new ZipCoder(Charset.forName("UTF-8"));
return utf8.getBytes(s);
}
String toStringUTF8(byte[] ba, int len) {
if (isutf8)
return toString(ba, len);
if (utf8 == null)
utf8 = new ZipCoder(Charset.forName("UTF-8"));
return utf8.toString(ba, len);
} }
boolean isUTF8() { boolean isUTF8() {
return isutf8; return cs == UTF_8;
} }
private Charset cs; private Charset cs;
private boolean isutf8;
private ZipCoder utf8;
private ZipCoder(Charset cs) { private ZipCoder(Charset cs) {
this.cs = cs; this.cs = cs;
this.isutf8 = cs.name().equals("UTF-8");
}
static ZipCoder get(Charset charset) {
return new ZipCoder(charset);
}
static ZipCoder get(String csn) {
try {
return new ZipCoder(Charset.forName(csn));
} catch (Throwable t) {
t.printStackTrace();
}
return new ZipCoder(Charset.defaultCharset());
} }
private final ThreadLocal<CharsetDecoder> decTL = new ThreadLocal<>(); private final ThreadLocal<CharsetDecoder> decTL = new ThreadLocal<>();
@ -133,10 +131,10 @@ final class ZipCoder {
private CharsetDecoder decoder() { private CharsetDecoder decoder() {
CharsetDecoder dec = decTL.get(); CharsetDecoder dec = decTL.get();
if (dec == null) { if (dec == null) {
dec = cs.newDecoder() dec = cs.newDecoder()
.onMalformedInput(CodingErrorAction.REPORT) .onMalformedInput(CodingErrorAction.REPORT)
.onUnmappableCharacter(CodingErrorAction.REPORT); .onUnmappableCharacter(CodingErrorAction.REPORT);
decTL.set(dec); decTL.set(dec);
} }
return dec; return dec;
} }
@ -144,10 +142,10 @@ final class ZipCoder {
private CharsetEncoder encoder() { private CharsetEncoder encoder() {
CharsetEncoder enc = encTL.get(); CharsetEncoder enc = encTL.get();
if (enc == null) { if (enc == null) {
enc = cs.newEncoder() enc = cs.newEncoder()
.onMalformedInput(CodingErrorAction.REPORT) .onMalformedInput(CodingErrorAction.REPORT)
.onUnmappableCharacter(CodingErrorAction.REPORT); .onUnmappableCharacter(CodingErrorAction.REPORT);
encTL.set(enc); encTL.set(enc);
} }
return enc; return enc;
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -314,8 +314,8 @@ class ZipFileSystem extends FileSystem {
IndexNode inode = getInode(path); IndexNode inode = getInode(path);
if (inode == null) if (inode == null)
return null; return null;
e = new Entry(inode.name); // pseudo directory e = new Entry(inode.name, inode.isdir); // pseudo directory
e.method = METHOD_STORED; // STORED for dir e.method = METHOD_STORED; // STORED for dir
e.mtime = e.atime = e.ctime = zfsDefaultTimeStamp; e.mtime = e.atime = e.ctime = zfsDefaultTimeStamp;
} }
} finally { } finally {
@ -400,7 +400,8 @@ class ZipFileSystem extends FileSystem {
List<Path> list = new ArrayList<>(); List<Path> list = new ArrayList<>();
IndexNode child = inode.child; IndexNode child = inode.child;
while (child != null) { while (child != null) {
ZipPath zp = new ZipPath(this, child.name); // assume all path from zip file itself is "normalized"
ZipPath zp = new ZipPath(this, child.name, true);
if (filter == null || filter.accept(zp)) if (filter == null || filter.accept(zp))
list.add(zp); list.add(zp);
child = child.sibling; child = child.sibling;
@ -415,14 +416,14 @@ class ZipFileSystem extends FileSystem {
throws IOException throws IOException
{ {
checkWritable(); checkWritable();
dir = toDirectoryPath(dir); // dir = toDirectoryPath(dir);
beginWrite(); beginWrite();
try { try {
ensureOpen(); ensureOpen();
if (dir.length == 0 || exists(dir)) // root dir, or exiting dir if (dir.length == 0 || exists(dir)) // root dir, or exiting dir
throw new FileAlreadyExistsException(getString(dir)); throw new FileAlreadyExistsException(getString(dir));
checkParents(dir); checkParents(dir);
Entry e = new Entry(dir, Entry.NEW); Entry e = new Entry(dir, Entry.NEW, true);
e.method = METHOD_STORED; // STORED for dir e.method = METHOD_STORED; // STORED for dir
update(e); update(e);
} finally { } finally {
@ -463,7 +464,7 @@ class ZipFileSystem extends FileSystem {
} else { } else {
checkParents(dst); checkParents(dst);
} }
Entry u = new Entry(eSrc, Entry.COPY); // copy eSrc entry Entry u = new Entry(eSrc, Entry.COPY); // copy eSrc entry
u.name(dst); // change name u.name(dst); // change name
if (eSrc.type == Entry.NEW || eSrc.type == Entry.FILECH) if (eSrc.type == Entry.NEW || eSrc.type == Entry.FILECH)
{ {
@ -533,7 +534,7 @@ class ZipFileSystem extends FileSystem {
if (!hasCreate && !hasCreateNew) if (!hasCreate && !hasCreateNew)
throw new NoSuchFileException(getString(path)); throw new NoSuchFileException(getString(path));
checkParents(path); checkParents(path);
return getOutputStream(new Entry(path, Entry.NEW)); return getOutputStream(new Entry(path, Entry.NEW, false));
} }
} finally { } finally {
endRead(); endRead();
@ -887,7 +888,7 @@ class ZipFileSystem extends FileSystem {
int off = getParentOff(path); int off = getParentOff(path);
if (off <= 1) if (off <= 1)
return ROOTPATH; return ROOTPATH;
return Arrays.copyOf(path, off + 1); return Arrays.copyOf(path, off);
} }
private static int getParentOff(byte[] path) { private static int getParentOff(byte[] path) {
@ -1075,11 +1076,9 @@ class ZipFileSystem extends FileSystem {
if (pos + CENHDR + nlen > limit) { if (pos + CENHDR + nlen > limit) {
zerror("invalid CEN header (bad header size)"); zerror("invalid CEN header (bad header size)");
} }
byte[] name = new byte[nlen + 1]; IndexNode inode = new IndexNode(cen, pos + CENHDR, nlen, pos);
System.arraycopy(cen, pos + CENHDR, name, 1, nlen);
name[0] = '/';
IndexNode inode = new IndexNode(name, pos);
inodes.put(inode, inode); inodes.put(inode, inode);
// skip ext and comment // skip ext and comment
pos += (CENHDR + nlen + elen + clen); pos += (CENHDR + nlen + elen + clen);
} }
@ -1112,7 +1111,7 @@ class ZipFileSystem extends FileSystem {
private boolean hasUpdate = false; private boolean hasUpdate = false;
// shared key. consumer guarantees the "writeLock" before use it. // shared key. consumer guarantees the "writeLock" before use it.
private final IndexNode LOOKUPKEY = IndexNode.keyOf(null); private final IndexNode LOOKUPKEY = new IndexNode(null, -1);
private void updateDelete(IndexNode inode) { private void updateDelete(IndexNode inode) {
beginWrite(); beginWrite();
@ -1312,16 +1311,7 @@ class ZipFileSystem extends FileSystem {
IndexNode getInode(byte[] path) { IndexNode getInode(byte[] path) {
if (path == null) if (path == null)
throw new NullPointerException("path"); throw new NullPointerException("path");
IndexNode key = IndexNode.keyOf(path); return inodes.get(IndexNode.keyOf(path));
IndexNode inode = inodes.get(key);
if (inode == null &&
(path.length == 0 || path[path.length -1] != '/')) {
// if does not ends with a slash
path = Arrays.copyOf(path, path.length + 1);
path[path.length - 1] = '/';
inode = inodes.get(key.as(path));
}
return inode;
} }
Entry getEntry(byte[] path) throws IOException { Entry getEntry(byte[] path) throws IOException {
@ -1782,8 +1772,11 @@ class ZipFileSystem extends FileSystem {
int hashcode; // node is hashable/hashed by its name int hashcode; // node is hashable/hashed by its name
int pos = -1; // position in cen table, -1 menas the int pos = -1; // position in cen table, -1 menas the
// entry does not exists in zip file // entry does not exists in zip file
IndexNode(byte[] name) { boolean isdir;
IndexNode(byte[] name, boolean isdir) {
name(name); name(name);
this.isdir = isdir;
this.pos = -1; this.pos = -1;
} }
@ -1792,8 +1785,28 @@ class ZipFileSystem extends FileSystem {
this.pos = pos; this.pos = pos;
} }
// constructor for cenInit()
IndexNode(byte[] cen, int noff, int nlen, int pos) {
if (cen[noff + nlen - 1] == '/') {
isdir = true;
nlen--;
}
name = new byte[nlen + 1];
System.arraycopy(cen, pos + CENHDR, name, 1, nlen);
name[0] = '/';
name(name);
this.pos = pos;
}
private static final ThreadLocal<IndexNode> cachedKey = new ThreadLocal<>();
final static IndexNode keyOf(byte[] name) { // get a lookup key; final static IndexNode keyOf(byte[] name) { // get a lookup key;
return new IndexNode(name, -1); IndexNode key = cachedKey.get();
if (key == null) {
key = new IndexNode(name, -1);
cachedKey.set(key);
}
return key.as(name);
} }
final void name(byte[] name) { final void name(byte[] name) {
@ -1807,8 +1820,7 @@ class ZipFileSystem extends FileSystem {
} }
boolean isDir() { boolean isDir() {
return name != null && return isdir;
(name.length == 0 || name[name.length - 1] == '/');
} }
public boolean equals(Object other) { public boolean equals(Object other) {
@ -1865,8 +1877,9 @@ class ZipFileSystem extends FileSystem {
Entry() {} Entry() {}
Entry(byte[] name) { Entry(byte[] name, boolean isdir) {
name(name); name(name);
this.isdir = isdir;
this.mtime = this.ctime = this.atime = System.currentTimeMillis(); this.mtime = this.ctime = this.atime = System.currentTimeMillis();
this.crc = 0; this.crc = 0;
this.size = 0; this.size = 0;
@ -1874,13 +1887,14 @@ class ZipFileSystem extends FileSystem {
this.method = METHOD_DEFLATED; this.method = METHOD_DEFLATED;
} }
Entry(byte[] name, int type) { Entry(byte[] name, int type, boolean isdir) {
this(name); this(name, isdir);
this.type = type; this.type = type;
} }
Entry (Entry e, int type) { Entry (Entry e, int type) {
name(e.name); name(e.name);
this.isdir = e.isdir;
this.version = e.version; this.version = e.version;
this.ctime = e.ctime; this.ctime = e.ctime;
this.atime = e.atime; this.atime = e.atime;
@ -1902,7 +1916,7 @@ class ZipFileSystem extends FileSystem {
} }
Entry (byte[] name, Path file, int type) { Entry (byte[] name, Path file, int type) {
this(name, type); this(name, type, false);
this.file = file; this.file = file;
this.method = METHOD_STORED; this.method = METHOD_STORED;
} }
@ -1948,6 +1962,7 @@ class ZipFileSystem extends FileSystem {
locoff = CENOFF(cen, pos); locoff = CENOFF(cen, pos);
pos += CENHDR; pos += CENHDR;
this.name = inode.name; this.name = inode.name;
this.isdir = inode.isdir;
this.hashcode = inode.hashcode; this.hashcode = inode.hashcode;
pos += nlen; pos += nlen;
@ -1974,9 +1989,10 @@ class ZipFileSystem extends FileSystem {
int elenEXTT = 0; // extra for Extended Timestamp int elenEXTT = 0; // extra for Extended Timestamp
boolean foundExtraTime = false; // if time stamp NTFS, EXTT present boolean foundExtraTime = false; // if time stamp NTFS, EXTT present
// confirm size/length byte[] zname = isdir ? toDirectoryPath(name) : name;
int nlen = (name != null) ? name.length - 1 : 0; // name has [0] as "slash" // confirm size/length
int nlen = (zname != null) ? zname.length - 1 : 0; // name has [0] as "slash"
int elen = (extra != null) ? extra.length : 0; int elen = (extra != null) ? extra.length : 0;
int eoff = 0; int eoff = 0;
int clen = (comment != null) ? comment.length : 0; int clen = (comment != null) ? comment.length : 0;
@ -2037,7 +2053,7 @@ class ZipFileSystem extends FileSystem {
writeShort(os, 0); // internal file attributes (unused) writeShort(os, 0); // internal file attributes (unused)
writeInt(os, 0); // external file attributes (unused) writeInt(os, 0); // external file attributes (unused)
writeInt(os, locoff0); // relative offset of local header writeInt(os, locoff0); // relative offset of local header
writeBytes(os, name, 1, nlen); writeBytes(os, zname, 1, nlen);
if (elen64 != 0) { if (elen64 != 0) {
writeShort(os, EXTID_ZIP64);// Zip64 extra writeShort(os, EXTID_ZIP64);// Zip64 extra
writeShort(os, elen64 - 4); // size of "this" extra block writeShort(os, elen64 - 4); // size of "this" extra block
@ -2075,87 +2091,13 @@ class ZipFileSystem extends FileSystem {
} }
///////////////////// LOC ////////////////////// ///////////////////// LOC //////////////////////
static Entry readLOC(ZipFileSystem zipfs, long pos)
throws IOException
{
return readLOC(zipfs, pos, new byte[1024]);
}
static Entry readLOC(ZipFileSystem zipfs, long pos, byte[] buf)
throws IOException
{
return new Entry().loc(zipfs, pos, buf);
}
Entry loc(ZipFileSystem zipfs, long pos, byte[] buf)
throws IOException
{
assert (buf.length >= LOCHDR);
if (zipfs.readFullyAt(buf, 0, LOCHDR , pos) != LOCHDR)
throw new ZipException("loc: reading failed");
if (!locSigAt(buf, 0))
throw new ZipException("loc: wrong sig ->"
+ Long.toString(getSig(buf, 0), 16));
//startPos = pos;
version = LOCVER(buf);
flag = LOCFLG(buf);
method = LOCHOW(buf);
mtime = dosToJavaTime(LOCTIM(buf));
crc = LOCCRC(buf);
csize = LOCSIZ(buf);
size = LOCLEN(buf);
int nlen = LOCNAM(buf);
int elen = LOCEXT(buf);
name = new byte[nlen + 1];
name[0] = '/';
if (zipfs.readFullyAt(name, 1, nlen, pos + LOCHDR) != nlen) {
throw new ZipException("loc: name reading failed");
}
if (elen > 0) {
extra = new byte[elen];
if (zipfs.readFullyAt(extra, 0, elen, pos + LOCHDR + nlen)
!= elen) {
throw new ZipException("loc: ext reading failed");
}
}
pos += (LOCHDR + nlen + elen);
if ((flag & FLAG_DATADESCR) != 0) {
// Data Descriptor
Entry e = zipfs.getEntry(name); // get the size/csize from cen
if (e == null)
throw new ZipException("loc: name not found in cen");
size = e.size;
csize = e.csize;
pos += (method == METHOD_STORED ? size : csize);
if (size >= ZIP64_MINVAL || csize >= ZIP64_MINVAL)
pos += 24;
else
pos += 16;
} else {
if (extra != null &&
(size == ZIP64_MINVAL || csize == ZIP64_MINVAL)) {
// zip64 ext: must include both size and csize
int off = 0;
while (off + 20 < elen) { // HeaderID+DataSize+Data
int sz = SH(extra, off + 2);
if (SH(extra, off) == EXTID_ZIP64 && sz == 16) {
size = LL(extra, off + 4);
csize = LL(extra, off + 12);
break;
}
off += (sz + 4);
}
}
pos += (method == METHOD_STORED ? size : csize);
}
return this;
}
int writeLOC(OutputStream os) throws IOException { int writeLOC(OutputStream os) throws IOException {
writeInt(os, LOCSIG); // LOC header signature writeInt(os, LOCSIG); // LOC header signature
int version = version(); int version = version();
int nlen = (name != null) ? name.length - 1 : 0; // [0] is slash
byte[] zname = isdir ? toDirectoryPath(name) : name;
int nlen = (zname != null) ? zname.length - 1 : 0; // [0] is slash
int elen = (extra != null) ? extra.length : 0; int elen = (extra != null) ? extra.length : 0;
boolean foundExtraTime = false; // if extra timestamp present boolean foundExtraTime = false; // if extra timestamp present
int eoff = 0; int eoff = 0;
@ -2214,7 +2156,7 @@ class ZipFileSystem extends FileSystem {
} }
writeShort(os, nlen); writeShort(os, nlen);
writeShort(os, elen + elen64 + elenNTFS + elenEXTT); writeShort(os, elen + elen64 + elenNTFS + elenEXTT);
writeBytes(os, name, 1, nlen); writeBytes(os, zname, 1, nlen);
if (elen64 != 0) { if (elen64 != 0) {
writeShort(os, EXTID_ZIP64); writeShort(os, EXTID_ZIP64);
writeShort(os, 16); writeShort(os, 16);
@ -2551,7 +2493,7 @@ class ZipFileSystem extends FileSystem {
private void buildNodeTree() throws IOException { private void buildNodeTree() throws IOException {
beginWrite(); beginWrite();
try { try {
IndexNode root = new IndexNode(ROOTPATH); IndexNode root = new IndexNode(ROOTPATH, true);
IndexNode[] nodes = inodes.keySet().toArray(new IndexNode[0]); IndexNode[] nodes = inodes.keySet().toArray(new IndexNode[0]);
inodes.put(root, root); inodes.put(root, root);
ParentLookup lookup = new ParentLookup(); ParentLookup lookup = new ParentLookup();
@ -2564,7 +2506,7 @@ class ZipFileSystem extends FileSystem {
root.child = node; root.child = node;
break; break;
} }
lookup = lookup.as(node.name, off + 1); lookup = lookup.as(node.name, off);
if (inodes.containsKey(lookup)) { if (inodes.containsKey(lookup)) {
parent = inodes.get(lookup); parent = inodes.get(lookup);
node.sibling = parent.child; node.sibling = parent.child;
@ -2572,7 +2514,7 @@ class ZipFileSystem extends FileSystem {
break; break;
} }
// add new pseudo directory entry // add new pseudo directory entry
parent = new IndexNode(Arrays.copyOf(node.name, off + 1)); parent = new IndexNode(Arrays.copyOf(node.name, off), true);
inodes.put(parent, parent); inodes.put(parent, parent);
node.sibling = parent.child; node.sibling = parent.child;
parent.child = node; parent.child = node;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -59,8 +59,7 @@ final class ZipPath implements Path {
} else { } else {
if (zfs.zc.isUTF8()) { if (zfs.zc.isUTF8()) {
this.path = normalize(path); this.path = normalize(path);
} else { } else { // see normalize(String);
// see normalize(String);
this.path = normalize(zfs.getString(path)); this.path = normalize(zfs.getString(path));
} }
} }
@ -68,12 +67,7 @@ final class ZipPath implements Path {
ZipPath(ZipFileSystem zfs, String path) { ZipPath(ZipFileSystem zfs, String path) {
this.zfs = zfs; this.zfs = zfs;
if (zfs.zc.isUTF8()) { this.path = normalize(path);
this.path = normalize(zfs.getBytes(path));
} else {
// see normalize(String);
this.path = normalize(path);
}
} }
@Override @Override
@ -84,33 +78,31 @@ final class ZipPath implements Path {
return null; return null;
} }
@Override @Override
public Path getFileName() { public Path getFileName() {
initOffsets(); int off = path.length;
int count = offsets.length; if (off == 0 || off == 1 && path[0] == '/')
if (count == 0) return null;
return null; // no elements so no name while (--off >= 0 && path[off] != '/') {}
if (count == 1 && path[0] != '/') if (off < 0)
return this; return this;
int lastOffset = offsets[count-1]; off++;
int len = path.length - lastOffset; byte[] result = new byte[path.length - off];
byte[] result = new byte[len]; System.arraycopy(path, off, result, 0, result.length);
System.arraycopy(path, lastOffset, result, 0, len); return new ZipPath(getFileSystem(), result, true);
return new ZipPath(zfs, result);
} }
@Override @Override
public ZipPath getParent() { public ZipPath getParent() {
initOffsets(); int off = path.length;
int count = offsets.length; if (off == 0 || off == 1 && path[0] == '/')
if (count == 0) // no elements so no parent
return null; return null;
int len = offsets[count-1] - 1; while (--off >= 0 && path[off] != '/') {}
if (len <= 0) // parent is root only (may be null) if (off <= 0)
return getRoot(); return getRoot();
byte[] result = new byte[len]; byte[] result = new byte[off];
System.arraycopy(path, 0, result, 0, len); System.arraycopy(path, 0, result, 0, off);
return new ZipPath(zfs, result); return new ZipPath(getFileSystem(), result, true);
} }
@Override @Override
@ -277,30 +269,36 @@ final class ZipPath implements Path {
@Override @Override
public boolean isAbsolute() { public boolean isAbsolute() {
return (this.path.length > 0 && path[0] == '/'); return path.length > 0 && path[0] == '/';
} }
@Override @Override
public ZipPath resolve(Path other) { public ZipPath resolve(Path other) {
final ZipPath o = checkPath(other); ZipPath o = checkPath(other);
int tlen = this.path.length; if (o.path.length == 0)
if (tlen == 0 || o.isAbsolute())
return o;
int olen = o.path.length;
if (olen == 0)
return this; return this;
if (o.isAbsolute() || this.path.length == 0)
return o;
return resolve(o.path);
}
// opath is normalized, just concat
private ZipPath resolve(byte[] opath) {
byte[] resolved = null; byte[] resolved = null;
if (this.path[tlen - 1] == '/') { byte[] tpath = this.path;
int tlen = tpath.length;
int olen = opath.length;
if (path[tlen - 1] == '/') {
resolved = new byte[tlen + olen]; resolved = new byte[tlen + olen];
System.arraycopy(path, 0, resolved, 0, tlen); System.arraycopy(tpath, 0, resolved, 0, tlen);
System.arraycopy(o.path, 0, resolved, tlen, olen); System.arraycopy(opath, 0, resolved, tlen, olen);
} else { } else {
resolved = new byte[tlen + 1 + olen]; resolved = new byte[tlen + 1 + olen];
System.arraycopy(path, 0, resolved, 0, tlen); System.arraycopy(tpath, 0, resolved, 0, tlen);
resolved[tlen] = '/'; resolved[tlen] = '/';
System.arraycopy(o.path, 0, resolved, tlen + 1, olen); System.arraycopy(opath, 0, resolved, tlen + 1, olen);
} }
return new ZipPath(zfs, resolved); return new ZipPath(zfs, resolved, true);
} }
@Override @Override
@ -351,7 +349,12 @@ final class ZipPath implements Path {
@Override @Override
public ZipPath resolve(String other) { public ZipPath resolve(String other) {
return resolve(zfs.getPath(other)); byte[] opath = normalize(other);
if (opath.length == 0)
return this;
if (opath[0] == '/' || this.path.length == 0)
return new ZipPath(zfs, opath, true);
return resolve(opath);
} }
@Override @Override
@ -455,8 +458,9 @@ final class ZipPath implements Path {
return normalize(path, i - 1); return normalize(path, i - 1);
prevC = c; prevC = c;
} }
if (len > 1 && prevC == '/') if (len > 1 && prevC == '/') {
return Arrays.copyOf(path, len - 1); return Arrays.copyOf(path, len - 1);
}
return path; return path;
} }
@ -490,6 +494,8 @@ final class ZipPath implements Path {
// to avoid incorrectly normalizing byte '0x5c' (as '\') // to avoid incorrectly normalizing byte '0x5c' (as '\')
// to '/'. // to '/'.
private byte[] normalize(String path) { private byte[] normalize(String path) {
if (zfs.zc.isUTF8())
return normalize(zfs.getBytes(path));
int len = path.length(); int len = path.length();
if (len == 0) if (len == 0)
return new byte[0]; return new byte[0];
@ -533,7 +539,8 @@ final class ZipPath implements Path {
// Remove DotSlash(./) and resolve DotDot (..) components // Remove DotSlash(./) and resolve DotDot (..) components
private byte[] getResolved() { private byte[] getResolved() {
for (int i = 0; i < path.length; i++) { for (int i = 0; i < path.length; i++) {
if (path[i] == (byte)'.') { if (path[i] == (byte)'.' &&
(i + 1 == path.length || path[i + 1] == '/')) {
return resolve0(); return resolve0();
} }
} }
@ -976,5 +983,4 @@ final class ZipPath implements Path {
} }
return sb.toString(); return sb.toString();
} }
} }

View File

@ -258,6 +258,8 @@ tools/jimage/JImageVerifyTest.java 8169713 generic-
tools/jlink/multireleasejar/JLinkMultiReleaseJarTest.java 8169971 windows-x64 tools/jlink/multireleasejar/JLinkMultiReleaseJarTest.java 8169971 windows-x64
tools/jlink/CustomPluginTest.java 8172864 generic-all
############################################################################ ############################################################################
# jdk_jdi # jdk_jdi

View File

@ -26,8 +26,8 @@ groups=TEST.groups [closed/TEST.groups]
# Allow querying of various System properties in @requires clauses # Allow querying of various System properties in @requires clauses
requires.properties=sun.arch.data.model java.runtime.name requires.properties=sun.arch.data.model java.runtime.name
# Tests using jtreg 4.2 b04 features # Tests using jtreg 4.2 b05 features
requiredVersion=4.2 b04 requiredVersion=4.2 b05
# Path to libraries in the topmost test directory. This is needed so @library # Path to libraries in the topmost test directory. This is needed so @library
# does not need ../../ notation to reach them # does not need ../../ notation to reach them

View File

@ -0,0 +1,87 @@
/*
* 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 java.nio.file.Paths;
import java.util.logging.Handler;
import java.util.logging.LogManager;
import java.util.logging.Logger;
/**
* @test
* @bug 8172886
* @summary Verifies that a custom LogManager or custom Handler can be
* instantiated by the logging system if they are in a package
* that is exported to java.logging by a module.
* @build test.logmanager/test.logmanager.TestLogManager
* test.handlers/test.handlers.TestHandler
* test.config/test.config.LogConfig
* LogManagerInModuleTest
* @run main/othervm --add-modules test.logmanager,test.handlers
* -Djava.util.logging.manager=test.logmanager.TestLogManager
* LogManagerInModuleTest
* @run main/othervm --add-modules test.logmanager,test.handlers,test.config
* -Djava.util.logging.manager=test.logmanager.TestLogManager
* -Djava.util.logging.config.class=test.config.LogConfig
* LogManagerInModuleTest
*
* @author danielfuchs
*/
public class LogManagerInModuleTest {
public static void main(String[] args) throws Exception {
if (System.getProperty("java.util.logging.config.class", null) == null) {
System.setProperty("java.util.logging.config.file",
Paths.get(System.getProperty("test.src", "src"),
"logging.properties").toString());
}
// sanity check
if (LogManagerInModuleTest.class.getModule().isNamed()) {
throw new RuntimeException("Unexpected named module for "
+ LogManagerInModuleTest.class + ": "
+ LogManagerInModuleTest.class.getModule().getName());
}
// now check that the LogManager was correctly instantiated.
LogManager manager = LogManager.getLogManager();
System.out.println("LogManager: " + manager);
Class<?> logManagerClass = manager.getClass();
if (!"test.logmanager".equals(logManagerClass.getModule().getName())) {
throw new RuntimeException("Bad module for log manager: "
+ logManagerClass.getModule() + "; class is: "
+ logManagerClass.getName());
}
Logger logger = Logger.getLogger("com.xyz.foo");
Handler[] handlers = logger.getHandlers();
if (handlers.length != 1) {
throw new RuntimeException("Expected 1 handler, found " + handlers.length);
}
Class<?> handlerClass = handlers[0].getClass();
if (!"test.handlers".equals(handlerClass.getModule().getName())) {
throw new RuntimeException("Bad module for handler: "
+ handlerClass.getModule() + "; class is: "
+ handlerClass.getName());
}
}
}

View File

@ -0,0 +1,56 @@
############################################################
# Global properties
############################################################
# "handlers" specifies a comma separated list of log Handler
# classes. These handlers will be installed during VM startup.
# Note that these classes must be on the system classpath.
# By default we only configure a ConsoleHandler, which will only
# show messages at the INFO and above levels.
handlers= java.util.logging.ConsoleHandler
# To also add the FileHandler, use the following line instead.
#handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler
# Default global logging level.
# This specifies which kinds of events are logged across
# all loggers. For any given facility this global level
# can be overriden by a facility specific level
# Note that the ConsoleHandler also has a separate level
# setting to limit messages printed to the console.
.level= INFO
############################################################
# Handler specific properties.
# Describes specific configuration info for Handlers.
############################################################
# default file output is in user's home directory.
java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
# Default number of locks FileHandler can obtain synchronously.
# This specifies maximum number of attempts to obtain lock file by FileHandler
# implemented by incrementing the unique field %u as per FileHandler API documentation.
java.util.logging.FileHandler.maxLocks = 100
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
# Limit the message that are printed on the console to INFO and above.
java.util.logging.ConsoleHandler.level = INFO
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
# Example to customize the SimpleFormatter output format
# to print one-line log message like this:
# <level>: <log message> [<date/time>]
#
# java.util.logging.SimpleFormatter.format=%4$s: %5$s [%1$tc]%n
############################################################
# Facility specific properties.
# Provides extra control for each logger.
############################################################
# For example, set the com.xyz.foo logger to only log SEVERE
# messages:
com.xyz.foo.level = SEVERE
com.xyz.foo.handlers = test.handlers.TestHandler

View 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.
*/
module test.config {
requires java.logging;
requires test.handlers;
// makes it possible for java.logging to instantiate test.config.LogConfig;
// this doesn't need to be a qualified export, but making it so will prevent
// any other module from being able to instantiate the provided classes.
exports test.config to java.logging;
}

View File

@ -0,0 +1,51 @@
/*
* 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 test.config;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import test.handlers.TestHandler;
/**
* A dummy class that configures the logging system.
* @author danielfuchs
*/
public class LogConfig {
private static final List<Logger> LOGGERS = new ArrayList<>();
public LogConfig() {
LogManager manager = LogManager.getLogManager();
Logger logger = Logger.getLogger("com.xyz.foo");
if (logger.getHandlers().length > 0) {
System.err.println(this.getClass().getName() + ": "
+ "Unexpected handlers: "
+ List.of(logger.getHandlers()));
throw new RuntimeException("Unexpected handlers: "
+ List.of(logger.getHandlers()));
}
logger.addHandler(new TestHandler());
LOGGERS.add(logger);
}
}

View 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.
*/
module test.handlers {
requires transitive java.logging;
// makes it possible for java.logging and test.config to instantiate
// test.handlers.TestHandler;
// this doesn't need to be a qualified export, but making it so will prevent
// any other module from being able to instantiate the provided classes.
exports test.handlers to java.logging, test.config;
}

View File

@ -0,0 +1,47 @@
/*
* 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 test.handlers;
import java.util.logging.Handler;
import java.util.logging.LogRecord;
/**
* A dummy Handler that does nothing.
* @author danielfuchs
*/
public class TestHandler extends Handler {
@Override
public void publish(LogRecord record) {
}
@Override
public void flush() {
}
@Override
public void close() throws SecurityException {
}
}

View 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.
*/
module test.logmanager {
requires java.logging;
// makes it possible for java.logging to instantiate
// test.logmanager.TestLogManager;
// this doesn't need to be a qualified export, but making it so will prevent
// any other module from being able to instantiate the provided classes.
exports test.logmanager to java.logging;
}

View File

@ -0,0 +1,34 @@
/*
* 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 test.logmanager;
import java.util.logging.LogManager;
/**
* A dummy LogManager that simply extends the standard class.
* @author danielfuchs
*/
public class TestLogManager extends LogManager {
}

View File

@ -31,7 +31,7 @@ import java.nio.file.Path;
/** /**
* *
* @test * @test
* @bug 8038500 8040059 8139956 8146754 * @bug 8038500 8040059 8139956 8146754 8172921
* @summary Tests path operations for zip provider. * @summary Tests path operations for zip provider.
* *
* @run main PathOps * @run main PathOps
@ -180,6 +180,13 @@ public class PathOps {
return this; return this;
} }
PathOps resolvePath(String other, String expected) {
out.format("test resolve %s\n", other);
checkPath();
check(path.resolve(fs.getPath(other)), expected);
return this;
}
PathOps resolveSibling(String other, String expected) { PathOps resolveSibling(String other, String expected) {
out.format("test resolveSibling %s\n", other); out.format("test resolveSibling %s\n", other);
checkPath(); checkPath();
@ -384,6 +391,30 @@ public class PathOps {
.resolve("", "") .resolve("", "")
.resolve("foo", "foo") .resolve("foo", "foo")
.resolve("/foo", "/foo"); .resolve("/foo", "/foo");
test("/")
.resolve("", "/")
.resolve("foo", "/foo")
.resolve("/foo", "/foo")
.resolve("/foo/", "/foo");
// resolve(Path)
test("/tmp")
.resolvePath("foo", "/tmp/foo")
.resolvePath("/foo", "/foo")
.resolvePath("", "/tmp");
test("tmp")
.resolvePath("foo", "tmp/foo")
.resolvePath("/foo", "/foo")
.resolvePath("", "tmp");
test("")
.resolvePath("", "")
.resolvePath("foo", "foo")
.resolvePath("/foo", "/foo");
test("/")
.resolvePath("", "/")
.resolvePath("foo", "/foo")
.resolvePath("/foo", "/foo")
.resolvePath("/foo/", "/foo");
// resolveSibling // resolveSibling
test("foo") test("foo")

View File

@ -0,0 +1,193 @@
/*
* 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
* @summary Tests for API validator.
* @library /test/lib /lib/testlibrary
* @modules java.base/jdk.internal.misc
* jdk.compiler
* jdk.jartool
* @build jdk.test.lib.JDKToolFinder jdk.test.lib.Utils jdk.test.lib.process.*
* @build jdk.testlibrary.FileUtils
* @build MRTestBase
* @run testng ApiValidatorTest
*/
import jdk.test.lib.process.OutputAnalyzer;
import jdk.testlibrary.FileUtils;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class ApiValidatorTest extends MRTestBase {
@Test(dataProvider = "signatureChange")
public void changeMethodSignature(String sigBase, String sigV10,
boolean isAcceptable,
Method method) throws Throwable {
Path root = Paths.get(method.getName());
Path classes = root.resolve("classes");
String METHOD_SIG = "#SIG";
String classTemplate =
"public class C { \n" +
" " + METHOD_SIG + "{ throw new RuntimeException(); };\n" +
"}\n";
String base = classTemplate.replace(METHOD_SIG, sigBase);
String v10 = classTemplate.replace(METHOD_SIG, sigV10);
compileTemplate(classes.resolve("base"), base);
compileTemplate(classes.resolve("v10"), v10);
String jarfile = root.resolve("test.jar").toString();
OutputAnalyzer result = jar("cf", jarfile,
"-C", classes.resolve("base").toString(), ".",
"--release", "10", "-C", classes.resolve("v10").toString(),
".");
if (isAcceptable) {
result.shouldHaveExitValue(SUCCESS)
.shouldBeEmpty();
} else {
result.shouldNotHaveExitValue(SUCCESS)
.shouldContain("contains a class with different api from earlier version");
}
FileUtils.deleteFileTreeWithRetry(root);
}
@DataProvider
Object[][] signatureChange() {
return new Object[][]{
{"public int m()", "protected int m()", false},
{"protected int m()", "public int m()", false},
{"public int m()", "int m()", false},
{"protected int m()", "private int m()", false},
{"private int m()", "int m()", true},
{"int m()", "private int m()", true},
{"int m()", "private int m(boolean b)", true},
{"public int m()", "public int m(int i)", false},
{"public int m()", "public int k()", false},
{"public int m()", "private int k()", false},
// @ignore JDK-8172147 {"public int m()", "public boolean m()", false},
// @ignore JDK-8172147 {"public boolean", "public Boolean", false},
// @ignore JDK-8172147 {"public <T> T", "public <T extends String> T", false},
};
}
@Test(dataProvider = "publicAPI")
public void introducingPublicMembers(String publicAPI,
Method method) throws Throwable {
Path root = Paths.get(method.getName());
Path classes = root.resolve("classes");
String API = "#API";
String classTemplate =
"public class C { \n" +
" " + API + "\n" +
" public void method(){ };\n" +
"}\n";
String base = classTemplate.replace(API, "");
String v10 = classTemplate.replace(API, publicAPI);
compileTemplate(classes.resolve("base"), base);
compileTemplate(classes.resolve("v10"), v10);
String jarfile = root.resolve("test.jar").toString();
jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
"--release", "10", "-C", classes.resolve("v10").toString(), ".")
.shouldNotHaveExitValue(SUCCESS)
.shouldContain("contains a class with different api from earlier version");
FileUtils.deleteFileTreeWithRetry(root);
}
@DataProvider
Object[][] publicAPI() {
return new Object[][]{
// @ignore JDK-8172148 {"protected class Inner { public void m(){ } } "}, // protected inner class
// @ignore JDK-8172148 {"public class Inner { public void m(){ } }"}, // public inner class
// @ignore JDK-8172148 {"public enum E { A; }"}, // public enum
{"public void m(){ }"}, // public method
{"protected void m(){ }"}, // protected method
};
}
@Test(dataProvider = "privateAPI")
public void introducingPrivateMembers(String privateAPI,
Method method) throws Throwable {
Path root = Paths.get(method.getName());
Path classes = root.resolve("classes");
String API = "#API";
String classTemplate =
"public class C { \n" +
" " + API + "\n" +
" public void method(){ };\n" +
"}\n";
String base = classTemplate.replace(API, "");
String v10 = classTemplate.replace(API, privateAPI);
compileTemplate(classes.resolve("base"), base);
compileTemplate(classes.resolve("v10"), v10);
String jarfile = root.resolve("test.jar").toString();
jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
"--release", "10", "-C", classes.resolve("v10").toString(), ".")
.shouldHaveExitValue(SUCCESS);
// add release
jar("uf", jarfile,
"--release", "11", "-C", classes.resolve("v10").toString(), ".")
.shouldHaveExitValue(SUCCESS);
// replace release
jar("uf", jarfile,
"--release", "11", "-C", classes.resolve("v10").toString(), ".")
.shouldHaveExitValue(SUCCESS);
FileUtils.deleteFileTreeWithRetry(root);
}
@DataProvider
Object[][] privateAPI() {
return new Object[][]{
{"private class Inner { public void m(){ } } "}, // private inner class
{"class Inner { public void m(){ } }"}, // package private inner class
{"enum E { A; }"}, // package private enum
// Local class and private method
{"private void m(){ class Inner { public void m(){} } Inner i = null; }"},
{"void m(){ }"}, // package private method
};
}
private void compileTemplate(Path classes, String template) throws Throwable {
Path classSourceFile = Files.createDirectories(
classes.getParent().resolve("src").resolve(classes.getFileName()))
.resolve("C.java");
Files.write(classSourceFile, template.getBytes());
javac(classes, classSourceFile);
}
}

View File

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -23,69 +23,59 @@
/* /*
* @test * @test
* @library /test/lib * @library /test/lib /lib/testlibrary
* @modules java.base/jdk.internal.misc * @modules java.base/jdk.internal.misc
* jdk.compiler * jdk.compiler
* jdk.jartool * jdk.jartool
* @build jdk.test.lib.JDKToolFinder jdk.test.lib.Utils * @build jdk.test.lib.JDKToolFinder jdk.test.lib.Utils jdk.test.lib.process.*
* @build jdk.testlibrary.FileUtils
* @build MRTestBase
* @run testng Basic * @run testng Basic
*/ */
import static org.testng.Assert.*; import static org.testng.Assert.*;
import jdk.testlibrary.FileUtils;
import org.testng.annotations.*; import org.testng.annotations.*;
import java.io.*; import java.io.File;
import java.nio.file.*; import java.nio.file.*;
import java.nio.file.attribute.*;
import java.util.*; import java.util.*;
import java.util.function.Consumer; import java.util.jar.JarFile;
import java.util.jar.*; import java.util.zip.ZipFile;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.*;
import jdk.test.lib.JDKToolFinder; public class Basic extends MRTestBase {
import jdk.test.lib.Utils;
import static java.lang.String.format;
import static java.lang.System.out;
public class Basic {
private final String src = System.getProperty("test.src", ".");
private final String usr = System.getProperty("user.dir", ".");
@Test @Test
// create a regular, non-multi-release jar // create a regular, non-multi-release jar
public void test00() throws IOException { public void test00() throws Throwable {
String jarfile = "test.jar"; String jarfile = "test.jar";
compile("test01"); //use same data as test01 compile("test01"); //use same data as test01
Path classes = Paths.get("classes"); Path classes = Paths.get("classes");
jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".") jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".")
.assertSuccess(); .shouldHaveExitValue(SUCCESS);
checkMultiRelease(jarfile, false); checkMultiRelease(jarfile, false);
Map<String,String[]> names = Map.of( Map<String, String[]> names = Map.of(
"version/Main.class", "version/Main.class",
new String[] {"base", "version", "Main.class"}, new String[]{"base", "version", "Main.class"},
"version/Version.class", "version/Version.class",
new String[] {"base", "version", "Version.class"} new String[]{"base", "version", "Version.class"}
); );
compare(jarfile, names); compare(jarfile, names);
delete(jarfile); FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile));
deleteDir(Paths.get(usr, "classes")); FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes"));
} }
@Test @Test
// create a multi-release jar // create a multi-release jar
public void test01() throws IOException { public void test01() throws Throwable {
String jarfile = "test.jar"; String jarfile = "test.jar";
compile("test01"); compile("test01");
@ -94,68 +84,96 @@ public class Basic {
jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".", jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
"--release", "9", "-C", classes.resolve("v9").toString(), ".", "--release", "9", "-C", classes.resolve("v9").toString(), ".",
"--release", "10", "-C", classes.resolve("v10").toString(), ".") "--release", "10", "-C", classes.resolve("v10").toString(), ".")
.assertSuccess(); .shouldHaveExitValue(SUCCESS);
checkMultiRelease(jarfile, true); checkMultiRelease(jarfile, true);
Map<String,String[]> names = Map.of( Map<String, String[]> names = Map.of(
"version/Main.class", "version/Main.class",
new String[] {"base", "version", "Main.class"}, new String[]{"base", "version", "Main.class"},
"version/Version.class", "version/Version.class",
new String[] {"base", "version", "Version.class"}, new String[]{"base", "version", "Version.class"},
"META-INF/versions/9/version/Version.class", "META-INF/versions/9/version/Version.class",
new String[] {"v9", "version", "Version.class"}, new String[]{"v9", "version", "Version.class"},
"META-INF/versions/10/version/Version.class", "META-INF/versions/10/version/Version.class",
new String[] {"v10", "version", "Version.class"} new String[]{"v10", "version", "Version.class"}
); );
compare(jarfile, names); compare(jarfile, names);
delete(jarfile); FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile));
deleteDir(Paths.get(usr, "classes")); FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes"));
}
@Test
public void versionFormat() throws Throwable {
String jarfile = "test.jar";
compile("test01");
Path classes = Paths.get("classes");
// valid
for (String release : List.of("10000", "09", "00010", "10")) {
jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
"--release", release, "-C", classes.resolve("v10").toString(), ".")
.shouldHaveExitValue(SUCCESS)
.shouldBeEmpty();
}
// invalid
for (String release : List.of("9.0", "8", "v9",
"9v", "0", "-10")) {
jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
"--release", release, "-C", classes.resolve("v10").toString(), ".")
.shouldNotHaveExitValue(SUCCESS)
.shouldContain("release " + release + " not valid");
}
FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile));
FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes"));
} }
@Test @Test
// update a regular jar to a multi-release jar // update a regular jar to a multi-release jar
public void test02() throws IOException { public void test02() throws Throwable {
String jarfile = "test.jar"; String jarfile = "test.jar";
compile("test01"); //use same data as test01 compile("test01"); //use same data as test01
Path classes = Paths.get("classes"); Path classes = Paths.get("classes");
jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".") jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".")
.assertSuccess(); .shouldHaveExitValue(SUCCESS);
checkMultiRelease(jarfile, false); checkMultiRelease(jarfile, false);
jar("uf", jarfile, "--release", "9", "-C", classes.resolve("v9").toString(), ".") jar("uf", jarfile,
.assertSuccess(); "--release", "9", "-C", classes.resolve("v9").toString(), ".")
.shouldHaveExitValue(SUCCESS);
checkMultiRelease(jarfile, true); checkMultiRelease(jarfile, true);
Map<String,String[]> names = Map.of( Map<String, String[]> names = Map.of(
"version/Main.class", "version/Main.class",
new String[] {"base", "version", "Main.class"}, new String[]{"base", "version", "Main.class"},
"version/Version.class", "version/Version.class",
new String[] {"base", "version", "Version.class"}, new String[]{"base", "version", "Version.class"},
"META-INF/versions/9/version/Version.class", "META-INF/versions/9/version/Version.class",
new String[] {"v9", "version", "Version.class"} new String[]{"v9", "version", "Version.class"}
); );
compare(jarfile, names); compare(jarfile, names);
delete(jarfile); FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile));
deleteDir(Paths.get(usr, "classes")); FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes"));
} }
@Test @Test
// replace a base entry and a versioned entry // replace a base entry and a versioned entry
public void test03() throws IOException { public void test03() throws Throwable {
String jarfile = "test.jar"; String jarfile = "test.jar";
compile("test01"); //use same data as test01 compile("test01"); //use same data as test01
@ -163,19 +181,19 @@ public class Basic {
Path classes = Paths.get("classes"); Path classes = Paths.get("classes");
jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".", jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
"--release", "9", "-C", classes.resolve("v9").toString(), ".") "--release", "9", "-C", classes.resolve("v9").toString(), ".")
.assertSuccess(); .shouldHaveExitValue(SUCCESS);
checkMultiRelease(jarfile, true); checkMultiRelease(jarfile, true);
Map<String,String[]> names = Map.of( Map<String, String[]> names = Map.of(
"version/Main.class", "version/Main.class",
new String[] {"base", "version", "Main.class"}, new String[]{"base", "version", "Main.class"},
"version/Version.class", "version/Version.class",
new String[] {"base", "version", "Version.class"}, new String[]{"base", "version", "Version.class"},
"META-INF/versions/9/version/Version.class", "META-INF/versions/9/version/Version.class",
new String[] {"v9", "version", "Version.class"} new String[]{"v9", "version", "Version.class"}
); );
compare(jarfile, names); compare(jarfile, names);
@ -184,25 +202,25 @@ public class Basic {
// version/Version.class entry in versions/9 section // version/Version.class entry in versions/9 section
jar("uf", jarfile, "-C", classes.resolve("v9").toString(), "version", jar("uf", jarfile, "-C", classes.resolve("v9").toString(), "version",
"--release", "9", "-C", classes.resolve("v10").toString(), ".") "--release", "9", "-C", classes.resolve("v10").toString(), ".")
.assertSuccess(); .shouldHaveExitValue(SUCCESS);
checkMultiRelease(jarfile, true); checkMultiRelease(jarfile, true);
names = Map.of( names = Map.of(
"version/Main.class", "version/Main.class",
new String[] {"base", "version", "Main.class"}, new String[]{"base", "version", "Main.class"},
"version/Version.class", "version/Version.class",
new String[] {"v9", "version", "Version.class"}, new String[]{"v9", "version", "Version.class"},
"META-INF/versions/9/version/Version.class", "META-INF/versions/9/version/Version.class",
new String[] {"v10", "version", "Version.class"} new String[]{"v10", "version", "Version.class"}
); );
compare(jarfile, names); compare(jarfile, names);
delete(jarfile); FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile));
deleteDir(Paths.get(usr, "classes")); FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes"));
} }
/* /*
@ -211,7 +229,7 @@ public class Basic {
@Test @Test
// META-INF/versions/9 class has different api than base class // META-INF/versions/9 class has different api than base class
public void test04() throws IOException { public void test04() throws Throwable {
String jarfile = "test.jar"; String jarfile = "test.jar";
compile("test01"); //use same data as test01 compile("test01"); //use same data as test01
@ -224,18 +242,16 @@ public class Basic {
jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".", jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
"--release", "9", "-C", classes.resolve("v9").toString(), ".") "--release", "9", "-C", classes.resolve("v9").toString(), ".")
.assertFailure() .shouldNotHaveExitValue(SUCCESS)
.resultChecker(r -> .shouldContain("different api from earlier");
assertTrue(r.output.contains("different api from earlier"), r.output)
);
delete(jarfile); FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile));
deleteDir(Paths.get(usr, "classes")); FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes"));
} }
@Test @Test
// META-INF/versions/9 contains an extra public class // META-INF/versions/9 contains an extra public class
public void test05() throws IOException { public void test05() throws Throwable {
String jarfile = "test.jar"; String jarfile = "test.jar";
compile("test01"); //use same data as test01 compile("test01"); //use same data as test01
@ -248,18 +264,16 @@ public class Basic {
jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".", jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
"--release", "9", "-C", classes.resolve("v9").toString(), ".") "--release", "9", "-C", classes.resolve("v9").toString(), ".")
.assertFailure() .shouldNotHaveExitValue(SUCCESS)
.resultChecker(r -> .shouldContain("contains a new public class");
assertTrue(r.output.contains("contains a new public class"), r.output)
);
delete(jarfile); FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile));
deleteDir(Paths.get(usr, "classes")); FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes"));
} }
@Test @Test
// META-INF/versions/9 contains an extra package private class -- this is okay // META-INF/versions/9 contains an extra package private class -- this is okay
public void test06() throws IOException { public void test06() throws Throwable {
String jarfile = "test.jar"; String jarfile = "test.jar";
compile("test01"); //use same data as test01 compile("test01"); //use same data as test01
@ -272,16 +286,16 @@ public class Basic {
jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".", jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
"--release", "9", "-C", classes.resolve("v9").toString(), ".") "--release", "9", "-C", classes.resolve("v9").toString(), ".")
.assertSuccess(); .shouldHaveExitValue(SUCCESS);
delete(jarfile); FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile));
deleteDir(Paths.get(usr, "classes")); FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes"));
} }
@Test @Test
// META-INF/versions/9 contains an identical class to base entry class // META-INF/versions/9 contains an identical class to base entry class
// this is okay but produces warning // this is okay but produces warning
public void test07() throws IOException { public void test07() throws Throwable {
String jarfile = "test.jar"; String jarfile = "test.jar";
compile("test01"); //use same data as test01 compile("test01"); //use same data as test01
@ -294,19 +308,42 @@ public class Basic {
jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".", jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
"--release", "9", "-C", classes.resolve("v9").toString(), ".") "--release", "9", "-C", classes.resolve("v9").toString(), ".")
.assertSuccess() .shouldHaveExitValue(SUCCESS)
.resultChecker(r -> .shouldContain("contains a class that")
assertTrue(r.outputContains("contains a class that is identical"), r.output) .shouldContain("is identical");
);
delete(jarfile); FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile));
deleteDir(Paths.get(usr, "classes")); FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes"));
}
@Test
// META-INF/versions/9 contains an identical class to previous version entry class
// this is okay but produces warning
public void identicalClassToPreviousVersion() throws Throwable {
String jarfile = "test.jar";
compile("test01"); //use same data as test01
Path classes = Paths.get("classes");
jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
"--release", "9", "-C", classes.resolve("v9").toString(), ".")
.shouldHaveExitValue(SUCCESS)
.shouldBeEmpty();
jar("uf", jarfile,
"--release", "10", "-C", classes.resolve("v9").toString(), ".")
.shouldHaveExitValue(SUCCESS)
.shouldContain("contains a class that")
.shouldContain("is identical");
FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile));
FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes"));
} }
@Test @Test
// resources with same name in different versions // resources with same name in different versions
// this is okay but produces warning // this is okay but produces warning
public void test08() throws IOException { public void test08() throws Throwable {
String jarfile = "test.jar"; String jarfile = "test.jar";
compile("test01"); //use same data as test01 compile("test01"); //use same data as test01
@ -320,10 +357,8 @@ public class Basic {
jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".", jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
"--release", "9", "-C", classes.resolve("v9").toString(), ".") "--release", "9", "-C", classes.resolve("v9").toString(), ".")
.assertSuccess() .shouldHaveExitValue(SUCCESS)
.resultChecker(r -> .shouldBeEmpty();
assertTrue(r.output.isEmpty(), r.output)
);
// now add a different resource with same name to META-INF/version/9 // now add a different resource with same name to META-INF/version/9
Files.copy(source.resolve("Main.java"), classes.resolve("v9") Files.copy(source.resolve("Main.java"), classes.resolve("v9")
@ -331,18 +366,16 @@ public class Basic {
jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".", jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
"--release", "9", "-C", classes.resolve("v9").toString(), ".") "--release", "9", "-C", classes.resolve("v9").toString(), ".")
.assertSuccess() .shouldHaveExitValue(SUCCESS)
.resultChecker(r -> .shouldContain("multiple resources with same name");
assertTrue(r.output.contains("multiple resources with same name"), r.output)
);
delete(jarfile); FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile));
deleteDir(Paths.get(usr, "classes")); FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes"));
} }
@Test @Test
// a class with an internal name different from the external name // a class with an internal name different from the external name
public void test09() throws IOException { public void test09() throws Throwable {
String jarfile = "test.jar"; String jarfile = "test.jar";
compile("test01"); //use same data as test01 compile("test01"); //use same data as test01
@ -355,18 +388,16 @@ public class Basic {
jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".", jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
"--release", "9", "-C", classes.resolve("v9").toString(), ".") "--release", "9", "-C", classes.resolve("v9").toString(), ".")
.assertFailure() .shouldNotHaveExitValue(SUCCESS)
.resultChecker(r -> .shouldContain("names do not match");
assertTrue(r.output.contains("names do not match"), r.output)
);
delete(jarfile); FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile));
deleteDir(Paths.get(usr, "classes")); FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes"));
} }
@Test @Test
// assure that basic nested classes are acceptable // assure that basic nested classes are acceptable
public void test10() throws IOException { public void test10() throws Throwable {
String jarfile = "test.jar"; String jarfile = "test.jar";
compile("test01"); //use same data as test01 compile("test01"); //use same data as test01
@ -383,15 +414,15 @@ public class Basic {
jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".", jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
"--release", "9", "-C", classes.resolve("v9").toString(), ".") "--release", "9", "-C", classes.resolve("v9").toString(), ".")
.assertSuccess(); .shouldHaveExitValue(SUCCESS);
delete(jarfile); FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile));
deleteDir(Paths.get(usr, "classes")); FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes"));
} }
@Test @Test
// a base entry contains a nested class that doesn't have a matching top level class // a base entry contains a nested class that doesn't have a matching top level class
public void test11() throws IOException { public void test11() throws Throwable {
String jarfile = "test.jar"; String jarfile = "test.jar";
compile("test01"); //use same data as test01 compile("test01"); //use same data as test01
@ -409,30 +440,29 @@ public class Basic {
source = Paths.get(src, "data", "test10", "v9", "version"); source = Paths.get(src, "data", "test10", "v9", "version");
javac(classes.resolve("v9"), source.resolve("Nested.java")); javac(classes.resolve("v9"), source.resolve("Nested.java"));
jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".", List<String> output = jar("cf", jarfile,
"-C", classes.resolve("base").toString(), ".",
"--release", "9", "-C", classes.resolve("v9").toString(), ".") "--release", "9", "-C", classes.resolve("v9").toString(), ".")
.assertFailure() .shouldNotHaveExitValue(SUCCESS)
.resultChecker(r -> { .asLines();
String[] msg = r.output.split("\\R");
// There should be 3 error messages, cascading from the first. Once we
// remove the base top level class, the base nested class becomes isolated,
// also the versioned top level class becomes a new public class, thus ignored
// for subsequent checks, leading to the associated versioned nested class
// becoming an isolated nested class
assertTrue(msg.length == 4);
assertTrue(msg[0].contains("an isolated nested class"), msg[0]);
assertTrue(msg[1].contains("contains a new public class"), msg[1]);
assertTrue(msg[2].contains("an isolated nested class"), msg[2]);
assertTrue(msg[3].contains("invalid multi-release jar file"), msg[3]);
});
delete(jarfile); assertTrue(output.size() == 4);
deleteDir(Paths.get(usr, "classes")); assertTrue(output.get(0).contains("an isolated nested class"),
output.get(0));
assertTrue(output.get(1).contains("contains a new public class"),
output.get(1));
assertTrue(output.get(2).contains("an isolated nested class"),
output.get(2));
assertTrue(output.get(3).contains("invalid multi-release jar file"),
output.get(3));
FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile));
FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes"));
} }
@Test @Test
// a versioned entry contains a nested class that doesn't have a matching top level class // a versioned entry contains a nested class that doesn't have a matching top level class
public void test12() throws IOException { public void test12() throws Throwable {
String jarfile = "test.jar"; String jarfile = "test.jar";
compile("test01"); //use same data as test01 compile("test01"); //use same data as test01
@ -452,178 +482,59 @@ public class Basic {
jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".", jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
"--release", "9", "-C", classes.resolve("v9").toString(), ".") "--release", "9", "-C", classes.resolve("v9").toString(), ".")
.assertFailure() .shouldNotHaveExitValue(SUCCESS)
.resultChecker(r -> .shouldContain("an isolated nested class");
assertTrue(r.outputContains("an isolated nested class"), r.output)
);
delete(jarfile); FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile));
deleteDir(Paths.get(usr, "classes")); FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes"));
} }
/* @Test
* Test Infrastructure public void testCustomManifest() throws Throwable {
*/ String jarfile = "test.jar";
private void compile(String test) throws IOException {
Path classes = Paths.get(usr, "classes", "base");
Files.createDirectories(classes);
Path source = Paths.get(src, "data", test, "base", "version");
javac(classes, source.resolve("Main.java"), source.resolve("Version.java"));
classes = Paths.get(usr, "classes", "v9"); compile("test01");
Files.createDirectories(classes);
source = Paths.get(src, "data", test, "v9", "version");
javac(classes, source.resolve("Version.java"));
classes = Paths.get(usr, "classes", "v10"); Path classes = Paths.get("classes");
Files.createDirectories(classes); Path manifest = Paths.get("Manifest.txt");
source = Paths.get(src, "data", test, "v10", "version");
javac(classes, source.resolve("Version.java"));
}
private void checkMultiRelease(String jarFile, boolean expected) throws IOException { // create
try (JarFile jf = new JarFile(new File(jarFile), true, ZipFile.OPEN_READ, Files.write(manifest, "Class-Path: MyUtils.jar\n".getBytes());
JarFile.runtimeVersion())) {
assertEquals(jf.isMultiRelease(), expected);
}
}
// compares the bytes found in the jar entries with the bytes found in the jar("cfm", jarfile, manifest.toString(),
// corresponding data files used to create the entries "-C", classes.resolve("base").toString(), ".",
private void compare(String jarfile, Map<String,String[]> names) throws IOException { "--release", "10", "-C", classes.resolve("v10").toString(), ".")
try (JarFile jf = new JarFile(jarfile)) { .shouldHaveExitValue(SUCCESS)
for (String name : names.keySet()) { .shouldBeEmpty();
Path path = Paths.get("classes", names.get(name));
byte[] b1 = Files.readAllBytes(path);
byte[] b2;
JarEntry je = jf.getJarEntry(name);
try (InputStream is = jf.getInputStream(je)) {
b2 = is.readAllBytes();
}
assertEquals(b1,b2);
}
}
}
private void delete(String name) throws IOException { try (JarFile jf = new JarFile(new File(jarfile), true,
Files.deleteIfExists(Paths.get(usr, name)); ZipFile.OPEN_READ, JarFile.runtimeVersion())) {
} assertTrue(jf.isMultiRelease(), "Not multi-release jar");
assertEquals(jf.getManifest()
private void deleteDir(Path dir) throws IOException { .getMainAttributes()
Files.walkFileTree(dir, new SimpleFileVisitor<Path>() { .getValue("Class-Path"),
@Override "MyUtils.jar");
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
Files.delete(file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
Files.delete(dir);
return FileVisitResult.CONTINUE;
}
});
}
/*
* The following methods were taken from modular jar and other jar tests
*/
void javac(Path dest, Path... sourceFiles) throws IOException {
String javac = JDKToolFinder.getJDKTool("javac");
List<String> commands = new ArrayList<>();
commands.add(javac);
String opts = System.getProperty("test.compiler.opts");
if (!opts.isEmpty()) {
commands.addAll(Arrays.asList(opts.split(" +")));
}
commands.add("-d");
commands.add(dest.toString());
Stream.of(sourceFiles).map(Object::toString).forEach(x -> commands.add(x));
quickFail(run(new ProcessBuilder(commands)));
}
Result jarWithStdin(File stdinSource, String... args) {
String jar = JDKToolFinder.getJDKTool("jar");
List<String> commands = new ArrayList<>();
commands.add(jar);
commands.addAll(Utils.getForwardVmOptions());
Stream.of(args).forEach(x -> commands.add(x));
ProcessBuilder p = new ProcessBuilder(commands);
if (stdinSource != null)
p.redirectInput(stdinSource);
return run(p);
}
Result jar(String... args) {
return jarWithStdin(null, args);
}
void quickFail(Result r) {
if (r.ec != 0)
throw new RuntimeException(r.output);
}
Result run(ProcessBuilder pb) {
Process p;
out.printf("Running: %s%n", pb.command());
try {
p = pb.start();
} catch (IOException e) {
throw new RuntimeException(
format("Couldn't start process '%s'", pb.command()), e);
} }
String output; // update
try { Files.write(manifest, "Multi-release: false\n".getBytes());
output = toString(p.getInputStream(), p.getErrorStream());
} catch (IOException e) { jar("ufm", jarfile, manifest.toString(),
throw new RuntimeException( "-C", classes.resolve("base").toString(), ".",
format("Couldn't read process output '%s'", pb.command()), e); "--release", "9", "-C", classes.resolve("v10").toString(), ".")
.shouldHaveExitValue(SUCCESS)
.shouldContain("WARNING: Duplicate name in Manifest: Multi-release.");
try (JarFile jf = new JarFile(new File(jarfile), true,
ZipFile.OPEN_READ, JarFile.runtimeVersion())) {
assertTrue(jf.isMultiRelease(), "Not multi-release jar");
assertEquals(jf.getManifest()
.getMainAttributes()
.getValue("Class-Path"),
"MyUtils.jar");
} }
try { FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile));
p.waitFor(); FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes"));
} catch (InterruptedException e) {
throw new RuntimeException(
format("Process hasn't finished '%s'", pb.command()), e);
}
return new Result(p.exitValue(), output);
}
String toString(InputStream in1, InputStream in2) throws IOException {
try (ByteArrayOutputStream dst = new ByteArrayOutputStream();
InputStream concatenated = new SequenceInputStream(in1, in2)) {
concatenated.transferTo(dst);
return new String(dst.toByteArray(), "UTF-8");
}
}
static class Result {
final int ec;
final String output;
private Result(int ec, String output) {
this.ec = ec;
this.output = output;
}
boolean outputContains(String msg) {
return Arrays.stream(output.split("\\R"))
.collect(Collectors.joining(" "))
.contains(msg);
}
Result assertSuccess() {
assertTrue(ec == 0, format("ec: %d, output: %s", ec, output));
return this;
}
Result assertFailure() {
assertTrue(ec != 0, format("ec: %d, output: %s", ec, output));
return this;
}
Result resultChecker(Consumer<Result> r) { r.accept(this); return this; }
} }
} }

View File

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -28,76 +28,65 @@
* jdk.compiler * jdk.compiler
* jdk.jartool * jdk.jartool
* @build jdk.test.lib.JDKToolFinder jdk.test.lib.Utils * @build jdk.test.lib.JDKToolFinder jdk.test.lib.Utils
* @build MRTestBase
* @run testng Basic1 * @run testng Basic1
*/ */
import static org.testng.Assert.*;
import org.testng.annotations.*; import org.testng.annotations.*;
import java.io.*;
import java.nio.file.*; import java.nio.file.*;
import java.util.*; import java.util.*;
import java.util.function.Consumer;
import java.util.jar.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.*;
import jdk.test.lib.JDKToolFinder; public class Basic1 extends MRTestBase {
import jdk.test.lib.Utils;
import static java.lang.String.format;
import static java.lang.System.out;
public class Basic1 {
private final String src = System.getProperty("test.src", ".");
@BeforeTest @BeforeTest
public void setup() throws IOException { public void setup() throws Throwable {
String test = "test01"; String test = "test01";
Path classes = Paths.get("classes", "base"); Path classes = Paths.get("classes");
Files.createDirectories(classes);
Path source = Paths.get(src, "data", test, "base", "version");
javac(classes, source.resolve("Main.java"), source.resolve("Version.java"));
Path v9 = Paths.get("v9"); Path base = classes.resolve("base");
Files.createDirectories(base);
Path source = Paths.get(src, "data", test, "base", "version");
javac(base, source.resolve("Main.java"), source.resolve("Version.java"));
Path v9 = classes.resolve("v9");
Files.createDirectories(v9); Files.createDirectories(v9);
source = Paths.get(src, "data", test, "v9", "version"); source = Paths.get(src, "data", test, "v9", "version");
javac(v9, source.resolve("Version.java")); javac(v9, source.resolve("Version.java"));
Path v10 = Paths.get("v10"); Path v10 = classes.resolve("v10");
Files.createDirectories(v10); Files.createDirectories(v10);
source = Paths.get(src, "data", test, "v10", "version"); source = Paths.get(src, "data", test, "v10", "version");
javac(v10, source.resolve("Version.java")); javac(v10, source.resolve("Version.java"));
Path v10_1 = Paths.get("v10_1").resolve("META-INF").resolve("versions").resolve("v10"); Path v10_1 = classes.resolve("v10_1").resolve("META-INF").resolve("versions").resolve("v10");
Files.createDirectories(v10_1); Files.createDirectories(v10_1);
source = Paths.get(src, "data", test, "v10", "version"); source = Paths.get(src, "data", test, "v10", "version");
javac(v10_1, source.resolve("Version.java")); javac(v10_1, source.resolve("Version.java"));
} }
@Test @Test
public void test() throws IOException { public void test() throws Throwable {
String jarfile = "test.jar"; String jarfile = "test.jar";
Path classes = Paths.get("classes"); Path classes = Paths.get("classes");
Path v9 = Paths.get("v9");
Path v10 = Paths.get("v10");
jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".", Path base = classes.resolve("base");
"--release", "9", "-C", v9.toString(), ".", Path v9 = classes.resolve("v9");
"--release", "10", "-C", v10.toString(), ".") Path v10 = classes.resolve("v10");
.assertSuccess();
jar("cf", jarfile, "-C", base.toString(), ".",
"--release", "9", "-C", v9.toString(), ".",
"--release", "10", "-C", v10.toString(), ".")
.shouldHaveExitValue(SUCCESS);
checkMultiRelease(jarfile, true); checkMultiRelease(jarfile, true);
Map<String,String[]> names = Map.of( Map<String, String[]> names = Map.of(
"version/Main.class", "version/Main.class",
new String[] {"classes", "base", "version", "Main.class"}, new String[]{"base", "version", "Main.class"},
"version/Version.class", "version/Version.class",
new String[] {"classes", "base", "version", "Version.class"}, new String[]{"base", "version", "Version.class"},
"META-INF/versions/9/version/Version.class", "META-INF/versions/9/version/Version.class",
new String[] {"v9", "version", "Version.class"}, new String[] {"v9", "version", "Version.class"},
@ -109,144 +98,16 @@ public class Basic1 {
compare(jarfile, names); compare(jarfile, names);
} }
@Test @Test
public void testFail() throws IOException { public void testFail() throws Throwable {
String jarfile = "test.jar"; String jarfile = "test.jar";
Path classes = Paths.get("classes"); Path classes = Paths.get("classes");
Path v10 = Paths.get("v10_1"); Path base = classes.resolve("base");
Path v10 = classes.resolve("v10_1");
jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".", jar("cf", jarfile, "-C", base.toString(), ".",
"--release", "10", "-C", v10.toString(), ".") "--release", "10", "-C", v10.toString(), ".")
.assertFailure() .shouldNotHaveExitValue(SUCCESS)
.outputContains("unexpected versioned entry META-INF/versions/"); .shouldContain("unexpected versioned entry META-INF/versions/");
}
private void checkMultiRelease(String jarFile, boolean expected) throws IOException {
try (JarFile jf = new JarFile(new File(jarFile), true, ZipFile.OPEN_READ,
JarFile.runtimeVersion())) {
assertEquals(jf.isMultiRelease(), expected);
}
}
// compares the bytes found in the jar entries with the bytes found in the
// corresponding data files used to create the entries
private void compare(String jarfile, Map<String,String[]> names) throws IOException {
try (JarFile jf = new JarFile(jarfile)) {
for (String name : names.keySet()) {
Path path = Paths.get("", names.get(name));
byte[] b1 = Files.readAllBytes(path);
byte[] b2;
JarEntry je = jf.getJarEntry(name);
try (InputStream is = jf.getInputStream(je)) {
b2 = is.readAllBytes();
}
assertEquals(b1,b2);
}
}
}
/*
* The following methods were taken from modular jar and other jar tests
*/
void javac(Path dest, Path... sourceFiles) throws IOException {
String javac = JDKToolFinder.getJDKTool("javac");
List<String> commands = new ArrayList<>();
commands.add(javac);
String opts = System.getProperty("test.compiler.opts");
if (!opts.isEmpty()) {
commands.addAll(Arrays.asList(opts.split(" +")));
}
commands.add("-d");
commands.add(dest.toString());
Stream.of(sourceFiles).map(Object::toString).forEach(x -> commands.add(x));
quickFail(run(new ProcessBuilder(commands)));
}
Result jarWithStdin(File stdinSource, String... args) {
String jar = JDKToolFinder.getJDKTool("jar");
List<String> commands = new ArrayList<>();
commands.add(jar);
commands.addAll(Utils.getForwardVmOptions());
Stream.of(args).forEach(x -> commands.add(x));
ProcessBuilder p = new ProcessBuilder(commands);
if (stdinSource != null)
p.redirectInput(stdinSource);
return run(p);
}
Result jar(String... args) {
return jarWithStdin(null, args);
}
void quickFail(Result r) {
if (r.ec != 0)
throw new RuntimeException(r.output);
}
Result run(ProcessBuilder pb) {
Process p;
out.printf("Running: %s%n", pb.command());
try {
p = pb.start();
} catch (IOException e) {
throw new RuntimeException(
format("Couldn't start process '%s'", pb.command()), e);
}
String output;
try {
output = toString(p.getInputStream(), p.getErrorStream());
} catch (IOException e) {
throw new RuntimeException(
format("Couldn't read process output '%s'", pb.command()), e);
}
try {
p.waitFor();
} catch (InterruptedException e) {
throw new RuntimeException(
format("Process hasn't finished '%s'", pb.command()), e);
}
return new Result(p.exitValue(), output);
}
String toString(InputStream in1, InputStream in2) throws IOException {
try (ByteArrayOutputStream dst = new ByteArrayOutputStream();
InputStream concatenated = new SequenceInputStream(in1, in2)) {
concatenated.transferTo(dst);
return new String(dst.toByteArray(), "UTF-8");
}
}
static class Result {
final int ec;
final String output;
private Result(int ec, String output) {
this.ec = ec;
this.output = output;
}
boolean outputContains(String msg) {
return Arrays.stream(output.split("\\R"))
.collect(Collectors.joining(" "))
.contains(msg);
}
Result assertSuccess() {
assertTrue(ec == 0, format("ec: %d, output: %s", ec, output));
return this;
}
Result assertFailure() {
assertTrue(ec != 0, format("ec: %d, output: %s", ec, output));
return this;
}
Result resultChecker(Consumer<Result> r) { r.accept(this); return this; }
} }
} }

View File

@ -0,0 +1,130 @@
/*
* 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 jdk.test.lib.JDKToolFinder;
import jdk.test.lib.Utils;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;
import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.stream.Stream;
import java.util.zip.ZipFile;
import static org.testng.Assert.assertEquals;
public class MRTestBase {
public static final int SUCCESS = 0;
protected final String src = System.getProperty("test.src", ".");
protected final String usr = System.getProperty("user.dir", ".");
protected void compile(String test) throws Throwable {
Path classes = Paths.get(usr, "classes", "base");
Files.createDirectories(classes);
Path source = Paths.get(src, "data", test, "base", "version");
javac(classes, source.resolve("Main.java"), source.resolve("Version.java"));
classes = Paths.get(usr, "classes", "v9");
Files.createDirectories(classes);
source = Paths.get(src, "data", test, "v9", "version");
javac(classes, source.resolve("Version.java"));
classes = Paths.get(usr, "classes", "v10");
Files.createDirectories(classes);
source = Paths.get(src, "data", test, "v10", "version");
javac(classes, source.resolve("Version.java"));
}
protected void checkMultiRelease(String jarFile,
boolean expected) throws IOException {
try (JarFile jf = new JarFile(new File(jarFile), true,
ZipFile.OPEN_READ, JarFile.runtimeVersion())) {
assertEquals(jf.isMultiRelease(), expected);
}
}
// compares the bytes found in the jar entries with the bytes found in the
// corresponding data files used to create the entries
protected void compare(String jarfile,
Map<String, String[]> names) throws IOException {
try (JarFile jf = new JarFile(jarfile)) {
for (String name : names.keySet()) {
Path path = Paths.get("classes", names.get(name));
byte[] b1 = Files.readAllBytes(path);
byte[] b2;
JarEntry je = jf.getJarEntry(name);
try (InputStream is = jf.getInputStream(je)) {
b2 = is.readAllBytes();
}
assertEquals(b1, b2);
}
}
}
void javac(Path dest, Path... sourceFiles) throws Throwable {
String javac = JDKToolFinder.getJDKTool("javac");
List<String> commands = new ArrayList<>();
commands.add(javac);
String opts = System.getProperty("test.compiler.opts");
if (!opts.isEmpty()) {
commands.addAll(Arrays.asList(opts.split(" +")));
}
commands.addAll(Utils.getForwardVmOptions());
commands.add("-d");
commands.add(dest.toString());
Stream.of(sourceFiles)
.map(Object::toString)
.forEach(x -> commands.add(x));
ProcessTools.executeCommand(new ProcessBuilder(commands))
.shouldHaveExitValue(SUCCESS);
}
OutputAnalyzer jarWithStdin(File stdinSource,
String... args) throws Throwable {
String jar = JDKToolFinder.getJDKTool("jar");
List<String> commands = new ArrayList<>();
commands.add(jar);
commands.addAll(Utils.getForwardVmOptions());
Stream.of(args).forEach(x -> commands.add(x));
ProcessBuilder p = new ProcessBuilder(commands);
if (stdinSource != null)
p.redirectInput(stdinSource);
return ProcessTools.executeCommand(p);
}
OutputAnalyzer jar(String... args) throws Throwable {
return jarWithStdin(null, args);
}
}

View File

@ -8,7 +8,7 @@ public class Version {
protected void doNothing() { protected void doNothing() {
} }
// extra publc method // extra public method
public void anyName() { public void anyName() {
} }
} }

View File

@ -44,6 +44,7 @@ import tests.Helper;
* @modules java.base/jdk.internal.jimage * @modules java.base/jdk.internal.jimage
* jdk.jdeps/com.sun.tools.classfile * jdk.jdeps/com.sun.tools.classfile
* jdk.jlink/jdk.tools.jlink.internal * jdk.jlink/jdk.tools.jlink.internal
* jdk.jlink/jdk.tools.jlink.plugin
* jdk.jlink/jdk.tools.jmod * jdk.jlink/jdk.tools.jmod
* jdk.jlink/jdk.tools.jimage * jdk.jlink/jdk.tools.jimage
* jdk.compiler * jdk.compiler

View File

@ -48,6 +48,7 @@ import jdk.tools.jlink.plugin.ResourcePool;
* @author Jean-Francois Denise * @author Jean-Francois Denise
* @modules jdk.jlink/jdk.tools.jlink.internal * @modules jdk.jlink/jdk.tools.jlink.internal
* jdk.jlink/jdk.tools.jlink.builder * jdk.jlink/jdk.tools.jlink.builder
* jdk.jlink/jdk.tools.jlink.plugin
* java.base/jdk.internal.jimage * java.base/jdk.internal.jimage
* @run main/othervm -verbose:gc -Xmx1g ImageFileCreatorTest * @run main/othervm -verbose:gc -Xmx1g ImageFileCreatorTest
*/ */

View File

@ -26,6 +26,7 @@
* @summary Test a pool containing external files. * @summary Test a pool containing external files.
* @author Andrei Eremeev * @author Andrei Eremeev
* @modules jdk.jlink/jdk.tools.jlink.internal * @modules jdk.jlink/jdk.tools.jlink.internal
* jdk.jlink/jdk.tools.jlink.plugin
* @run build ImageFilePoolTest * @run build ImageFilePoolTest
* @run main ImageFilePoolTest * @run main ImageFilePoolTest
*/ */

View File

@ -62,6 +62,7 @@ import tests.JImageGenerator;
* jdk.jlink/jdk.tools.jlink.builder * jdk.jlink/jdk.tools.jlink.builder
* jdk.jlink/jdk.tools.jlink.internal * jdk.jlink/jdk.tools.jlink.internal
* jdk.jlink/jdk.tools.jlink.internal.plugins * jdk.jlink/jdk.tools.jlink.internal.plugins
* jdk.jlink/jdk.tools.jlink.plugin
* jdk.jlink/jdk.tools.jmod * jdk.jlink/jdk.tools.jmod
* jdk.jlink/jdk.tools.jimage * jdk.jlink/jdk.tools.jimage
* jdk.compiler * jdk.compiler

View File

@ -29,6 +29,7 @@
* @modules java.base/jdk.internal.jimage * @modules java.base/jdk.internal.jimage
* jdk.jdeps/com.sun.tools.classfile * jdk.jdeps/com.sun.tools.classfile
* jdk.jlink/jdk.tools.jlink.internal * jdk.jlink/jdk.tools.jlink.internal
* jdk.jlink/jdk.tools.jlink.plugin
* jdk.jlink/jdk.tools.jmod * jdk.jlink/jdk.tools.jmod
* jdk.jlink/jdk.tools.jimage * jdk.jlink/jdk.tools.jimage
* jdk.compiler * jdk.compiler

View File

@ -39,6 +39,7 @@ import tests.Helper;
* @modules java.base/jdk.internal.jimage * @modules java.base/jdk.internal.jimage
* jdk.jdeps/com.sun.tools.classfile * jdk.jdeps/com.sun.tools.classfile
* jdk.jlink/jdk.tools.jlink.internal * jdk.jlink/jdk.tools.jlink.internal
* jdk.jlink/jdk.tools.jlink.plugin
* jdk.jlink/jdk.tools.jmod * jdk.jlink/jdk.tools.jmod
* jdk.jlink/jdk.tools.jimage * jdk.jlink/jdk.tools.jimage
* jdk.compiler * jdk.compiler

View File

@ -46,6 +46,7 @@ import tests.Helper;
* @modules java.base/jdk.internal.jimage * @modules java.base/jdk.internal.jimage
* jdk.jdeps/com.sun.tools.classfile * jdk.jdeps/com.sun.tools.classfile
* jdk.jlink/jdk.tools.jlink.internal * jdk.jlink/jdk.tools.jlink.internal
* jdk.jlink/jdk.tools.jlink.plugin
* jdk.jlink/jdk.tools.jmod * jdk.jlink/jdk.tools.jmod
* jdk.jlink/jdk.tools.jimage * jdk.jlink/jdk.tools.jimage
* jdk.compiler * jdk.compiler

View File

@ -48,6 +48,7 @@ import tests.JImageGenerator;
* @modules java.base/jdk.internal.jimage * @modules java.base/jdk.internal.jimage
* jdk.jdeps/com.sun.tools.classfile * jdk.jdeps/com.sun.tools.classfile
* jdk.jlink/jdk.tools.jlink.internal * jdk.jlink/jdk.tools.jlink.internal
* jdk.jlink/jdk.tools.jlink.plugin
* jdk.jlink/jdk.tools.jimage * jdk.jlink/jdk.tools.jimage
* jdk.compiler * jdk.compiler
* @build tests.* * @build tests.*

View File

@ -0,0 +1,85 @@
/*
* 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
* @bug 8168254
* @summary Detect duplicated resources in packaged modules
* @modules jdk.jlink/jdk.tools.jlink.builder
* jdk.jlink/jdk.tools.jlink.internal
* jdk.jlink/jdk.tools.jlink.plugin
* @run build ResourceDuplicateCheckTest
* @run main ResourceDuplicateCheckTest
*/
import java.net.URI;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import jdk.tools.jlink.builder.DefaultImageBuilder;
import jdk.tools.jlink.internal.ResourcePoolEntryFactory;
import jdk.tools.jlink.internal.ResourcePoolManager;
import jdk.tools.jlink.plugin.PluginException;
import jdk.tools.jlink.plugin.ResourcePoolEntry;
public class ResourceDuplicateCheckTest {
public static void main(String[] args) throws Exception {
new ResourceDuplicateCheckTest().test();
}
public void test() throws Exception {
ResourcePoolManager input = new ResourcePoolManager();
// need java.base module info because OS name is retrieved from it from storeFiles
input.add(ResourcePoolEntryFactory.create("/java.base/module-info.class",
ResourcePoolEntry.Type.CLASS_OR_RESOURCE, getJavaBaseModuleInfo()));
// same NATIVE_CMD from two different modules
input.add(newInMemoryImageFile("/com.acme/bin/myexec",
ResourcePoolEntry.Type.NATIVE_CMD, "mylib"));
input.add(newInMemoryImageFile("/com.foo/bin/myexec",
ResourcePoolEntry.Type.NATIVE_CMD, "mylib"));
Path root = Paths.get(System.getProperty("test.classes"));
DefaultImageBuilder writer = new DefaultImageBuilder(root, Collections.emptyMap());
try {
writer.storeFiles(input.resourcePool());
} catch (PluginException pe) {
if (! pe.getMessage().contains("Duplicate resources:")) {
throw new AssertionError("expected duplicate resources message");
}
}
}
private byte[] getJavaBaseModuleInfo() throws Exception {
Path path = FileSystems.
getFileSystem(URI.create("jrt:/")).
getPath("/modules/java.base/module-info.class");
return Files.readAllBytes(path);
}
private static ResourcePoolEntry newInMemoryImageFile(String path,
ResourcePoolEntry.Type type, String content) {
return ResourcePoolEntryFactory.create(path, type, content.getBytes());
}
}

View File

@ -26,6 +26,7 @@
* @summary Test a pool containing jimage resources and classes. * @summary Test a pool containing jimage resources and classes.
* @author Jean-Francois Denise * @author Jean-Francois Denise
* @modules jdk.jlink/jdk.tools.jlink.internal * @modules jdk.jlink/jdk.tools.jlink.internal
* jdk.jlink/jdk.tools.jlink.plugin
* @run build ResourcePoolTest * @run build ResourcePoolTest
* @run main ResourcePoolTest * @run main ResourcePoolTest
*/ */

View File

@ -28,6 +28,7 @@
* @modules java.base/jdk.internal.jimage.decompressor * @modules java.base/jdk.internal.jimage.decompressor
* jdk.jlink/jdk.tools.jlink.internal * jdk.jlink/jdk.tools.jlink.internal
* jdk.jlink/jdk.tools.jlink.internal.plugins * jdk.jlink/jdk.tools.jlink.internal.plugins
* jdk.jlink/jdk.tools.jlink.plugin
* @run main CompressorPluginTest * @run main CompressorPluginTest
*/ */
import java.net.URI; import java.net.URI;

View File

@ -27,6 +27,7 @@
* @author Jean-Francois Denise * @author Jean-Francois Denise
* @modules jdk.jlink/jdk.tools.jlink.internal * @modules jdk.jlink/jdk.tools.jlink.internal
* jdk.jlink/jdk.tools.jlink.internal.plugins * jdk.jlink/jdk.tools.jlink.internal.plugins
* jdk.jlink/jdk.tools.jlink.plugin
* @run main ExcludeFilesPluginTest * @run main ExcludeFilesPluginTest
*/ */

View File

@ -27,6 +27,7 @@
* @author Jean-Francois Denise * @author Jean-Francois Denise
* @modules jdk.jlink/jdk.tools.jlink.internal * @modules jdk.jlink/jdk.tools.jlink.internal
* jdk.jlink/jdk.tools.jlink.internal.plugins * jdk.jlink/jdk.tools.jlink.internal.plugins
* jdk.jlink/jdk.tools.jlink.plugin
* @run main ExcludePluginTest * @run main ExcludePluginTest
*/ */

View File

@ -27,6 +27,7 @@
* @author Jean-Francois Denise * @author Jean-Francois Denise
* @modules jdk.jlink/jdk.tools.jlink.internal * @modules jdk.jlink/jdk.tools.jlink.internal
* jdk.jlink/jdk.tools.jlink.internal.plugins * jdk.jlink/jdk.tools.jlink.internal.plugins
* jdk.jlink/jdk.tools.jlink.plugin
* @run main ExcludeVMPluginTest * @run main ExcludeVMPluginTest
*/ */
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;

View File

@ -48,6 +48,7 @@ import tests.Result;
* jdk.jdeps/com.sun.tools.classfile * jdk.jdeps/com.sun.tools.classfile
* jdk.jlink/jdk.tools.jlink.internal * jdk.jlink/jdk.tools.jlink.internal
* jdk.jlink/jdk.tools.jlink.internal.plugins * jdk.jlink/jdk.tools.jlink.internal.plugins
* jdk.jlink/jdk.tools.jlink.plugin
* jdk.jlink/jdk.tools.jmod * jdk.jlink/jdk.tools.jmod
* jdk.jlink/jdk.tools.jimage * jdk.jlink/jdk.tools.jimage
* jdk.compiler * jdk.compiler

View File

@ -25,8 +25,9 @@
* @test * @test
* @summary Test last sorter property * @summary Test last sorter property
* @author Jean-Francois Denise * @author Jean-Francois Denise
* @modules jdk.jlink/jdk.tools.jlink.internal * @modules jdk.jlink/jdk.tools.jlink
* jdk.jlink/jdk.tools.jlink * jdk.jlink/jdk.tools.jlink.internal
* jdk.jlink/jdk.tools.jlink.plugin
* @run main/othervm LastSorterTest * @run main/othervm LastSorterTest
*/ */

View File

@ -25,8 +25,9 @@
* @test * @test
* @summary Negative test for ImagePluginStack. * @summary Negative test for ImagePluginStack.
* @author Andrei Eremeev * @author Andrei Eremeev
* @modules jdk.jlink/jdk.tools.jlink.internal * @modules jdk.jlink/jdk.tools.jlink
* jdk.jlink/jdk.tools.jlink * jdk.jlink/jdk.tools.jlink.internal
* jdk.jlink/jdk.tools.jlink.plugin
* @run main/othervm PluginsNegativeTest * @run main/othervm PluginsNegativeTest
*/ */
import java.lang.reflect.Layer; import java.lang.reflect.Layer;

View File

@ -25,8 +25,9 @@
* @test * @test
* @summary Test previsitor * @summary Test previsitor
* @author Andrei Eremeev * @author Andrei Eremeev
* @modules jdk.jlink/jdk.tools.jlink.internal * @modules jdk.jlink/jdk.tools.jlink
* jdk.jlink/jdk.tools.jlink * jdk.jlink/jdk.tools.jlink.internal
* jdk.jlink/jdk.tools.jlink.plugin
* @run main/othervm PrevisitorTest * @run main/othervm PrevisitorTest
*/ */
import java.nio.ByteOrder; import java.nio.ByteOrder;

View File

@ -30,6 +30,7 @@
* java.base/jdk.internal.jimage.decompressor * java.base/jdk.internal.jimage.decompressor
* jdk.jlink/jdk.tools.jlink.internal * jdk.jlink/jdk.tools.jlink.internal
* jdk.jlink/jdk.tools.jlink.internal.plugins * jdk.jlink/jdk.tools.jlink.internal.plugins
* jdk.jlink/jdk.tools.jlink.plugin
* jdk.jlink/jdk.tools.jmod * jdk.jlink/jdk.tools.jmod
* jdk.jlink/jdk.tools.jimage * jdk.jlink/jdk.tools.jimage
* jdk.jdeps/com.sun.tools.classfile * jdk.jdeps/com.sun.tools.classfile

View File

@ -30,6 +30,7 @@
* @modules java.base/jdk.internal.jimage * @modules java.base/jdk.internal.jimage
* jdk.jlink/jdk.tools.jlink.internal * jdk.jlink/jdk.tools.jlink.internal
* jdk.jlink/jdk.tools.jlink.internal.plugins * jdk.jlink/jdk.tools.jlink.internal.plugins
* jdk.jlink/jdk.tools.jlink.plugin
* jdk.jlink/jdk.tools.jimage * jdk.jlink/jdk.tools.jimage
* jdk.jlink/jdk.tools.jmod * jdk.jlink/jdk.tools.jmod
* jdk.jdeps/com.sun.tools.classfile * jdk.jdeps/com.sun.tools.classfile

View File

@ -29,7 +29,7 @@
* @modules jdk.compiler * @modules jdk.compiler
* jdk.jlink * jdk.jlink
* @build jdk.testlibrary.FileUtils CompilerUtils * @build jdk.testlibrary.FileUtils CompilerUtils
* @run testng JmodTest * @run testng/othervm -Djava.io.tmpdir=. JmodTest
*/ */
import java.io.*; import java.io.*;
@ -40,8 +40,10 @@ import java.util.*;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.spi.ToolProvider; import java.util.spi.ToolProvider;
import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import jdk.testlibrary.FileUtils; import jdk.testlibrary.FileUtils;
import jdk.testlibrary.JDKToolFinder;
import org.testng.annotations.BeforeTest; import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test; import org.testng.annotations.Test;
@ -593,9 +595,7 @@ public class JmodTest {
findTmpFiles(filename).forEach(tmp -> { findTmpFiles(filename).forEach(tmp -> {
try { try {
FileUtils.deleteFileIfExistsWithRetry(tmp); FileUtils.deleteFileIfExistsWithRetry(tmp);
} catch (IOException e) { } catch (IOException e) {}
throw new UncheckedIOException(e);
}
}); });
String cp = EXPLODED_DIR.resolve("foo").resolve("classes") + File.pathSeparator + String cp = EXPLODED_DIR.resolve("foo").resolve("classes") + File.pathSeparator +
@ -608,17 +608,25 @@ public class JmodTest {
.assertFailure() .assertFailure()
.resultChecker(r -> { .resultChecker(r -> {
assertContains(r.output, "unnamed package"); assertContains(r.output, "unnamed package");
Set<Path> tmpfiles = findTmpFiles(filename).collect(toSet()); List<Path> tmpfiles = findTmpFiles(filename);
assertTrue(tmpfiles.isEmpty(), "Unexpected tmp file:" + tmpfiles); assertTrue(tmpfiles.isEmpty(), "Unexpected tmp file:" + tmpfiles);
}); });
} }
private Stream<Path> findTmpFiles(String prefix) { /*
try { * Returns the list of writeable tmp files with the given prefix.
Path tmpdir = Paths.get(System.getProperty("java.io.tmpdir")); *
return Files.find(tmpdir, 1, (p, attrs) -> * Ignore the non-writeable tmp files because this test is possibly
p.getFileName().toString().startsWith(prefix) * running by another user.
&& p.getFileName().toString().endsWith(".tmp")); */
private List<Path> findTmpFiles(String prefix) {
Path tmpdir = Paths.get(System.getProperty("java.io.tmpdir"));
try (Stream<Path> stream = Files.list(tmpdir)) {
return stream.filter(p -> {
String fn = p.getFileName().toString();
return Files.isWritable(p)
&& fn.startsWith(prefix) && fn.endsWith(".tmp");
}).collect(Collectors.toList());
} catch (IOException e) { } catch (IOException e) {
throw new UncheckedIOException(e); throw new UncheckedIOException(e);
} }

View File

@ -111,6 +111,7 @@ class Utils {
compiler("-d", compiler("-d",
XCLASSES.getName(), XCLASSES.getName(),
"--add-modules=jdk.jdeps",
"--add-exports=jdk.jdeps/com.sun.tools.classfile=ALL-UNNAMED", "--add-exports=jdk.jdeps/com.sun.tools.classfile=ALL-UNNAMED",
"@" + tmpFile.getAbsolutePath()); "@" + tmpFile.getAbsolutePath());