changeset 16525:cd7eaa7bf282

Merge
author henryjen
date Thu, 19 Jan 2017 07:02:33 -0800
parents a3f48d8fc56a fbec62a1f43e
children 5e1a848e7652
files
diffstat 50 files changed, 1516 insertions(+), 901 deletions(-) [+]
line wrap: on
line diff
--- a/make/ModuleTools.gmk	Thu Nov 17 09:51:10 2016 -0800
+++ b/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) \
--- a/src/java.base/share/classes/java/lang/Class.java	Thu Nov 17 09:51:10 2016 -0800
+++ b/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<T> 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<T> 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 @@
      *
      * <p> Default policy: allow all clients access with normal Java access
      * control.
+     *
+     * <p> 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() + ".<init>" + argumentTypesToString(parameterTypes));
+        throw new NoSuchMethodException(methodToString("<init>", parameterTypes));
     }
 
     //
@@ -3294,8 +3323,11 @@
     private native Constructor<T>[] 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];
--- a/src/java.base/share/classes/java/lang/invoke/CallSite.java	Thu Nov 17 09:51:10 2016 -0800
+++ b/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);
--- a/src/java.base/share/classes/java/lang/invoke/MethodType.java	Thu Nov 17 09:51:10 2016 -0800
+++ b/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<T> stale;
 
         public ConcurrentWeakInternSet() {
-            this.map = new ConcurrentHashMap<>();
+            this.map = new ConcurrentHashMap<>(512);
             this.stale = new ReferenceQueue<>();
         }
 
--- a/src/java.base/share/classes/java/util/ResourceBundle.java	Thu Nov 17 09:51:10 2016 -0800
+++ b/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 <code>loader</code> 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;
             }
         );
--- a/src/java.base/share/classes/sun/reflect/misc/ReflectUtil.java	Thu Nov 17 09:51:10 2016 -0800
+++ b/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");
         }
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java	Thu Nov 17 09:51:10 2016 -0800
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java	Thu Jan 19 07:02:33 2017 -0800
@@ -165,19 +165,10 @@
                 throw new PluginException("TargetPlatform attribute is missing for java.base module");
             }
 
+            checkResourcePool(files);
+
             Path bin = root.resolve(BIN_DIRNAME);
 
-            // check any duplicated resource files
-            Map<Path, Set<String>> duplicates = new HashMap<>();
-            files.entries()
-                .filter(f -> f.type() != ResourcePoolEntry.Type.CLASS_OR_RESOURCE)
-                .collect(groupingBy(this::entryToImagePath,
-                         mapping(ResourcePoolEntry::moduleName, toSet())))
-                .entrySet()
-                .stream()
-                .filter(e -> e.getValue().size() > 1)
-                .forEach(e -> duplicates.put(e.getKey(), e.getValue()));
-
             // write non-classes resource files to the image
             files.entries()
                 .filter(f -> f.type() != ResourcePoolEntry.Type.CLASS_OR_RESOURCE)
@@ -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<Path, Set<String>> duplicates = new HashMap<>();
+        pool.entries()
+             .filter(f -> f.type() != ResourcePoolEntry.Type.CLASS_OR_RESOURCE)
+             .collect(groupingBy(this::entryToImagePath,
+                      mapping(ResourcePoolEntry::moduleName, toSet())))
+             .entrySet()
+             .stream()
+             .filter(e -> e.getValue().size() > 1)
+             .forEach(e -> duplicates.put(e.getKey(), e.getValue()));
+        if (!duplicates.isEmpty()) {
+            throw new PluginException("Duplicate resources: " + duplicates);
+        }
+    }
+
     /**
      * Generates launcher scripts.
      *
--- a/src/jdk.jlink/share/classes/module-info.java	Thu Nov 17 09:51:10 2016 -0800
+++ b/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;
 
--- a/src/jdk.zipfs/share/classes/jdk/nio/zipfs/JarFileSystem.java	Thu Nov 17 09:51:10 2016 -0800
+++ b/src/jdk.zipfs/share/classes/jdk/nio/zipfs/JarFileSystem.java	Thu Jan 19 07:02:33 2017 -0800
@@ -135,7 +135,7 @@
         TreeMap<Integer,IndexNode> 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);
     }
--- a/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipCoder.java	Thu Nov 17 09:51:10 2016 -0800
+++ b/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 {
 
-    String toString(byte[] ba, int length) {
+    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);
+        }
+
+        @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<CharsetDecoder> 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;
     }
--- a/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java	Thu Nov 17 09:51:10 2016 -0800
+++ b/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<Path> 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<IndexNode> 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
 
+            byte[] zname = isdir ? toDirectoryPath(name) : name;
+
             // confirm size/length
-
-            int nlen = (name != null) ? name.length - 1 : 0;  // name has [0] as "slash"
+            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;
--- a/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipPath.java	Thu Nov 17 09:51:10 2016 -0800
+++ b/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();
     }
-
 }
--- a/test/ProblemList.txt	Thu Nov 17 09:51:10 2016 -0800
+++ b/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
--- a/test/TEST.ROOT	Thu Nov 17 09:51:10 2016 -0800
+++ b/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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/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());
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/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:
+#     <level>: <log message> [<date/time>]
+#
+# java.util.logging.SimpleFormatter.format=%4$s: %5$s [%1$tc]%n
+
+############################################################
+# Facility specific properties.
+# Provides extra control for each logger.
+############################################################
+
+# For example, set the com.xyz.foo logger to only log SEVERE
+# messages:
+com.xyz.foo.level = SEVERE
+com.xyz.foo.handlers = test.handlers.TestHandler
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/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<Logger> LOGGERS = new ArrayList<>();
+    public LogConfig() {
+        LogManager manager = LogManager.getLogManager();
+        Logger logger = Logger.getLogger("com.xyz.foo");
+        if (logger.getHandlers().length > 0) {
+            System.err.println(this.getClass().getName() + ": "
+                    + "Unexpected handlers: "
+                    + List.of(logger.getHandlers()));
+            throw new RuntimeException("Unexpected handlers: "
+                    + List.of(logger.getHandlers()));
+        }
+        logger.addHandler(new TestHandler());
+        LOGGERS.add(logger);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/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 {
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/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 {
+
+}
--- a/test/jdk/nio/zipfs/PathOps.java	Thu Nov 17 09:51:10 2016 -0800
+++ b/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")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/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> T", "public <T extends String> T", false},
+        };
+    }
+
+    @Test(dataProvider = "publicAPI")
+    public void introducingPublicMembers(String publicAPI,
+                                         Method method) throws Throwable {
+        Path root = Paths.get(method.getName());
+        Path classes = root.resolve("classes");
+
+        String API = "#API";
+        String classTemplate =
+                "public class C { \n" +
+                        "    " + API + "\n" +
+                        "    public void method(){ };\n" +
+                        "}\n";
+        String base = classTemplate.replace(API, "");
+        String v10 = classTemplate.replace(API, publicAPI);
+
+        compileTemplate(classes.resolve("base"), base);
+        compileTemplate(classes.resolve("v10"), v10);
+
+        String jarfile = root.resolve("test.jar").toString();
+        jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
+                "--release", "10", "-C", classes.resolve("v10").toString(), ".")
+                .shouldNotHaveExitValue(SUCCESS)
+                .shouldContain("contains a class with different api from earlier version");
+
+        FileUtils.deleteFileTreeWithRetry(root);
+    }
+
+    @DataProvider
+    Object[][] publicAPI() {
+        return new Object[][]{
+// @ignore JDK-8172148  {"protected class Inner { public void m(){ } } "}, // protected inner class
+// @ignore JDK-8172148  {"public class Inner { public void m(){ } }"},  // public inner class
+// @ignore JDK-8172148  {"public enum E { A; }"},  // public enum
+                {"public void m(){ }"}, // public method
+                {"protected void m(){ }"}, // protected method
+        };
+    }
+
+    @Test(dataProvider = "privateAPI")
+    public void introducingPrivateMembers(String privateAPI,
+                                          Method method) throws Throwable {
+        Path root = Paths.get(method.getName());
+        Path classes = root.resolve("classes");
+
+        String API = "#API";
+        String classTemplate =
+                "public class C { \n" +
+                        "    " + API + "\n" +
+                        "    public void method(){ };\n" +
+                        "}\n";
+        String base = classTemplate.replace(API, "");
+        String v10 = classTemplate.replace(API, privateAPI);
+
+        compileTemplate(classes.resolve("base"), base);
+        compileTemplate(classes.resolve("v10"), v10);
+
+        String jarfile = root.resolve("test.jar").toString();
+        jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
+                "--release", "10", "-C", classes.resolve("v10").toString(), ".")
+                .shouldHaveExitValue(SUCCESS);
+        // add release
+        jar("uf", jarfile,
+                "--release", "11", "-C", classes.resolve("v10").toString(), ".")
+                .shouldHaveExitValue(SUCCESS);
+        // replace release
+        jar("uf", jarfile,
+                "--release", "11", "-C", classes.resolve("v10").toString(), ".")
+                .shouldHaveExitValue(SUCCESS);
+
+        FileUtils.deleteFileTreeWithRetry(root);
+    }
+
+    @DataProvider
+    Object[][] privateAPI() {
+        return new Object[][]{
+                {"private class Inner { public void m(){ } } "}, // private inner class
+                {"class Inner { public void m(){ } }"},  // package private inner class
+                {"enum E { A; }"},  // package private enum
+                // Local class and private method
+                {"private void m(){ class Inner { public void m(){} } Inner i = null; }"},
+                {"void m(){ }"}, // package private method
+        };
+    }
+
+    private void compileTemplate(Path classes, String template) throws Throwable {
+        Path classSourceFile = Files.createDirectories(
+                classes.getParent().resolve("src").resolve(classes.getFileName()))
+                .resolve("C.java");
+        Files.write(classSourceFile, template.getBytes());
+        javac(classes, classSourceFile);
+    }
+}
\ No newline at end of file
--- a/test/tools/jar/multiRelease/Basic.java	Thu Nov 17 09:51:10 2016 -0800
+++ b/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<String,String[]> names = Map.of(
+        Map<String, String[]> 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<String,String[]> names = Map.of(
+        Map<String, String[]> 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<String,String[]> names = Map.of(
+        Map<String, String[]> 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<String,String[]> names = Map.of(
+        Map<String, String[]> 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");
 
-        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 previous version entry class
+    // this is okay but produces warning
+    public void identicalClassToPreviousVersion() throws Throwable {
+        String jarfile = "test.jar";
+
+        compile("test01");  //use same data as test01
+
+        Path classes = Paths.get("classes");
+
+        jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
+                "--release", "9", "-C", classes.resolve("v9").toString(), ".")
+                .shouldHaveExitValue(SUCCESS)
+                .shouldBeEmpty();
+        jar("uf", jarfile,
+                "--release", "10", "-C", classes.resolve("v9").toString(), ".")
+                .shouldHaveExitValue(SUCCESS)
+                .shouldContain("contains a class that")
+                .shouldContain("is identical");
+
+        FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile));
+        FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes"));
     }
 
     @Test
     // 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<String> 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)
-                );
+                .shouldNotHaveExitValue(SUCCESS)
+                .shouldContain("an isolated nested class");
 
-        delete(jarfile);
-        deleteDir(Paths.get(usr, "classes"));
+        FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile));
+        FileUtils.deleteFileTreeWithRetry(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"));
+    @Test
+    public void testCustomManifest() throws Throwable {
+        String jarfile = "test.jar";
 
-        classes = Paths.get(usr, "classes", "v9");
-        Files.createDirectories(classes);
-        source = Paths.get(src, "data", test, "v9", "version");
-        javac(classes, source.resolve("Version.java"));
+        compile("test01");
 
-        classes = Paths.get(usr, "classes", "v10");
-        Files.createDirectories(classes);
-        source = Paths.get(src, "data", test, "v10", "version");
-        javac(classes, source.resolve("Version.java"));
-    }
+        Path classes = Paths.get("classes");
+        Path manifest = Paths.get("Manifest.txt");
 
-    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);
-        }
-    }
+        // create
+        Files.write(manifest, "Class-Path: MyUtils.jar\n".getBytes());
 
-    // compares the bytes found in the jar entries with the bytes found in the
-    // corresponding data files used to create the entries
-    private void compare(String jarfile, Map<String,String[]> names) throws IOException {
-        try (JarFile jf = new JarFile(jarfile)) {
-            for (String name : names.keySet()) {
-                Path path = Paths.get("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);
-            }
-        }
-    }
+        jar("cfm", jarfile, manifest.toString(),
+                "-C", classes.resolve("base").toString(), ".",
+                "--release", "10", "-C", classes.resolve("v10").toString(), ".")
+                .shouldHaveExitValue(SUCCESS)
+                .shouldBeEmpty();
 
-    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<Path>() {
-            @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;
-            }
-        });
-    }
-
-    /*
-     * The following methods were taken from modular jar and other jar tests
-     */
-
-    void javac(Path dest, Path... sourceFiles) throws IOException {
-        String javac = JDKToolFinder.getJDKTool("javac");
-
-        List<String> commands = new ArrayList<>();
-        commands.add(javac);
-        String opts = System.getProperty("test.compiler.opts");
-        if (!opts.isEmpty()) {
-            commands.addAll(Arrays.asList(opts.split(" +")));
-        }
-        commands.add("-d");
-        commands.add(dest.toString());
-        Stream.of(sourceFiles).map(Object::toString).forEach(x -> commands.add(x));
-
-        quickFail(run(new ProcessBuilder(commands)));
-    }
-
-    Result jarWithStdin(File stdinSource, String... args) {
-        String jar = JDKToolFinder.getJDKTool("jar");
-        List<String> commands = new ArrayList<>();
-        commands.add(jar);
-        commands.addAll(Utils.getForwardVmOptions());
-        Stream.of(args).forEach(x -> commands.add(x));
-        ProcessBuilder p = new ProcessBuilder(commands);
-        if (stdinSource != null)
-            p.redirectInput(stdinSource);
-        return run(p);
-    }
-
-    Result jar(String... args) {
-        return jarWithStdin(null, args);
-    }
-
-    void quickFail(Result r) {
-        if (r.ec != 0)
-            throw new RuntimeException(r.output);
-    }
-
-    Result run(ProcessBuilder pb) {
-        Process p;
-        out.printf("Running: %s%n", pb.command());
-        try {
-            p = pb.start();
-        } catch (IOException e) {
-            throw new RuntimeException(
-                    format("Couldn't start process '%s'", pb.command()), e);
+        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<Result> r) { r.accept(this); return this; }
+        FileUtils.deleteFileIfExistsWithRetry(Paths.get(jarfile));
+        FileUtils.deleteFileTreeWithRetry(Paths.get(usr, "classes"));
     }
 }
--- a/test/tools/jar/multiRelease/Basic1.java	Thu Nov 17 09:51:10 2016 -0800
+++ b/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");
 
-        jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".",
-            "--release", "9", "-C", v9.toString(), ".",
-            "--release", "10", "-C", v10.toString(), ".")
-            .assertSuccess();
+        Path base = classes.resolve("base");
+        Path v9 = classes.resolve("v9");
+        Path v10 = classes.resolve("v10");
+
+        jar("cf", jarfile, "-C", base.toString(), ".",
+                "--release", "9", "-C", v9.toString(), ".",
+                "--release", "10", "-C", v10.toString(), ".")
+                .shouldHaveExitValue(SUCCESS);
 
         checkMultiRelease(jarfile, true);
 
-        Map<String,String[]> names = Map.of(
-            "version/Main.class",
-            new String[] {"classes", "base", "version", "Main.class"},
+        Map<String, String[]> 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");
+        Path base = classes.resolve("base");
+        Path v10 = classes.resolve("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<String,String[]> names) throws IOException {
-        try (JarFile jf = new JarFile(jarfile)) {
-            for (String name : names.keySet()) {
-                Path path = Paths.get("", names.get(name));
-                byte[] b1 = Files.readAllBytes(path);
-                byte[] b2;
-                JarEntry je = jf.getJarEntry(name);
-                try (InputStream is = jf.getInputStream(je)) {
-                    b2 = is.readAllBytes();
-                }
-                assertEquals(b1,b2);
-            }
-        }
-    }
-
-    /*
-     * The following methods were taken from modular jar and other jar tests
-     */
-
-    void javac(Path dest, Path... sourceFiles) throws IOException {
-        String javac = JDKToolFinder.getJDKTool("javac");
-
-        List<String> commands = new ArrayList<>();
-        commands.add(javac);
-        String opts = System.getProperty("test.compiler.opts");
-        if (!opts.isEmpty()) {
-            commands.addAll(Arrays.asList(opts.split(" +")));
-        }
-        commands.add("-d");
-        commands.add(dest.toString());
-        Stream.of(sourceFiles).map(Object::toString).forEach(x -> commands.add(x));
-
-        quickFail(run(new ProcessBuilder(commands)));
-    }
-
-    Result jarWithStdin(File stdinSource, String... args) {
-        String jar = JDKToolFinder.getJDKTool("jar");
-        List<String> commands = new ArrayList<>();
-        commands.add(jar);
-        commands.addAll(Utils.getForwardVmOptions());
-        Stream.of(args).forEach(x -> commands.add(x));
-        ProcessBuilder p = new ProcessBuilder(commands);
-        if (stdinSource != null)
-            p.redirectInput(stdinSource);
-        return run(p);
-    }
-
-    Result jar(String... args) {
-        return jarWithStdin(null, args);
-    }
-
-    void quickFail(Result r) {
-        if (r.ec != 0)
-            throw new RuntimeException(r.output);
-    }
-
-    Result run(ProcessBuilder pb) {
-        Process p;
-        out.printf("Running: %s%n", pb.command());
-        try {
-            p = pb.start();
-        } catch (IOException e) {
-            throw new RuntimeException(
-                    format("Couldn't start process '%s'", pb.command()), e);
-        }
-
-        String output;
-        try {
-            output = toString(p.getInputStream(), p.getErrorStream());
-        } catch (IOException e) {
-            throw new RuntimeException(
-                    format("Couldn't read process output '%s'", pb.command()), e);
-        }
-
-        try {
-            p.waitFor();
-        } catch (InterruptedException e) {
-            throw new RuntimeException(
-                    format("Process hasn't finished '%s'", pb.command()), e);
-        }
-        return new Result(p.exitValue(), output);
-    }
-
-    String toString(InputStream in1, InputStream in2) throws IOException {
-        try (ByteArrayOutputStream dst = new ByteArrayOutputStream();
-             InputStream concatenated = new SequenceInputStream(in1, in2)) {
-            concatenated.transferTo(dst);
-            return new String(dst.toByteArray(), "UTF-8");
-        }
-    }
-
-    static class Result {
-        final int ec;
-        final String output;
-
-        private Result(int ec, String output) {
-            this.ec = ec;
-            this.output = output;
-        }
-
-        boolean outputContains(String msg) {
-            return Arrays.stream(output.split("\\R"))
-                         .collect(Collectors.joining(" "))
-                         .contains(msg);
-        }
-
-        Result assertSuccess() {
-            assertTrue(ec == 0, format("ec: %d, output: %s", ec, output));
-            return this;
-        }
-        Result assertFailure() {
-            assertTrue(ec != 0, format("ec: %d, output: %s", ec, output));
-            return this;
-        }
-        Result resultChecker(Consumer<Result> r) { r.accept(this); return this; }
+        jar("cf", jarfile, "-C", base.toString(), ".",
+                "--release", "10", "-C", v10.toString(), ".")
+                .shouldNotHaveExitValue(SUCCESS)
+                .shouldContain("unexpected versioned entry META-INF/versions/");
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/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<String, String[]> names) throws IOException {
+        try (JarFile jf = new JarFile(jarfile)) {
+            for (String name : names.keySet()) {
+                Path path = Paths.get("classes", names.get(name));
+                byte[] b1 = Files.readAllBytes(path);
+                byte[] b2;
+                JarEntry je = jf.getJarEntry(name);
+                try (InputStream is = jf.getInputStream(je)) {
+                    b2 = is.readAllBytes();
+                }
+                assertEquals(b1, b2);
+            }
+        }
+    }
+
+    void javac(Path dest, Path... sourceFiles) throws Throwable {
+        String javac = JDKToolFinder.getJDKTool("javac");
+
+        List<String> commands = new ArrayList<>();
+        commands.add(javac);
+        String opts = System.getProperty("test.compiler.opts");
+        if (!opts.isEmpty()) {
+            commands.addAll(Arrays.asList(opts.split(" +")));
+        }
+        commands.addAll(Utils.getForwardVmOptions());
+        commands.add("-d");
+        commands.add(dest.toString());
+        Stream.of(sourceFiles)
+                .map(Object::toString)
+                .forEach(x -> commands.add(x));
+
+        ProcessTools.executeCommand(new ProcessBuilder(commands))
+                .shouldHaveExitValue(SUCCESS);
+    }
+
+    OutputAnalyzer jarWithStdin(File stdinSource,
+                                String... args) throws Throwable {
+
+        String jar = JDKToolFinder.getJDKTool("jar");
+        List<String> commands = new ArrayList<>();
+        commands.add(jar);
+        commands.addAll(Utils.getForwardVmOptions());
+        Stream.of(args).forEach(x -> commands.add(x));
+        ProcessBuilder p = new ProcessBuilder(commands);
+        if (stdinSource != null)
+            p.redirectInput(stdinSource);
+        return ProcessTools.executeCommand(p);
+    }
+
+    OutputAnalyzer jar(String... args) throws Throwable {
+        return jarWithStdin(null, args);
+    }
+}
\ No newline at end of file
--- a/test/tools/jar/multiRelease/data/test04/v9/version/Version.java	Thu Nov 17 09:51:10 2016 -0800
+++ b/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() {
     }
 }
--- a/test/tools/jlink/DefaultProviderTest.java	Thu Nov 17 09:51:10 2016 -0800
+++ b/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
--- a/test/tools/jlink/ImageFileCreatorTest.java	Thu Nov 17 09:51:10 2016 -0800
+++ b/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
  */
--- a/test/tools/jlink/ImageFilePoolTest.java	Thu Nov 17 09:51:10 2016 -0800
+++ b/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
  */
--- a/test/tools/jlink/IntegrationTest.java	Thu Nov 17 09:51:10 2016 -0800
+++ b/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
--- a/test/tools/jlink/JLink2Test.java	Thu Nov 17 09:51:10 2016 -0800
+++ b/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
--- a/test/tools/jlink/JLinkOptionsTest.java	Thu Nov 17 09:51:10 2016 -0800
+++ b/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
--- a/test/tools/jlink/JLinkPostProcessingTest.java	Thu Nov 17 09:51:10 2016 -0800
+++ b/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
--- a/test/tools/jlink/JLinkTest.java	Thu Nov 17 09:51:10 2016 -0800
+++ b/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.*
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/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());
+    }
+}
--- a/test/tools/jlink/ResourcePoolTest.java	Thu Nov 17 09:51:10 2016 -0800
+++ b/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
  */
--- a/test/tools/jlink/plugins/CompressorPluginTest.java	Thu Nov 17 09:51:10 2016 -0800
+++ b/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;
--- a/test/tools/jlink/plugins/ExcludeFilesPluginTest.java	Thu Nov 17 09:51:10 2016 -0800
+++ b/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
  */
 
--- a/test/tools/jlink/plugins/ExcludePluginTest.java	Thu Nov 17 09:51:10 2016 -0800
+++ b/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
  */
 
--- a/test/tools/jlink/plugins/ExcludeVMPluginTest.java	Thu Nov 17 09:51:10 2016 -0800
+++ b/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;
--- a/test/tools/jlink/plugins/IncludeLocalesPluginTest.java	Thu Nov 17 09:51:10 2016 -0800
+++ b/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
--- a/test/tools/jlink/plugins/LastSorterTest.java	Thu Nov 17 09:51:10 2016 -0800
+++ b/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
  */
 
--- a/test/tools/jlink/plugins/PluginsNegativeTest.java	Thu Nov 17 09:51:10 2016 -0800
+++ b/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;
--- a/test/tools/jlink/plugins/PrevisitorTest.java	Thu Nov 17 09:51:10 2016 -0800
+++ b/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;
--- a/test/tools/jlink/plugins/StringSharingPluginTest.java	Thu Nov 17 09:51:10 2016 -0800
+++ b/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
--- a/test/tools/jlink/plugins/StripDebugPluginTest.java	Thu Nov 17 09:51:10 2016 -0800
+++ b/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
--- a/test/tools/jmod/JmodTest.java	Thu Nov 17 09:51:10 2016 -0800
+++ b/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<Path> tmpfiles = findTmpFiles(filename).collect(toSet());
+                 List<Path> tmpfiles = findTmpFiles(filename);
                  assertTrue(tmpfiles.isEmpty(), "Unexpected tmp file:" + tmpfiles);
              });
     }
 
-    private Stream<Path> 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<Path> findTmpFiles(String prefix) {
+        Path tmpdir = Paths.get(System.getProperty("java.io.tmpdir"));
+        try (Stream<Path> stream = Files.list(tmpdir)) {
+            return stream.filter(p -> {
+                        String fn = p.getFileName().toString();
+                        return Files.isWritable(p)
+                                && fn.startsWith(prefix) && fn.endsWith(".tmp");
+                    }).collect(Collectors.toList());
         } catch (IOException e) {
             throw new UncheckedIOException(e);
         }
--- a/test/tools/pack200/Utils.java	Thu Nov 17 09:51:10 2016 -0800
+++ b/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());