# HG changeset patch # User henryjen # Date 1484838153 28800 # Node ID d935c4d3f039cd83a5cdff4305e7eab2d99f697d # Parent c0f498d6a804cfd6482e0755dea4094b0e24fcd9# Parent 3c8c3c61ae9280a21a5cdc6db40d54ee89cf5a30 Merge diff -r c0f498d6a804 -r d935c4d3f039 jdk/make/ModuleTools.gmk --- a/jdk/make/ModuleTools.gmk Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/make/ModuleTools.gmk Thu Jan 19 07:02:33 2017 -0800 @@ -39,7 +39,6 @@ build.tools.jigsaw.GenGraphs TOOL_MODULESUMMARY := $(BUILD_JAVA) -esa -ea -cp $(TOOLS_CLASSES_DIR) \ - --add-exports jdk.jdeps/com.sun.tools.classfile=ALL-UNNAMED \ build.tools.jigsaw.ModuleSummary TOOL_ADD_PACKAGES_ATTRIBUTE := $(BUILD_JAVA) $(JAVA_FLAGS_SMALL) \ diff -r c0f498d6a804 -r d935c4d3f039 jdk/src/java.base/share/classes/java/lang/Class.java --- a/jdk/src/java.base/share/classes/java/lang/Class.java Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/src/java.base/share/classes/java/lang/Class.java Thu Jan 19 07:02:33 2017 -0800 @@ -508,8 +508,9 @@ public T newInstance() throws InstantiationException, IllegalAccessException { - if (System.getSecurityManager() != null) { - checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), false); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + checkMemberAccess(sm, Member.PUBLIC, Reflection.getCallerClass(), false); } // NOTE: the following code may not be strictly correct under @@ -1223,38 +1224,27 @@ // Perform access check final Class enclosingCandidate = enclosingInfo.getEnclosingClass(); - enclosingCandidate.checkMemberAccess(Member.DECLARED, - Reflection.getCallerClass(), true); - // Client is ok to access declared methods but j.l.Class might not be. - Method[] candidates = AccessController.doPrivileged( - new PrivilegedAction<>() { - @Override - public Method[] run() { - return enclosingCandidate.getDeclaredMethods(); - } - }); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + enclosingCandidate.checkMemberAccess(sm, Member.DECLARED, + Reflection.getCallerClass(), true); + } + Method[] candidates = enclosingCandidate.privateGetDeclaredMethods(false); + /* * Loop over all declared methods; match method name, * number of and type of parameters, *and* return * type. Matching return type is also necessary * because of covariant returns, etc. */ - for(Method m: candidates) { - if (m.getName().equals(enclosingInfo.getName()) ) { - Class[] candidateParamClasses = m.getParameterTypes(); - if (candidateParamClasses.length == parameterClasses.length) { - boolean matches = true; - for(int i = 0; i < candidateParamClasses.length; i++) { - if (!candidateParamClasses[i].equals(parameterClasses[i])) { - matches = false; - break; - } - } - - if (matches) { // finally, check return type - if (m.getReturnType().equals(returnType) ) - return m; - } + ReflectionFactory fact = getReflectionFactory(); + for (Method m : candidates) { + if (m.getName().equals(enclosingInfo.getName()) && + arrayContentsEq(parameterClasses, + fact.getExecutableSharedParameterTypes(m))) { + // finally, check return type + if (m.getReturnType().equals(returnType)) { + return fact.copyMethod(m); } } } @@ -1390,33 +1380,23 @@ // Perform access check final Class enclosingCandidate = enclosingInfo.getEnclosingClass(); - enclosingCandidate.checkMemberAccess(Member.DECLARED, - Reflection.getCallerClass(), true); - // Client is ok to access declared methods but j.l.Class might not be. - Constructor[] candidates = AccessController.doPrivileged( - new PrivilegedAction<>() { - @Override - public Constructor[] run() { - return enclosingCandidate.getDeclaredConstructors(); - } - }); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + enclosingCandidate.checkMemberAccess(sm, Member.DECLARED, + Reflection.getCallerClass(), true); + } + + Constructor[] candidates = enclosingCandidate + .privateGetDeclaredConstructors(false); /* * Loop over all declared constructors; match number * of and type of parameters. */ - for(Constructor c: candidates) { - Class[] candidateParamClasses = c.getParameterTypes(); - if (candidateParamClasses.length == parameterClasses.length) { - boolean matches = true; - for(int i = 0; i < candidateParamClasses.length; i++) { - if (!candidateParamClasses[i].equals(parameterClasses[i])) { - matches = false; - break; - } - } - - if (matches) - return c; + ReflectionFactory fact = getReflectionFactory(); + for (Constructor c : candidates) { + if (arrayContentsEq(parameterClasses, + fact.getExecutableSharedParameterTypes(c))) { + return fact.copyConstructor(c); } } @@ -1446,9 +1426,13 @@ public Class getDeclaringClass() throws SecurityException { final Class candidate = getDeclaringClass0(); - if (candidate != null) - candidate.checkPackageAccess( + if (candidate != null) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + candidate.checkPackageAccess(sm, ClassLoader.getClassLoader(Reflection.getCallerClass()), true); + } + } return candidate; } @@ -1496,9 +1480,13 @@ enclosingCandidate = enclosingClass; } - if (enclosingCandidate != null) - enclosingCandidate.checkPackageAccess( + if (enclosingCandidate != null) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + enclosingCandidate.checkPackageAccess(sm, ClassLoader.getClassLoader(Reflection.getCallerClass()), true); + } + } return enclosingCandidate; } @@ -1688,7 +1676,10 @@ */ @CallerSensitive 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, // something the caller might not have privilege to do. The code here @@ -1754,7 +1745,10 @@ */ @CallerSensitive 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)); } @@ -1841,7 +1835,10 @@ */ @CallerSensitive 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()); } @@ -1877,7 +1874,10 @@ */ @CallerSensitive 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)); } @@ -1928,7 +1928,10 @@ public Field getField(String name) throws NoSuchFieldException, SecurityException { 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); if (field == null) { throw new NoSuchFieldException(name); @@ -2034,10 +2037,13 @@ public Method getMethod(String name, Class... parameterTypes) throws NoSuchMethodException, SecurityException { 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); if (method == null) { - throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes)); + throw new NoSuchMethodException(methodToString(name, parameterTypes)); } return getReflectionFactory().copyMethod(method); } @@ -2092,8 +2098,12 @@ */ @CallerSensitive public Constructor getConstructor(Class... parameterTypes) - throws NoSuchMethodException, SecurityException { - checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true); + throws NoSuchMethodException, SecurityException + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + checkMemberAccess(sm, Member.PUBLIC, Reflection.getCallerClass(), true); + } return getReflectionFactory().copyConstructor( getConstructor0(parameterTypes, Member.PUBLIC)); } @@ -2136,7 +2146,10 @@ */ @CallerSensitive 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(); } @@ -2185,7 +2198,10 @@ */ @CallerSensitive 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)); } @@ -2244,7 +2260,10 @@ */ @CallerSensitive 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)); } @@ -2289,7 +2308,10 @@ */ @CallerSensitive 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)); } @@ -2338,7 +2360,10 @@ public Field getDeclaredField(String name) throws NoSuchFieldException, SecurityException { 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); if (field == null) { throw new NoSuchFieldException(name); @@ -2399,10 +2424,13 @@ public Method getDeclaredMethod(String name, Class... parameterTypes) throws NoSuchMethodException, SecurityException { 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); if (method == null) { - throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes)); + throw new NoSuchMethodException(methodToString(name, parameterTypes)); } return getReflectionFactory().copyMethod(method); } @@ -2448,8 +2476,13 @@ */ @CallerSensitive public Constructor getDeclaredConstructor(Class... parameterTypes) - throws NoSuchMethodException, SecurityException { - checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true); + throws NoSuchMethodException, SecurityException + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + checkMemberAccess(sm, Member.DECLARED, Reflection.getCallerClass(), true); + } + return getReflectionFactory().copyConstructor( getConstructor0(parameterTypes, Member.DECLARED)); } @@ -2697,51 +2730,49 @@ * *

Default policy: allow all clients access with normal Java access * control. + * + *

NOTE: should only be called if a SecurityManager is installed */ - private void checkMemberAccess(int which, Class caller, boolean checkProxyInterfaces) { - final SecurityManager s = System.getSecurityManager(); - if (s != null) { - /* 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. - * In all other cases, it requires RuntimePermission("accessDeclaredMembers") - * permission. - */ - final ClassLoader ccl = ClassLoader.getClassLoader(caller); + private void checkMemberAccess(SecurityManager sm, int which, + Class caller, boolean checkProxyInterfaces) { + /* 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. + * In all other cases, it requires RuntimePermission("accessDeclaredMembers") + * permission. + */ + final ClassLoader ccl = caller.getClassLoader0(); + if (which != Member.PUBLIC) { final ClassLoader cl = getClassLoader0(); - if (which != Member.PUBLIC) { - if (ccl != cl) { - s.checkPermission(SecurityConstants.CHECK_MEMBER_ACCESS_PERMISSION); - } + if (ccl != cl) { + sm.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 * class under the current package access policy. If access is denied, * throw a SecurityException. + * + * NOTE: this method should only be called if a SecurityManager is active */ - private void checkPackageAccess(final ClassLoader ccl, boolean checkProxyInterfaces) { - final SecurityManager s = System.getSecurityManager(); - if (s != null) { - final ClassLoader cl = getClassLoader0(); - - if (ReflectUtil.needsPackageAccessCheck(ccl, cl)) { - String name = this.getName(); - int i = name.lastIndexOf('.'); - if (i != -1) { - // skip the package access check on a proxy class in default proxy package - String pkg = name.substring(0, i); - if (!Proxy.isProxyClass(this) || ReflectUtil.isNonPublicProxyClass(this)) { - s.checkPackageAccess(pkg); - } + private void checkPackageAccess(SecurityManager sm, final ClassLoader ccl, + boolean checkProxyInterfaces) { + final ClassLoader cl = getClassLoader0(); + + if (ReflectUtil.needsPackageAccessCheck(ccl, cl)) { + String pkg = this.getPackageName(); + if (pkg != null && !pkg.isEmpty()) { + // skip the package access check on a proxy class in default proxy package + if (!Proxy.isProxyClass(this) || ReflectUtil.isNonPublicProxyClass(this)) { + sm.checkPackageAccess(pkg); } } - // check package access on the proxy interfaces - if (checkProxyInterfaces && Proxy.isProxyClass(this)) { - ReflectUtil.checkProxyPackageAccess(ccl, this.getInterfaces()); - } + } + // check package access on the proxy interfaces + if (checkProxyInterfaces && Proxy.isProxyClass(this)) { + ReflectUtil.checkProxyPackageAccess(ccl, this.getInterfaces()); } } @@ -2755,11 +2786,9 @@ while (c.isArray()) { c = c.getComponentType(); } - String baseName = c.getName(); - int index = baseName.lastIndexOf('.'); - if (index != -1) { - name = baseName.substring(0, index).replace('.', '/') - +"/"+name; + String baseName = c.getPackageName(); + if (baseName != null && !baseName.isEmpty()) { + name = baseName.replace('.', '/') + "/" + name; } } else { name = name.substring(1); @@ -3233,7 +3262,7 @@ return constructor; } } - throw new NoSuchMethodException(getName() + "." + argumentTypesToString(parameterTypes)); + throw new NoSuchMethodException(methodToString("", parameterTypes)); } // @@ -3294,8 +3323,11 @@ private native Constructor[] getDeclaredConstructors0(boolean publicOnly); 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) { for (int i = 0; i < argTypes.length; i++) { Class c = argTypes[i]; diff -r c0f498d6a804 -r d935c4d3f039 jdk/src/java.base/share/classes/java/lang/invoke/CallSite.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/CallSite.java Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/CallSite.java Thu Jan 19 07:02:33 2017 -0800 @@ -28,6 +28,8 @@ import static java.lang.invoke.MethodHandleStatics.*; 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}, * which is called its {@code target}. @@ -215,19 +217,36 @@ public abstract MethodHandle dynamicInvoker(); /*non-public*/ MethodHandle makeDynamicInvoker() { - MethodHandle getTarget = GET_TARGET.bindArgumentL(0, this); + MethodHandle getTarget = getTargetHandle().bindArgumentL(0, this); MethodHandle invoker = MethodHandles.exactInvoker(this.type()); return MethodHandles.foldArguments(invoker, getTarget); } - private static final MethodHandle GET_TARGET; - private static final MethodHandle THROW_UCS; - static { + private static @Stable MethodHandle GET_TARGET; + private static MethodHandle getTargetHandle() { + MethodHandle handle = GET_TARGET; + if (handle != null) { + return handle; + } try { - GET_TARGET = IMPL_LOOKUP. - findVirtual(CallSite.class, "getTarget", MethodType.methodType(MethodHandle.class)); - THROW_UCS = IMPL_LOOKUP. - findStatic(CallSite.class, "uninitializedCallSite", MethodType.methodType(Object.class, Object[].class)); + return GET_TARGET = IMPL_LOOKUP. + findVirtual(CallSite.class, "getTarget", + MethodType.methodType(MethodHandle.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) { throw newInternalError(e); } @@ -242,7 +261,7 @@ MethodType basicType = targetType.basicType(); MethodHandle invoker = basicType.form().cachedMethodHandle(MethodTypeForm.MH_UNINIT_CS); if (invoker == null) { - invoker = THROW_UCS.asType(basicType); + invoker = uninitializedCallSiteHandle().asType(basicType); invoker = basicType.form().setCachedMethodHandle(MethodTypeForm.MH_UNINIT_CS, invoker); } // unchecked view is OK since no values will be received or returned @@ -250,12 +269,16 @@ } // unsafe stuff: - private static final long TARGET_OFFSET; - private static final long CONTEXT_OFFSET; - static { + private static @Stable long TARGET_OFFSET; + private static long getTargetOffset() { + long offset = TARGET_OFFSET; + if (offset > 0) { + return offset; + } try { - TARGET_OFFSET = UNSAFE.objectFieldOffset(CallSite.class.getDeclaredField("target")); - CONTEXT_OFFSET = UNSAFE.objectFieldOffset(CallSite.class.getDeclaredField("context")); + offset = TARGET_OFFSET = UNSAFE.objectFieldOffset(CallSite.class.getDeclaredField("target")); + assert(offset > 0); + return offset; } catch (Exception ex) { throw newInternalError(ex); } } @@ -265,7 +288,7 @@ } /*package-private*/ MethodHandle getTargetVolatile() { - return (MethodHandle) UNSAFE.getObjectVolatile(this, TARGET_OFFSET); + return (MethodHandle) UNSAFE.getObjectVolatile(this, getTargetOffset()); } /*package-private*/ void setTargetVolatile(MethodHandle newTarget) { @@ -324,7 +347,7 @@ final int NON_SPREAD_ARG_COUNT = 3; // (caller, name, type) if (NON_SPREAD_ARG_COUNT + argv.length > MethodType.MAX_MH_ARITY) throw new BootstrapMethodError("too many bootstrap method arguments"); - MethodType bsmType = bootstrapMethod.type(); + MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argv.length); MethodHandle typedBSM = bootstrapMethod.asType(invocationType); MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT); diff -r c0f498d6a804 -r d935c4d3f039 jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java Thu Jan 19 07:02:33 2017 -0800 @@ -1128,7 +1128,7 @@ public String toMethodDescriptorString() { String desc = methodDescriptor; if (desc == null) { - desc = BytecodeDescriptor.unparse(this); + desc = BytecodeDescriptor.unparseMethod(this.rtype, this.ptypes); methodDescriptor = desc; } return desc; @@ -1256,7 +1256,7 @@ private final ReferenceQueue stale; public ConcurrentWeakInternSet() { - this.map = new ConcurrentHashMap<>(); + this.map = new ConcurrentHashMap<>(512); this.stale = new ReferenceQueue<>(); } diff -r c0f498d6a804 -r d935c4d3f039 jdk/src/java.base/share/classes/java/util/ResourceBundle.java --- a/jdk/src/java.base/share/classes/java/util/ResourceBundle.java Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/src/java.base/share/classes/java/util/ResourceBundle.java Thu Jan 19 07:02:33 2017 -0800 @@ -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. * * This code is free software; you can redistribute it and/or modify it @@ -2134,7 +2134,7 @@ /** * 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 * @see ResourceBundle.Control#getTimeToLive(String,Locale) @@ -2142,50 +2142,26 @@ @CallerSensitive public static final void clearCache() { 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 - * by the caller's module using the given class loader. + * by the given class loader. * * @param loader the class loader * @exception NullPointerException if loader is null * @since 1.6 * @see ResourceBundle.Control#getTimeToLive(String,Locale) */ - @CallerSensitive public static final void clearCache(ClassLoader 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( key -> { Module m; - return key.getCallerModule() == callerModule && - (m = key.getModule()) != null && + return (m = key.getModule()) != null && getLoader(m) == loader; } ); diff -r c0f498d6a804 -r d935c4d3f039 jdk/src/java.base/share/classes/sun/reflect/misc/ReflectUtil.java --- a/jdk/src/java.base/share/classes/sun/reflect/misc/ReflectUtil.java Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/src/java.base/share/classes/sun/reflect/misc/ReflectUtil.java Thu Jan 19 07:02:33 2017 -0800 @@ -96,7 +96,7 @@ final Class declaringClass = m.getDeclaringClass(); - checkPackageAccess(declaringClass); + privateCheckPackageAccess(sm, declaringClass); if (Modifier.isPublic(m.getModifiers()) && Modifier.isPublic(declaringClass.getModifiers())) @@ -114,9 +114,27 @@ * also check the package access on the proxy interfaces. */ 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)) { - checkProxyPackageAccess(clazz); + privateCheckProxyPackageAccess(s, clazz); } } @@ -195,15 +213,21 @@ public static void checkProxyPackageAccess(Class clazz) { SecurityManager s = System.getSecurityManager(); if (s != null) { - // check proxy interfaces if the given class is a proxy class - if (Proxy.isProxyClass(clazz)) { - for (Class intf : clazz.getInterfaces()) { - checkPackageAccess(intf); - } + privateCheckProxyPackageAccess(s, clazz); + } + } + + /** + * 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 * {@code SecurityException} if it accesses a restricted package from @@ -220,7 +244,7 @@ for (Class intf : interfaces) { ClassLoader cl = intf.getClassLoader(); if (needsPackageAccessCheck(ccl, cl)) { - checkPackageAccess(intf); + privateCheckPackageAccess(sm, intf); } } } @@ -236,10 +260,11 @@ * package that bypasses checkPackageAccess. */ public static boolean isNonPublicProxyClass(Class cls) { - String name = cls.getName(); - int i = name.lastIndexOf('.'); - String pkg = (i != -1) ? name.substring(0, i) : ""; - return Proxy.isProxyClass(cls) && !pkg.startsWith(PROXY_PACKAGE); + if (!Proxy.isProxyClass(cls)) { + return false; + } + String pkg = cls.getPackageName(); + return pkg == null || !pkg.startsWith(PROXY_PACKAGE); } /** @@ -255,7 +280,7 @@ // check if it is a valid proxy instance if (proxy == null || !Proxy.isProxyClass(proxy.getClass())) { throw new IllegalArgumentException("Not a Proxy instance"); -} + } if (Modifier.isStatic(method.getModifiers())) { throw new IllegalArgumentException("Can't handle static method"); } diff -r c0f498d6a804 -r d935c4d3f039 jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java Thu Jan 19 07:02:33 2017 -0800 @@ -165,18 +165,9 @@ throw new PluginException("TargetPlatform attribute is missing for java.base module"); } - Path bin = root.resolve(BIN_DIRNAME); + checkResourcePool(files); - // check any duplicated resource files - Map> 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())); + Path bin = root.resolve(BIN_DIRNAME); // write non-classes resource files to the image files.entries() @@ -185,13 +176,8 @@ try { accept(f); } catch (FileAlreadyExistsException e) { - // error for duplicated entries - Path path = entryToImagePath(f); - UncheckedIOException x = - new UncheckedIOException(path + " duplicated in " + - duplicates.get(path), e); - x.addSuppressed(e); - throw x; + // Should not happen! Duplicates checking already done! + throw new AssertionError("Duplicate entry!", e); } catch (IOException ioExp) { throw new UncheckedIOException(ioExp); } @@ -242,6 +228,27 @@ } } + 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> 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. * diff -r c0f498d6a804 -r d935c4d3f039 jdk/src/jdk.jlink/share/classes/module-info.java --- a/jdk/src/jdk.jlink/share/classes/module-info.java Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/src/jdk.jlink/share/classes/module-info.java Thu Jan 19 07:02:33 2017 -0800 @@ -24,8 +24,6 @@ */ module jdk.jlink { - exports jdk.tools.jlink.plugin; - requires jdk.internal.opt; requires jdk.jdeps; diff -r c0f498d6a804 -r d935c4d3f039 jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/JarFileSystem.java --- a/jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/JarFileSystem.java Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/JarFileSystem.java Thu Jan 19 07:02:33 2017 -0800 @@ -135,7 +135,7 @@ TreeMap map = new TreeMap<>(); IndexNode child = metaInfVersions.child; 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) { map.put(key, child); } @@ -149,7 +149,7 @@ */ private Integer getVersion(byte[] name, int offset) { 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) { // ignore this even though it might indicate issues with the JAR structure return null; @@ -176,7 +176,7 @@ * returns foo/bar.class */ private byte[] getRootName(IndexNode prefix, IndexNode inode) { - int offset = prefix.name.length - 1; + int offset = prefix.name.length; byte[] fullName = inode.name; return Arrays.copyOfRange(fullName, offset, fullName.length); } diff -r c0f498d6a804 -r d935c4d3f039 jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipCoder.java --- a/jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipCoder.java Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipCoder.java Thu Jan 19 07:02:33 2017 -0800 @@ -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. * * This code is free software; you can redistribute it and/or modify it @@ -34,97 +34,95 @@ import java.nio.charset.CodingErrorAction; 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 * * @author Xueming Shen */ -final class ZipCoder { +class ZipCoder { + + static class UTF8 extends ZipCoder { + UTF8() { + super(UTF_8); + } + + @Override + byte[] getBytes(String s) { // fast pass for ascii + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) > 0x7f) return super.getBytes(s); + } + return s.getBytes(ISO_8859_1); + } - String toString(byte[] ba, int length) { + @Override + 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) { CharsetDecoder cd = decoder().reset(); - int len = (int)(length * cd.maxCharsPerByte()); - char[] ca = new char[len]; - if (len == 0) - return new String(ca); - ByteBuffer bb = ByteBuffer.wrap(ba, 0, length); + 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()); + throw new IllegalArgumentException(cr.toString()); cr = cd.flush(cb); if (!cr.isUnderflow()) - throw new IllegalArgumentException(cr.toString()); + throw new IllegalArgumentException(cr.toString()); return new String(ca, 0, cb.position()); } - String toString(byte[] ba) { - return toString(ba, ba.length); - } - byte[] getBytes(String s) { CharsetEncoder ce = encoder().reset(); char[] ca = s.toCharArray(); int len = (int)(ca.length * ce.maxBytesPerChar()); byte[] ba = new byte[len]; if (len == 0) - return ba; + return ba; ByteBuffer bb = ByteBuffer.wrap(ba); CharBuffer cb = CharBuffer.wrap(ca); CoderResult cr = ce.encode(cb, bb, true); if (!cr.isUnderflow()) - throw new IllegalArgumentException(cr.toString()); + throw new IllegalArgumentException(cr.toString()); cr = ce.flush(bb); if (!cr.isUnderflow()) - throw new IllegalArgumentException(cr.toString()); + throw new IllegalArgumentException(cr.toString()); if (bb.position() == ba.length) // defensive copy? - return ba; + return ba; else - 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); + return Arrays.copyOf(ba, bb.position()); } boolean isUTF8() { - return isutf8; + return cs == UTF_8; } private Charset cs; - private boolean isutf8; - private ZipCoder utf8; private ZipCoder(Charset 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 decTL = new ThreadLocal<>(); @@ -133,10 +131,10 @@ private CharsetDecoder decoder() { CharsetDecoder dec = decTL.get(); if (dec == null) { - dec = cs.newDecoder() - .onMalformedInput(CodingErrorAction.REPORT) - .onUnmappableCharacter(CodingErrorAction.REPORT); - decTL.set(dec); + dec = cs.newDecoder() + .onMalformedInput(CodingErrorAction.REPORT) + .onUnmappableCharacter(CodingErrorAction.REPORT); + decTL.set(dec); } return dec; } @@ -144,10 +142,10 @@ private CharsetEncoder encoder() { CharsetEncoder enc = encTL.get(); if (enc == null) { - enc = cs.newEncoder() - .onMalformedInput(CodingErrorAction.REPORT) - .onUnmappableCharacter(CodingErrorAction.REPORT); - encTL.set(enc); + enc = cs.newEncoder() + .onMalformedInput(CodingErrorAction.REPORT) + .onUnmappableCharacter(CodingErrorAction.REPORT); + encTL.set(enc); } return enc; } diff -r c0f498d6a804 -r d935c4d3f039 jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java --- a/jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java Thu Jan 19 07:02:33 2017 -0800 @@ -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. * * This code is free software; you can redistribute it and/or modify it @@ -314,8 +314,8 @@ IndexNode inode = getInode(path); if (inode == null) return null; - e = new Entry(inode.name); // pseudo directory - e.method = METHOD_STORED; // STORED for dir + e = new Entry(inode.name, inode.isdir); // pseudo directory + e.method = METHOD_STORED; // STORED for dir e.mtime = e.atime = e.ctime = zfsDefaultTimeStamp; } } finally { @@ -400,7 +400,8 @@ List list = new ArrayList<>(); IndexNode child = inode.child; 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)) list.add(zp); child = child.sibling; @@ -415,14 +416,14 @@ throws IOException { checkWritable(); - dir = toDirectoryPath(dir); + // dir = toDirectoryPath(dir); beginWrite(); try { ensureOpen(); if (dir.length == 0 || exists(dir)) // root dir, or exiting dir throw new FileAlreadyExistsException(getString(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 update(e); } finally { @@ -463,7 +464,7 @@ } else { 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 if (eSrc.type == Entry.NEW || eSrc.type == Entry.FILECH) { @@ -533,7 +534,7 @@ if (!hasCreate && !hasCreateNew) throw new NoSuchFileException(getString(path)); checkParents(path); - return getOutputStream(new Entry(path, Entry.NEW)); + return getOutputStream(new Entry(path, Entry.NEW, false)); } } finally { endRead(); @@ -887,7 +888,7 @@ int off = getParentOff(path); if (off <= 1) return ROOTPATH; - return Arrays.copyOf(path, off + 1); + return Arrays.copyOf(path, off); } private static int getParentOff(byte[] path) { @@ -1075,11 +1076,9 @@ if (pos + CENHDR + nlen > limit) { zerror("invalid CEN header (bad header size)"); } - byte[] name = new byte[nlen + 1]; - System.arraycopy(cen, pos + CENHDR, name, 1, nlen); - name[0] = '/'; - IndexNode inode = new IndexNode(name, pos); + IndexNode inode = new IndexNode(cen, pos + CENHDR, nlen, pos); inodes.put(inode, inode); + // skip ext and comment pos += (CENHDR + nlen + elen + clen); } @@ -1112,7 +1111,7 @@ private boolean hasUpdate = false; // 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) { beginWrite(); @@ -1312,16 +1311,7 @@ IndexNode getInode(byte[] path) { if (path == null) throw new NullPointerException("path"); - IndexNode key = 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; + return inodes.get(IndexNode.keyOf(path)); } Entry getEntry(byte[] path) throws IOException { @@ -1782,8 +1772,11 @@ int hashcode; // node is hashable/hashed by its name int pos = -1; // position in cen table, -1 menas the // entry does not exists in zip file - IndexNode(byte[] name) { + boolean isdir; + + IndexNode(byte[] name, boolean isdir) { name(name); + this.isdir = isdir; this.pos = -1; } @@ -1792,8 +1785,28 @@ 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 cachedKey = new ThreadLocal<>(); + 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) { @@ -1807,8 +1820,7 @@ } boolean isDir() { - return name != null && - (name.length == 0 || name[name.length - 1] == '/'); + return isdir; } public boolean equals(Object other) { @@ -1865,8 +1877,9 @@ Entry() {} - Entry(byte[] name) { + Entry(byte[] name, boolean isdir) { name(name); + this.isdir = isdir; this.mtime = this.ctime = this.atime = System.currentTimeMillis(); this.crc = 0; this.size = 0; @@ -1874,13 +1887,14 @@ this.method = METHOD_DEFLATED; } - Entry(byte[] name, int type) { - this(name); + Entry(byte[] name, int type, boolean isdir) { + this(name, isdir); this.type = type; } Entry (Entry e, int type) { name(e.name); + this.isdir = e.isdir; this.version = e.version; this.ctime = e.ctime; this.atime = e.atime; @@ -1902,7 +1916,7 @@ } Entry (byte[] name, Path file, int type) { - this(name, type); + this(name, type, false); this.file = file; this.method = METHOD_STORED; } @@ -1948,6 +1962,7 @@ locoff = CENOFF(cen, pos); pos += CENHDR; this.name = inode.name; + this.isdir = inode.isdir; this.hashcode = inode.hashcode; pos += nlen; @@ -1974,9 +1989,10 @@ int elenEXTT = 0; // extra for Extended Timestamp 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 eoff = 0; int clen = (comment != null) ? comment.length : 0; @@ -2037,7 +2053,7 @@ writeShort(os, 0); // internal file attributes (unused) writeInt(os, 0); // external file attributes (unused) writeInt(os, locoff0); // relative offset of local header - writeBytes(os, name, 1, nlen); + writeBytes(os, zname, 1, nlen); if (elen64 != 0) { writeShort(os, EXTID_ZIP64);// Zip64 extra writeShort(os, elen64 - 4); // size of "this" extra block @@ -2075,87 +2091,13 @@ } ///////////////////// 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 { writeInt(os, LOCSIG); // LOC header signature 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; boolean foundExtraTime = false; // if extra timestamp present int eoff = 0; @@ -2214,7 +2156,7 @@ } writeShort(os, nlen); writeShort(os, elen + elen64 + elenNTFS + elenEXTT); - writeBytes(os, name, 1, nlen); + writeBytes(os, zname, 1, nlen); if (elen64 != 0) { writeShort(os, EXTID_ZIP64); writeShort(os, 16); @@ -2551,7 +2493,7 @@ private void buildNodeTree() throws IOException { beginWrite(); try { - IndexNode root = new IndexNode(ROOTPATH); + IndexNode root = new IndexNode(ROOTPATH, true); IndexNode[] nodes = inodes.keySet().toArray(new IndexNode[0]); inodes.put(root, root); ParentLookup lookup = new ParentLookup(); @@ -2564,7 +2506,7 @@ root.child = node; break; } - lookup = lookup.as(node.name, off + 1); + lookup = lookup.as(node.name, off); if (inodes.containsKey(lookup)) { parent = inodes.get(lookup); node.sibling = parent.child; @@ -2572,7 +2514,7 @@ break; } // 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); node.sibling = parent.child; parent.child = node; diff -r c0f498d6a804 -r d935c4d3f039 jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipPath.java --- a/jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipPath.java Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipPath.java Thu Jan 19 07:02:33 2017 -0800 @@ -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. * * This code is free software; you can redistribute it and/or modify it @@ -59,8 +59,7 @@ } else { if (zfs.zc.isUTF8()) { this.path = normalize(path); - } else { - // see normalize(String); + } else { // see normalize(String); this.path = normalize(zfs.getString(path)); } } @@ -68,12 +67,7 @@ ZipPath(ZipFileSystem zfs, String path) { this.zfs = zfs; - if (zfs.zc.isUTF8()) { - this.path = normalize(zfs.getBytes(path)); - } else { - // see normalize(String); - this.path = normalize(path); - } + this.path = normalize(path); } @Override @@ -84,33 +78,31 @@ return null; } - @Override + @Override public Path getFileName() { - initOffsets(); - int count = offsets.length; - if (count == 0) - return null; // no elements so no name - if (count == 1 && path[0] != '/') + int off = path.length; + if (off == 0 || off == 1 && path[0] == '/') + return null; + while (--off >= 0 && path[off] != '/') {} + if (off < 0) return this; - int lastOffset = offsets[count-1]; - int len = path.length - lastOffset; - byte[] result = new byte[len]; - System.arraycopy(path, lastOffset, result, 0, len); - return new ZipPath(zfs, result); + off++; + byte[] result = new byte[path.length - off]; + System.arraycopy(path, off, result, 0, result.length); + return new ZipPath(getFileSystem(), result, true); } @Override public ZipPath getParent() { - initOffsets(); - int count = offsets.length; - if (count == 0) // no elements so no parent + int off = path.length; + if (off == 0 || off == 1 && path[0] == '/') return null; - int len = offsets[count-1] - 1; - if (len <= 0) // parent is root only (may be null) + while (--off >= 0 && path[off] != '/') {} + if (off <= 0) return getRoot(); - byte[] result = new byte[len]; - System.arraycopy(path, 0, result, 0, len); - return new ZipPath(zfs, result); + byte[] result = new byte[off]; + System.arraycopy(path, 0, result, 0, off); + return new ZipPath(getFileSystem(), result, true); } @Override @@ -277,30 +269,36 @@ @Override public boolean isAbsolute() { - return (this.path.length > 0 && path[0] == '/'); + return path.length > 0 && path[0] == '/'; } @Override public ZipPath resolve(Path other) { - final ZipPath o = checkPath(other); - int tlen = this.path.length; - if (tlen == 0 || o.isAbsolute()) + ZipPath o = checkPath(other); + if (o.path.length == 0) + return this; + if (o.isAbsolute() || this.path.length == 0) return o; - int olen = o.path.length; - if (olen == 0) - return this; + return resolve(o.path); + } + + // opath is normalized, just concat + private ZipPath resolve(byte[] opath) { 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]; - System.arraycopy(path, 0, resolved, 0, tlen); - System.arraycopy(o.path, 0, resolved, tlen, olen); + System.arraycopy(tpath, 0, resolved, 0, tlen); + System.arraycopy(opath, 0, resolved, tlen, olen); } else { resolved = new byte[tlen + 1 + olen]; - System.arraycopy(path, 0, resolved, 0, tlen); + System.arraycopy(tpath, 0, resolved, 0, 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 @@ -351,7 +349,12 @@ @Override 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 @@ -455,8 +458,9 @@ return normalize(path, i - 1); prevC = c; } - if (len > 1 && prevC == '/') + if (len > 1 && prevC == '/') { return Arrays.copyOf(path, len - 1); + } return path; } @@ -490,6 +494,8 @@ // to avoid incorrectly normalizing byte '0x5c' (as '\') // to '/'. private byte[] normalize(String path) { + if (zfs.zc.isUTF8()) + return normalize(zfs.getBytes(path)); int len = path.length(); if (len == 0) return new byte[0]; @@ -533,7 +539,8 @@ // Remove DotSlash(./) and resolve DotDot (..) components private byte[] getResolved() { 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(); } } @@ -976,5 +983,4 @@ } return sb.toString(); } - } diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/ProblemList.txt --- a/jdk/test/ProblemList.txt Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/test/ProblemList.txt Thu Jan 19 07:02:33 2017 -0800 @@ -258,6 +258,8 @@ tools/jlink/multireleasejar/JLinkMultiReleaseJarTest.java 8169971 windows-x64 +tools/jlink/CustomPluginTest.java 8172864 generic-all + ############################################################################ # jdk_jdi diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/TEST.ROOT --- a/jdk/test/TEST.ROOT Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/test/TEST.ROOT Thu Jan 19 07:02:33 2017 -0800 @@ -26,8 +26,8 @@ # Allow querying of various System properties in @requires clauses requires.properties=sun.arch.data.model java.runtime.name -# Tests using jtreg 4.2 b04 features -requiredVersion=4.2 b04 +# Tests using jtreg 4.2 b05 features +requiredVersion=4.2 b05 # Path to libraries in the topmost test directory. This is needed so @library # does not need ../../ notation to reach them diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/java/util/logging/modules/LogManagerInModule/LogManagerInModuleTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/logging/modules/LogManagerInModule/LogManagerInModuleTest.java Thu Jan 19 07:02:33 2017 -0800 @@ -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()); + } + } + +} diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/java/util/logging/modules/LogManagerInModule/logging.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/logging/modules/LogManagerInModule/logging.properties Thu Jan 19 07:02:33 2017 -0800 @@ -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: +# : [] +# +# 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 diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/java/util/logging/modules/LogManagerInModule/test.config/module-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/logging/modules/LogManagerInModule/test.config/module-info.java Thu Jan 19 07:02:33 2017 -0800 @@ -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; +} diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/java/util/logging/modules/LogManagerInModule/test.config/test/config/LogConfig.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/logging/modules/LogManagerInModule/test.config/test/config/LogConfig.java Thu Jan 19 07:02:33 2017 -0800 @@ -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 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); + } +} diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/java/util/logging/modules/LogManagerInModule/test.handlers/module-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/logging/modules/LogManagerInModule/test.handlers/module-info.java Thu Jan 19 07:02:33 2017 -0800 @@ -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; +} diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/java/util/logging/modules/LogManagerInModule/test.handlers/test/handlers/TestHandler.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/logging/modules/LogManagerInModule/test.handlers/test/handlers/TestHandler.java Thu Jan 19 07:02:33 2017 -0800 @@ -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 { + } + +} diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/java/util/logging/modules/LogManagerInModule/test.logmanager/module-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/logging/modules/LogManagerInModule/test.logmanager/module-info.java Thu Jan 19 07:02:33 2017 -0800 @@ -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; +} diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/java/util/logging/modules/LogManagerInModule/test.logmanager/test/logmanager/TestLogManager.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/logging/modules/LogManagerInModule/test.logmanager/test/logmanager/TestLogManager.java Thu Jan 19 07:02:33 2017 -0800 @@ -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 { + +} diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/jdk/nio/zipfs/PathOps.java --- a/jdk/test/jdk/nio/zipfs/PathOps.java Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/test/jdk/nio/zipfs/PathOps.java Thu Jan 19 07:02:33 2017 -0800 @@ -31,7 +31,7 @@ /** * * @test - * @bug 8038500 8040059 8139956 8146754 + * @bug 8038500 8040059 8139956 8146754 8172921 * @summary Tests path operations for zip provider. * * @run main PathOps @@ -180,6 +180,13 @@ 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) { out.format("test resolveSibling %s\n", other); checkPath(); @@ -384,6 +391,30 @@ .resolve("", "") .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 test("foo") diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/tools/jar/multiRelease/ApiValidatorTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/jar/multiRelease/ApiValidatorTest.java Thu Jan 19 07:02:33 2017 -0800 @@ -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", "public 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); + } +} \ No newline at end of file diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/tools/jar/multiRelease/Basic.java --- a/jdk/test/tools/jar/multiRelease/Basic.java Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/test/tools/jar/multiRelease/Basic.java Thu Jan 19 07:02:33 2017 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,69 +23,59 @@ /* * @test - * @library /test/lib + * @library /test/lib /lib/testlibrary * @modules java.base/jdk.internal.misc * jdk.compiler * 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 */ import static org.testng.Assert.*; +import jdk.testlibrary.FileUtils; import org.testng.annotations.*; -import java.io.*; +import java.io.File; import java.nio.file.*; -import java.nio.file.attribute.*; 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 java.util.jar.JarFile; +import java.util.zip.ZipFile; -import jdk.test.lib.JDKToolFinder; -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", "."); +public class Basic extends MRTestBase { @Test // create a regular, non-multi-release jar - public void test00() throws IOException { + public void test00() 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(), ".") - .assertSuccess(); + .shouldHaveExitValue(SUCCESS); checkMultiRelease(jarfile, false); - Map names = Map.of( + Map names = Map.of( "version/Main.class", - new String[] {"base", "version", "Main.class"}, + new String[]{"base", "version", "Main.class"}, "version/Version.class", - new String[] {"base", "version", "Version.class"} + new String[]{"base", "version", "Version.class"} ); compare(jarfile, names); - delete(jarfile); - deleteDir(Paths.get(usr, "classes")); + FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile)); + FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes")); } @Test // create a multi-release jar - public void test01() throws IOException { + public void test01() throws Throwable { String jarfile = "test.jar"; compile("test01"); @@ -94,68 +84,96 @@ jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".", "--release", "9", "-C", classes.resolve("v9").toString(), ".", "--release", "10", "-C", classes.resolve("v10").toString(), ".") - .assertSuccess(); + .shouldHaveExitValue(SUCCESS); checkMultiRelease(jarfile, true); - Map names = Map.of( + Map names = Map.of( "version/Main.class", - new String[] {"base", "version", "Main.class"}, + new String[]{"base", "version", "Main.class"}, "version/Version.class", - new String[] {"base", "version", "Version.class"}, + new String[]{"base", "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", - new String[] {"v10", "version", "Version.class"} + new String[]{"v10", "version", "Version.class"} ); compare(jarfile, names); - delete(jarfile); - deleteDir(Paths.get(usr, "classes")); + FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile)); + 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 // update a regular jar to a multi-release jar - public void test02() throws IOException { + public void test02() 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(), ".") - .assertSuccess(); + .shouldHaveExitValue(SUCCESS); checkMultiRelease(jarfile, false); - jar("uf", jarfile, "--release", "9", "-C", classes.resolve("v9").toString(), ".") - .assertSuccess(); + jar("uf", jarfile, + "--release", "9", "-C", classes.resolve("v9").toString(), ".") + .shouldHaveExitValue(SUCCESS); checkMultiRelease(jarfile, true); - Map names = Map.of( + Map names = Map.of( "version/Main.class", - new String[] {"base", "version", "Main.class"}, + new String[]{"base", "version", "Main.class"}, "version/Version.class", - new String[] {"base", "version", "Version.class"}, + new String[]{"base", "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); - delete(jarfile); - deleteDir(Paths.get(usr, "classes")); + FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile)); + FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes")); } @Test // replace a base entry and a versioned entry - public void test03() throws IOException { + public void test03() throws Throwable { String jarfile = "test.jar"; compile("test01"); //use same data as test01 @@ -163,19 +181,19 @@ Path classes = Paths.get("classes"); jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".", "--release", "9", "-C", classes.resolve("v9").toString(), ".") - .assertSuccess(); + .shouldHaveExitValue(SUCCESS); checkMultiRelease(jarfile, true); - Map names = Map.of( + Map names = Map.of( "version/Main.class", - new String[] {"base", "version", "Main.class"}, + new String[]{"base", "version", "Main.class"}, "version/Version.class", - new String[] {"base", "version", "Version.class"}, + new String[]{"base", "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); @@ -184,25 +202,25 @@ // version/Version.class entry in versions/9 section jar("uf", jarfile, "-C", classes.resolve("v9").toString(), "version", "--release", "9", "-C", classes.resolve("v10").toString(), ".") - .assertSuccess(); + .shouldHaveExitValue(SUCCESS); checkMultiRelease(jarfile, true); names = Map.of( "version/Main.class", - new String[] {"base", "version", "Main.class"}, + new String[]{"base", "version", "Main.class"}, "version/Version.class", - new String[] {"v9", "version", "Version.class"}, + new String[]{"v9", "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); - delete(jarfile); - deleteDir(Paths.get(usr, "classes")); + FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile)); + FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes")); } /* @@ -211,7 +229,7 @@ @Test // 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"; compile("test01"); //use same data as test01 @@ -224,18 +242,16 @@ jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".", "--release", "9", "-C", classes.resolve("v9").toString(), ".") - .assertFailure() - .resultChecker(r -> - assertTrue(r.output.contains("different api from earlier"), r.output) - ); + .shouldNotHaveExitValue(SUCCESS) + .shouldContain("different api from earlier"); - delete(jarfile); - deleteDir(Paths.get(usr, "classes")); + FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile)); + FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes")); } @Test // META-INF/versions/9 contains an extra public class - public void test05() throws IOException { + public void test05() throws Throwable { String jarfile = "test.jar"; compile("test01"); //use same data as test01 @@ -248,18 +264,16 @@ jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".", "--release", "9", "-C", classes.resolve("v9").toString(), ".") - .assertFailure() - .resultChecker(r -> - assertTrue(r.output.contains("contains a new public class"), r.output) - ); + .shouldNotHaveExitValue(SUCCESS) + .shouldContain("contains a new public class"); - delete(jarfile); - deleteDir(Paths.get(usr, "classes")); + FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile)); + FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes")); } @Test // 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"; compile("test01"); //use same data as test01 @@ -272,16 +286,16 @@ jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".", "--release", "9", "-C", classes.resolve("v9").toString(), ".") - .assertSuccess(); + .shouldHaveExitValue(SUCCESS); - delete(jarfile); - deleteDir(Paths.get(usr, "classes")); + FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile)); + FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes")); } @Test // META-INF/versions/9 contains an identical class to base entry class // this is okay but produces warning - public void test07() throws IOException { + public void test07() throws Throwable { String jarfile = "test.jar"; compile("test01"); //use same data as test01 @@ -294,19 +308,42 @@ jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".", "--release", "9", "-C", classes.resolve("v9").toString(), ".") - .assertSuccess() - .resultChecker(r -> - assertTrue(r.outputContains("contains a class that is identical"), r.output) - ); + .shouldHaveExitValue(SUCCESS) + .shouldContain("contains a class that") + .shouldContain("is identical"); + + FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile)); + 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"; - delete(jarfile); - deleteDir(Paths.get(usr, "classes")); + 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 // resources with same name in different versions // this is okay but produces warning - public void test08() throws IOException { + public void test08() throws Throwable { String jarfile = "test.jar"; compile("test01"); //use same data as test01 @@ -320,10 +357,8 @@ jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".", "--release", "9", "-C", classes.resolve("v9").toString(), ".") - .assertSuccess() - .resultChecker(r -> - assertTrue(r.output.isEmpty(), r.output) - ); + .shouldHaveExitValue(SUCCESS) + .shouldBeEmpty(); // now add a different resource with same name to META-INF/version/9 Files.copy(source.resolve("Main.java"), classes.resolve("v9") @@ -331,18 +366,16 @@ jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".", "--release", "9", "-C", classes.resolve("v9").toString(), ".") - .assertSuccess() - .resultChecker(r -> - assertTrue(r.output.contains("multiple resources with same name"), r.output) - ); + .shouldHaveExitValue(SUCCESS) + .shouldContain("multiple resources with same name"); - delete(jarfile); - deleteDir(Paths.get(usr, "classes")); + FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile)); + FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes")); } @Test // 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"; compile("test01"); //use same data as test01 @@ -355,18 +388,16 @@ jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".", "--release", "9", "-C", classes.resolve("v9").toString(), ".") - .assertFailure() - .resultChecker(r -> - assertTrue(r.output.contains("names do not match"), r.output) - ); + .shouldNotHaveExitValue(SUCCESS) + .shouldContain("names do not match"); - delete(jarfile); - deleteDir(Paths.get(usr, "classes")); + FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile)); + FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes")); } @Test // assure that basic nested classes are acceptable - public void test10() throws IOException { + public void test10() throws Throwable { String jarfile = "test.jar"; compile("test01"); //use same data as test01 @@ -383,15 +414,15 @@ jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".", "--release", "9", "-C", classes.resolve("v9").toString(), ".") - .assertSuccess(); + .shouldHaveExitValue(SUCCESS); - delete(jarfile); - deleteDir(Paths.get(usr, "classes")); + FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile)); + FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes")); } @Test // 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"; compile("test01"); //use same data as test01 @@ -409,30 +440,29 @@ source = Paths.get(src, "data", "test10", "v9", "version"); javac(classes.resolve("v9"), source.resolve("Nested.java")); - jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".", + List output = jar("cf", jarfile, + "-C", classes.resolve("base").toString(), ".", "--release", "9", "-C", classes.resolve("v9").toString(), ".") - .assertFailure() - .resultChecker(r -> { - 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]); - }); + .shouldNotHaveExitValue(SUCCESS) + .asLines(); - delete(jarfile); - deleteDir(Paths.get(usr, "classes")); + assertTrue(output.size() == 4); + 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 // 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"; compile("test01"); //use same data as test01 @@ -452,178 +482,59 @@ jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".", "--release", "9", "-C", classes.resolve("v9").toString(), ".") - .assertFailure() - .resultChecker(r -> - assertTrue(r.outputContains("an isolated nested class"), r.output) - ); - - delete(jarfile); - deleteDir(Paths.get(usr, "classes")); - } - - /* - * Test Infrastructure - */ - 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"); - 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")); - } - - 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); - } - } + .shouldNotHaveExitValue(SUCCESS) + .shouldContain("an isolated nested class"); - // 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 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); - } - } - } - - private void delete(String name) throws IOException { - Files.deleteIfExists(Paths.get(usr, name)); - } - - private void deleteDir(Path dir) throws IOException { - Files.walkFileTree(dir, new SimpleFileVisitor() { - @Override - 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; - } - }); + FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile)); + FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes")); } - /* - * The following methods were taken from modular jar and other jar tests - */ + @Test + public void testCustomManifest() throws Throwable { + String jarfile = "test.jar"; - void javac(Path dest, Path... sourceFiles) throws IOException { - String javac = JDKToolFinder.getJDKTool("javac"); + compile("test01"); - List 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)); + Path classes = Paths.get("classes"); + Path manifest = Paths.get("Manifest.txt"); - quickFail(run(new ProcessBuilder(commands))); - } + // create + Files.write(manifest, "Class-Path: MyUtils.jar\n".getBytes()); - Result jarWithStdin(File stdinSource, String... args) { - String jar = JDKToolFinder.getJDKTool("jar"); - List 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); - } + jar("cfm", jarfile, manifest.toString(), + "-C", classes.resolve("base").toString(), ".", + "--release", "10", "-C", classes.resolve("v10").toString(), ".") + .shouldHaveExitValue(SUCCESS) + .shouldBeEmpty(); - 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); + 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"); } - 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); + // update + Files.write(manifest, "Multi-release: false\n".getBytes()); + + jar("ufm", jarfile, manifest.toString(), + "-C", classes.resolve("base").toString(), ".", + "--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 { - 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 r) { r.accept(this); return this; } + FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile)); + FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes")); } } diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/tools/jar/multiRelease/Basic1.java --- a/jdk/test/tools/jar/multiRelease/Basic1.java Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/test/tools/jar/multiRelease/Basic1.java Thu Jan 19 07:02:33 2017 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,76 +28,65 @@ * jdk.compiler * jdk.jartool * @build jdk.test.lib.JDKToolFinder jdk.test.lib.Utils + * @build MRTestBase * @run testng Basic1 */ -import static org.testng.Assert.*; - import org.testng.annotations.*; -import java.io.*; import java.nio.file.*; 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; -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", "."); +public class Basic1 extends MRTestBase { @BeforeTest - public void setup() throws IOException { + public void setup() throws Throwable { String test = "test01"; - Path classes = Paths.get("classes", "base"); - Files.createDirectories(classes); + Path classes = Paths.get("classes"); + + Path base = classes.resolve("base"); + Files.createDirectories(base); Path source = Paths.get(src, "data", test, "base", "version"); - javac(classes, source.resolve("Main.java"), source.resolve("Version.java")); + javac(base, source.resolve("Main.java"), source.resolve("Version.java")); - Path v9 = Paths.get("v9"); + Path v9 = classes.resolve("v9"); Files.createDirectories(v9); source = Paths.get(src, "data", test, "v9", "version"); javac(v9, source.resolve("Version.java")); - Path v10 = Paths.get("v10"); + Path v10 = classes.resolve("v10"); Files.createDirectories(v10); source = Paths.get(src, "data", test, "v10", "version"); 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); source = Paths.get(src, "data", test, "v10", "version"); javac(v10_1, source.resolve("Version.java")); } @Test - public void test() throws IOException { + public void test() throws Throwable { String jarfile = "test.jar"; Path classes = Paths.get("classes"); - Path v9 = Paths.get("v9"); - Path v10 = Paths.get("v10"); + + Path base = classes.resolve("base"); + Path v9 = classes.resolve("v9"); + Path v10 = classes.resolve("v10"); - jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".", - "--release", "9", "-C", v9.toString(), ".", - "--release", "10", "-C", v10.toString(), ".") - .assertSuccess(); + jar("cf", jarfile, "-C", base.toString(), ".", + "--release", "9", "-C", v9.toString(), ".", + "--release", "10", "-C", v10.toString(), ".") + .shouldHaveExitValue(SUCCESS); checkMultiRelease(jarfile, true); - Map names = Map.of( - "version/Main.class", - new String[] {"classes", "base", "version", "Main.class"}, + Map names = Map.of( + "version/Main.class", + new String[]{"base", "version", "Main.class"}, - "version/Version.class", - new String[] {"classes", "base", "version", "Version.class"}, + "version/Version.class", + new String[]{"base", "version", "Version.class"}, "META-INF/versions/9/version/Version.class", new String[] {"v9", "version", "Version.class"}, @@ -109,144 +98,16 @@ compare(jarfile, names); } - @Test - public void testFail() throws IOException { + public void testFail() throws Throwable { String jarfile = "test.jar"; Path classes = Paths.get("classes"); - Path v10 = Paths.get("v10_1"); - - jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".", - "--release", "10", "-C", v10.toString(), ".") - .assertFailure() - .outputContains("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 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 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 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); - } + Path base = classes.resolve("base"); + Path v10 = classes.resolve("v10_1"); - 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 r) { r.accept(this); return this; } + jar("cf", jarfile, "-C", base.toString(), ".", + "--release", "10", "-C", v10.toString(), ".") + .shouldNotHaveExitValue(SUCCESS) + .shouldContain("unexpected versioned entry META-INF/versions/"); } } diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/tools/jar/multiRelease/MRTestBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/jar/multiRelease/MRTestBase.java Thu Jan 19 07:02:33 2017 -0800 @@ -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 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 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 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); + } +} \ No newline at end of file diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/tools/jar/multiRelease/data/test04/v9/version/Version.java --- a/jdk/test/tools/jar/multiRelease/data/test04/v9/version/Version.java Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/test/tools/jar/multiRelease/data/test04/v9/version/Version.java Thu Jan 19 07:02:33 2017 -0800 @@ -8,7 +8,7 @@ protected void doNothing() { } - // extra publc method + // extra public method public void anyName() { } } diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/tools/jlink/DefaultProviderTest.java --- a/jdk/test/tools/jlink/DefaultProviderTest.java Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/test/tools/jlink/DefaultProviderTest.java Thu Jan 19 07:02:33 2017 -0800 @@ -44,6 +44,7 @@ * @modules java.base/jdk.internal.jimage * jdk.jdeps/com.sun.tools.classfile * jdk.jlink/jdk.tools.jlink.internal + * jdk.jlink/jdk.tools.jlink.plugin * jdk.jlink/jdk.tools.jmod * jdk.jlink/jdk.tools.jimage * jdk.compiler diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/tools/jlink/ImageFileCreatorTest.java --- a/jdk/test/tools/jlink/ImageFileCreatorTest.java Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/test/tools/jlink/ImageFileCreatorTest.java Thu Jan 19 07:02:33 2017 -0800 @@ -48,6 +48,7 @@ * @author Jean-Francois Denise * @modules jdk.jlink/jdk.tools.jlink.internal * jdk.jlink/jdk.tools.jlink.builder + * jdk.jlink/jdk.tools.jlink.plugin * java.base/jdk.internal.jimage * @run main/othervm -verbose:gc -Xmx1g ImageFileCreatorTest */ diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/tools/jlink/ImageFilePoolTest.java --- a/jdk/test/tools/jlink/ImageFilePoolTest.java Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/test/tools/jlink/ImageFilePoolTest.java Thu Jan 19 07:02:33 2017 -0800 @@ -26,6 +26,7 @@ * @summary Test a pool containing external files. * @author Andrei Eremeev * @modules jdk.jlink/jdk.tools.jlink.internal + * jdk.jlink/jdk.tools.jlink.plugin * @run build ImageFilePoolTest * @run main ImageFilePoolTest */ diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/tools/jlink/IntegrationTest.java --- a/jdk/test/tools/jlink/IntegrationTest.java Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/test/tools/jlink/IntegrationTest.java Thu Jan 19 07:02:33 2017 -0800 @@ -62,6 +62,7 @@ * jdk.jlink/jdk.tools.jlink.builder * jdk.jlink/jdk.tools.jlink.internal * jdk.jlink/jdk.tools.jlink.internal.plugins + * jdk.jlink/jdk.tools.jlink.plugin * jdk.jlink/jdk.tools.jmod * jdk.jlink/jdk.tools.jimage * jdk.compiler diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/tools/jlink/JLink2Test.java --- a/jdk/test/tools/jlink/JLink2Test.java Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/test/tools/jlink/JLink2Test.java Thu Jan 19 07:02:33 2017 -0800 @@ -29,6 +29,7 @@ * @modules java.base/jdk.internal.jimage * jdk.jdeps/com.sun.tools.classfile * jdk.jlink/jdk.tools.jlink.internal + * jdk.jlink/jdk.tools.jlink.plugin * jdk.jlink/jdk.tools.jmod * jdk.jlink/jdk.tools.jimage * jdk.compiler diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/tools/jlink/JLinkOptionsTest.java --- a/jdk/test/tools/jlink/JLinkOptionsTest.java Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/test/tools/jlink/JLinkOptionsTest.java Thu Jan 19 07:02:33 2017 -0800 @@ -39,6 +39,7 @@ * @modules java.base/jdk.internal.jimage * jdk.jdeps/com.sun.tools.classfile * jdk.jlink/jdk.tools.jlink.internal + * jdk.jlink/jdk.tools.jlink.plugin * jdk.jlink/jdk.tools.jmod * jdk.jlink/jdk.tools.jimage * jdk.compiler diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/tools/jlink/JLinkPostProcessingTest.java --- a/jdk/test/tools/jlink/JLinkPostProcessingTest.java Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/test/tools/jlink/JLinkPostProcessingTest.java Thu Jan 19 07:02:33 2017 -0800 @@ -46,6 +46,7 @@ * @modules java.base/jdk.internal.jimage * jdk.jdeps/com.sun.tools.classfile * jdk.jlink/jdk.tools.jlink.internal + * jdk.jlink/jdk.tools.jlink.plugin * jdk.jlink/jdk.tools.jmod * jdk.jlink/jdk.tools.jimage * jdk.compiler diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/tools/jlink/JLinkTest.java --- a/jdk/test/tools/jlink/JLinkTest.java Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/test/tools/jlink/JLinkTest.java Thu Jan 19 07:02:33 2017 -0800 @@ -48,6 +48,7 @@ * @modules java.base/jdk.internal.jimage * jdk.jdeps/com.sun.tools.classfile * jdk.jlink/jdk.tools.jlink.internal + * jdk.jlink/jdk.tools.jlink.plugin * jdk.jlink/jdk.tools.jimage * jdk.compiler * @build tests.* diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/tools/jlink/ResourceDuplicateCheckTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/jlink/ResourceDuplicateCheckTest.java Thu Jan 19 07:02:33 2017 -0800 @@ -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()); + } +} diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/tools/jlink/ResourcePoolTest.java --- a/jdk/test/tools/jlink/ResourcePoolTest.java Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/test/tools/jlink/ResourcePoolTest.java Thu Jan 19 07:02:33 2017 -0800 @@ -26,6 +26,7 @@ * @summary Test a pool containing jimage resources and classes. * @author Jean-Francois Denise * @modules jdk.jlink/jdk.tools.jlink.internal + * jdk.jlink/jdk.tools.jlink.plugin * @run build ResourcePoolTest * @run main ResourcePoolTest */ diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/tools/jlink/plugins/CompressorPluginTest.java --- a/jdk/test/tools/jlink/plugins/CompressorPluginTest.java Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/test/tools/jlink/plugins/CompressorPluginTest.java Thu Jan 19 07:02:33 2017 -0800 @@ -28,6 +28,7 @@ * @modules java.base/jdk.internal.jimage.decompressor * jdk.jlink/jdk.tools.jlink.internal * jdk.jlink/jdk.tools.jlink.internal.plugins + * jdk.jlink/jdk.tools.jlink.plugin * @run main CompressorPluginTest */ import java.net.URI; diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/tools/jlink/plugins/ExcludeFilesPluginTest.java --- a/jdk/test/tools/jlink/plugins/ExcludeFilesPluginTest.java Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/test/tools/jlink/plugins/ExcludeFilesPluginTest.java Thu Jan 19 07:02:33 2017 -0800 @@ -27,6 +27,7 @@ * @author Jean-Francois Denise * @modules jdk.jlink/jdk.tools.jlink.internal * jdk.jlink/jdk.tools.jlink.internal.plugins + * jdk.jlink/jdk.tools.jlink.plugin * @run main ExcludeFilesPluginTest */ diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/tools/jlink/plugins/ExcludePluginTest.java --- a/jdk/test/tools/jlink/plugins/ExcludePluginTest.java Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/test/tools/jlink/plugins/ExcludePluginTest.java Thu Jan 19 07:02:33 2017 -0800 @@ -27,6 +27,7 @@ * @author Jean-Francois Denise * @modules jdk.jlink/jdk.tools.jlink.internal * jdk.jlink/jdk.tools.jlink.internal.plugins + * jdk.jlink/jdk.tools.jlink.plugin * @run main ExcludePluginTest */ diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/tools/jlink/plugins/ExcludeVMPluginTest.java --- a/jdk/test/tools/jlink/plugins/ExcludeVMPluginTest.java Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/test/tools/jlink/plugins/ExcludeVMPluginTest.java Thu Jan 19 07:02:33 2017 -0800 @@ -27,6 +27,7 @@ * @author Jean-Francois Denise * @modules jdk.jlink/jdk.tools.jlink.internal * jdk.jlink/jdk.tools.jlink.internal.plugins + * jdk.jlink/jdk.tools.jlink.plugin * @run main ExcludeVMPluginTest */ import java.io.ByteArrayInputStream; diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/tools/jlink/plugins/IncludeLocalesPluginTest.java --- a/jdk/test/tools/jlink/plugins/IncludeLocalesPluginTest.java Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/test/tools/jlink/plugins/IncludeLocalesPluginTest.java Thu Jan 19 07:02:33 2017 -0800 @@ -48,6 +48,7 @@ * jdk.jdeps/com.sun.tools.classfile * jdk.jlink/jdk.tools.jlink.internal * jdk.jlink/jdk.tools.jlink.internal.plugins + * jdk.jlink/jdk.tools.jlink.plugin * jdk.jlink/jdk.tools.jmod * jdk.jlink/jdk.tools.jimage * jdk.compiler diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/tools/jlink/plugins/LastSorterTest.java --- a/jdk/test/tools/jlink/plugins/LastSorterTest.java Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/test/tools/jlink/plugins/LastSorterTest.java Thu Jan 19 07:02:33 2017 -0800 @@ -25,8 +25,9 @@ * @test * @summary Test last sorter property * @author Jean-Francois Denise - * @modules jdk.jlink/jdk.tools.jlink.internal - * jdk.jlink/jdk.tools.jlink + * @modules jdk.jlink/jdk.tools.jlink + * jdk.jlink/jdk.tools.jlink.internal + * jdk.jlink/jdk.tools.jlink.plugin * @run main/othervm LastSorterTest */ diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/tools/jlink/plugins/PluginsNegativeTest.java --- a/jdk/test/tools/jlink/plugins/PluginsNegativeTest.java Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/test/tools/jlink/plugins/PluginsNegativeTest.java Thu Jan 19 07:02:33 2017 -0800 @@ -25,8 +25,9 @@ * @test * @summary Negative test for ImagePluginStack. * @author Andrei Eremeev - * @modules jdk.jlink/jdk.tools.jlink.internal - * jdk.jlink/jdk.tools.jlink + * @modules jdk.jlink/jdk.tools.jlink + * jdk.jlink/jdk.tools.jlink.internal + * jdk.jlink/jdk.tools.jlink.plugin * @run main/othervm PluginsNegativeTest */ import java.lang.reflect.Layer; diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/tools/jlink/plugins/PrevisitorTest.java --- a/jdk/test/tools/jlink/plugins/PrevisitorTest.java Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/test/tools/jlink/plugins/PrevisitorTest.java Thu Jan 19 07:02:33 2017 -0800 @@ -25,8 +25,9 @@ * @test * @summary Test previsitor * @author Andrei Eremeev - * @modules jdk.jlink/jdk.tools.jlink.internal - * jdk.jlink/jdk.tools.jlink + * @modules jdk.jlink/jdk.tools.jlink + * jdk.jlink/jdk.tools.jlink.internal + * jdk.jlink/jdk.tools.jlink.plugin * @run main/othervm PrevisitorTest */ import java.nio.ByteOrder; diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/tools/jlink/plugins/StringSharingPluginTest.java --- a/jdk/test/tools/jlink/plugins/StringSharingPluginTest.java Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/test/tools/jlink/plugins/StringSharingPluginTest.java Thu Jan 19 07:02:33 2017 -0800 @@ -30,6 +30,7 @@ * java.base/jdk.internal.jimage.decompressor * jdk.jlink/jdk.tools.jlink.internal * jdk.jlink/jdk.tools.jlink.internal.plugins + * jdk.jlink/jdk.tools.jlink.plugin * jdk.jlink/jdk.tools.jmod * jdk.jlink/jdk.tools.jimage * jdk.jdeps/com.sun.tools.classfile diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/tools/jlink/plugins/StripDebugPluginTest.java --- a/jdk/test/tools/jlink/plugins/StripDebugPluginTest.java Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/test/tools/jlink/plugins/StripDebugPluginTest.java Thu Jan 19 07:02:33 2017 -0800 @@ -30,6 +30,7 @@ * @modules java.base/jdk.internal.jimage * jdk.jlink/jdk.tools.jlink.internal * jdk.jlink/jdk.tools.jlink.internal.plugins + * jdk.jlink/jdk.tools.jlink.plugin * jdk.jlink/jdk.tools.jimage * jdk.jlink/jdk.tools.jmod * jdk.jdeps/com.sun.tools.classfile diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/tools/jmod/JmodTest.java --- a/jdk/test/tools/jmod/JmodTest.java Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/test/tools/jmod/JmodTest.java Thu Jan 19 07:02:33 2017 -0800 @@ -29,7 +29,7 @@ * @modules jdk.compiler * jdk.jlink * @build jdk.testlibrary.FileUtils CompilerUtils - * @run testng JmodTest + * @run testng/othervm -Djava.io.tmpdir=. JmodTest */ import java.io.*; @@ -40,8 +40,10 @@ import java.util.function.Consumer; import java.util.regex.Pattern; import java.util.spi.ToolProvider; +import java.util.stream.Collectors; import java.util.stream.Stream; import jdk.testlibrary.FileUtils; +import jdk.testlibrary.JDKToolFinder; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; @@ -593,9 +595,7 @@ findTmpFiles(filename).forEach(tmp -> { try { FileUtils.deleteFileIfExistsWithRetry(tmp); - } catch (IOException e) { - throw new UncheckedIOException(e); - } + } catch (IOException e) {} }); String cp = EXPLODED_DIR.resolve("foo").resolve("classes") + File.pathSeparator + @@ -608,17 +608,25 @@ .assertFailure() .resultChecker(r -> { assertContains(r.output, "unnamed package"); - Set tmpfiles = findTmpFiles(filename).collect(toSet()); + List tmpfiles = findTmpFiles(filename); assertTrue(tmpfiles.isEmpty(), "Unexpected tmp file:" + tmpfiles); }); } - private Stream findTmpFiles(String prefix) { - try { - Path tmpdir = Paths.get(System.getProperty("java.io.tmpdir")); - return Files.find(tmpdir, 1, (p, attrs) -> - p.getFileName().toString().startsWith(prefix) - && p.getFileName().toString().endsWith(".tmp")); + /* + * Returns the list of writeable tmp files with the given prefix. + * + * Ignore the non-writeable tmp files because this test is possibly + * running by another user. + */ + private List findTmpFiles(String prefix) { + Path tmpdir = Paths.get(System.getProperty("java.io.tmpdir")); + try (Stream 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) { throw new UncheckedIOException(e); } diff -r c0f498d6a804 -r d935c4d3f039 jdk/test/tools/pack200/Utils.java --- a/jdk/test/tools/pack200/Utils.java Thu Nov 17 09:51:10 2016 -0800 +++ b/jdk/test/tools/pack200/Utils.java Thu Jan 19 07:02:33 2017 -0800 @@ -111,6 +111,7 @@ compiler("-d", XCLASSES.getName(), + "--add-modules=jdk.jdeps", "--add-exports=jdk.jdeps/com.sun.tools.classfile=ALL-UNNAMED", "@" + tmpFile.getAbsolutePath());