changeset 16026:f970d80b7a55

Merge
author ddehaven
date Mon, 31 Oct 2016 10:39:14 -0700
parents 5445b9413d9d 608dcced40aa
children d13621cb40df
files src/java.base/share/classes/jdk/internal/module/ConfigurableModuleFinder.java test/ProblemList.txt test/java/lang/module/ModuleReader/MultiReleaseJarTest.java test/java/util/ResourceBundle/modules/basic/src/asiabundles/jdk/test/resources/MyResources_ja_JP.properties
diffstat 74 files changed, 3425 insertions(+), 1301 deletions(-) [+]
line wrap: on
line diff
--- a/.hgtags	Mon Oct 31 14:50:09 2016 +0300
+++ b/.hgtags	Mon Oct 31 10:39:14 2016 -0700
@@ -384,3 +384,4 @@
 5518ac2f2ead5e594bd983f2047178136aafdfd0 jdk-9+139
 e93b7ea559759f036c9f69fd2ddaf47bb4e98385 jdk-9+140
 8d752af5f61d41f226adf2cda72a20faa9ad620a jdk-9+141
+6ce43dd8e954b452f330dd7a412df5107f7e1923 jdk-9+142
--- a/make/data/tzdata/VERSION	Mon Oct 31 14:50:09 2016 +0300
+++ b/make/data/tzdata/VERSION	Mon Oct 31 10:39:14 2016 -0700
@@ -21,4 +21,4 @@
 # or visit www.oracle.com if you need additional information or have any
 # questions.
 #
-tzdata2016g
+tzdata2016h
--- a/make/data/tzdata/asia	Mon Oct 31 14:50:09 2016 +0300
+++ b/make/data/tzdata/asia	Mon Oct 31 10:39:14 2016 -0700
@@ -2567,11 +2567,6 @@
 # From Paul Eggert (2015-03-03):
 # http://www.timeanddate.com/time/change/west-bank/ramallah?year=2014
 # says that the fall 2014 transition was Oct 23 at 24:00.
-# For future dates, guess the last Friday in March at 24:00 through
-# the first Friday on or after October 21 at 00:00.  This is consistent with
-# the predictions in today's editions of the following URLs:
-# http://www.timeanddate.com/time/change/gaza-strip/gaza
-# http://www.timeanddate.com/time/change/west-bank/hebron
 
 # From Hannah Kreitem (2016-03-09):
 # http://www.palestinecabinet.gov.ps/WebSite/ar/ViewDetails?ID=31728
@@ -2581,7 +2576,21 @@
 #
 # From Paul Eggert (2016-03-12):
 # Predict spring transitions on March's last Saturday at 01:00 from now on.
-# Leave fall predictions alone for now.
+
+# From Sharef Mustafa (2016-10-19):
+# [T]he Palestinian cabinet decision (Mar 8th 2016) published on
+# http://www.palestinecabinet.gov.ps/WebSite/Upload/Decree/GOV_17/16032016134830.pdf
+# states that summer time will end on Oct 29th at 01:00.
+#
+# From Tim Parenti (2016-10-19):
+# Predict fall transitions on October's last Saturday at 01:00 from now on.
+# This is consistent with the 2016 transition as well as our spring
+# predictions.
+#
+# From Paul Eggert (2016-10-19):
+# It's also consistent with predictions in the following URLs today:
+# http://www.timeanddate.com/time/change/gaza-strip/gaza
+# http://www.timeanddate.com/time/change/west-bank/hebron
 
 # Rule	NAME	FROM	TO	TYPE	IN	ON	AT	SAVE	LETTER/S
 Rule EgyptAsia	1957	only	-	May	10	0:00	1:00	S
@@ -2610,9 +2619,10 @@
 Rule Palestine	2012	2014	-	Mar	lastThu	24:00	1:00	S
 Rule Palestine	2012	only	-	Sep	21	1:00	0	-
 Rule Palestine	2013	only	-	Sep	Fri>=21	0:00	0	-
-Rule Palestine	2014	max	-	Oct	Fri>=21	0:00	0	-
+Rule Palestine	2014	2015	-	Oct	Fri>=21	0:00	0	-
 Rule Palestine	2015	only	-	Mar	lastFri	24:00	1:00	S
 Rule Palestine	2016	max	-	Mar	lastSat	1:00	1:00	S
+Rule Palestine	2016	max	-	Oct	lastSat	1:00	0	-
 
 # Zone	NAME		GMTOFF	RULES	FORMAT	[UNTIL]
 Zone	Asia/Gaza	2:17:52	-	LMT	1900 Oct
@@ -2762,45 +2772,31 @@
 # People who live in regions under Tamil control can use [TZ='Asia/Kolkata'],
 # as that zone has agreed with the Tamil areas since our cutoff date of 1970.
 
-# From K Sethu (2006-04-25):
-# I think the abbreviation LKT originated from the world of computers at
-# the time of or subsequent to the time zone changes by SL Government
-# twice in 1996 and probably SL Government or its standardization
-# agencies never declared an abbreviation as a national standard.
+# From Sadika Sumanapala (2016-10-19):
+# According to http://www.sltime.org (maintained by Measurement Units,
+# Standards & Services Department, Sri Lanka) abbreviation for Sri Lanka
+# standard time is SLST.
 #
-# I recollect before the recent change the government announcements
-# mentioning it as simply changing Sri Lanka Standard Time or Sri Lanka
-# Time and no mention was made about the abbreviation.
-#
-# If we look at Sri Lanka Department of Government's "Official News
-# Website of Sri Lanka" ... http://www.news.lk/ we can see that they
-# use SLT as abbreviation in time stamp at the beginning of each news
-# item....
-#
-# Within Sri Lanka I think LKT is well known among computer users and
-# administrators.  In my opinion SLT may not be a good choice because the
-# nation's largest telcom / internet operator Sri Lanka Telcom is well
-# known by that abbreviation - simply as SLT (there IP domains are
-# slt.lk and sltnet.lk).
-#
-# But if indeed our government has adopted SLT as standard abbreviation
-# (that we have not known so far) then  it is better that it be used for
-# all computers.
-
-# From Paul Eggert (2006-04-25):
-# One possibility is that we wait for a bit for the dust to settle down
-# and then see what people actually say in practice.
+# From Paul Eggert (2016-10-18):
+# "SLST" seems to be reasonably recent and rarely-used outside time
+# zone nerd sources.  I searched Google News and found three uses of
+# it in the International Business Times of India in February and
+# March of this year when discussing cricket match times, but nothing
+# since then (though there has been a lot of cricket) and nothing in
+# other English-language news sources.  Our old abbreviation "LKT" is
+# even worse.  For now, let's use a numeric abbreviation; we can
+# switch to "SLST" if it catches on.
 
 # Zone	NAME		GMTOFF	RULES	FORMAT	[UNTIL]
 Zone	Asia/Colombo	5:19:24 -	LMT	1880
 			5:19:32	-	MMT	1906        # Moratuwa Mean Time
-			5:30	-	IST	1942 Jan  5
-			5:30	0:30	IHST	1942 Sep
-			5:30	1:00	IST	1945 Oct 16  2:00
-			5:30	-	IST	1996 May 25  0:00
-			6:30	-	LKT	1996 Oct 26  0:30
-			6:00	-	LKT	2006 Apr 15  0:30
-			5:30	-	IST
+			5:30	-	+0530	1942 Jan  5
+			5:30	0:30	+0530/+06 1942 Sep
+			5:30	1:00	+0530/+0630 1945 Oct 16  2:00
+			5:30	-	+0530	1996 May 25  0:00
+			6:30	-	+0630	1996 Oct 26  0:30
+			6:00	-	+06	2006 Apr 15  0:30
+			5:30	-	+0530
 
 # Syria
 # Rule	NAME	FROM	TO	TYPE	IN	ON	AT	SAVE	LETTER/S
--- a/make/data/tzdata/australasia	Mon Oct 31 14:50:09 2016 +0300
+++ b/make/data/tzdata/australasia	Mon Oct 31 10:39:14 2016 -0700
@@ -373,7 +373,13 @@
 # commencing at 2.00 am on Sunday 1st November, 2015 and ending at
 # 3.00 am on Sunday 17th January, 2016.
 
-# From Paul Eggert (2015-09-01):
+# From Raymond Kumar (2016-10-04):
+# http://www.fiji.gov.fj/Media-Center/Press-Releases/DAYLIGHT-SAVING-STARTS-ON-6th-NOVEMBER,-2016.aspx
+# "Fiji's daylight savings will begin on Sunday, 6 November 2016, when
+# clocks go forward an hour at 2am to 3am....  Daylight Saving will
+# end at 3.00am on Sunday 15th January 2017."
+
+# From Paul Eggert (2016-10-03):
 # For now, guess DST from 02:00 the first Sunday in November to
 # 03:00 the third Sunday in January.  Although ad hoc, it matches
 # transitions since late 2014 and seems more likely to match future
--- a/make/data/tzdata/europe	Mon Oct 31 14:50:09 2016 +0300
+++ b/make/data/tzdata/europe	Mon Oct 31 10:39:14 2016 -0700
@@ -1931,7 +1931,7 @@
 # Amsterdam mean time.
 
 # The data entries before 1945 are taken from
-# http://www.phys.uu.nl/~vgent/wettijd/wettijd.htm
+# http://www.staff.science.uu.nl/~gent0113/idl/idl.htm
 
 # Rule	NAME	FROM	TO	TYPE	IN	ON	AT	SAVE	LETTER/S
 Rule	Neth	1916	only	-	May	 1	0:00	1:00	NST	# Netherlands Summer Time
@@ -3450,22 +3450,24 @@
 
 # Turkey
 
-# From Amar Devegowda (2007-01-03):
-# The time zone rules for Istanbul, Turkey have not been changed for years now.
-# ... The latest rules are available at:
-# http://www.timeanddate.com/worldclock/timezone.html?n=107
-# From Steffen Thorsen (2007-01-03):
-# I have been able to find press records back to 1996 which all say that
-# DST started 01:00 local time and end at 02:00 local time.  I am not sure
-# what happened before that.  One example for each year from 1996 to 2001:
-# http://newspot.byegm.gov.tr/arsiv/1996/21/N4.htm
-# http://www.byegm.gov.tr/YAYINLARIMIZ/CHR/ING97/03/97X03X25.TXT
-# http://www.byegm.gov.tr/YAYINLARIMIZ/CHR/ING98/03/98X03X02.HTM
-# http://www.byegm.gov.tr/YAYINLARIMIZ/CHR/ING99/10/99X10X26.HTM#%2016
-# http://www.byegm.gov.tr/YAYINLARIMIZ/CHR/ING2000/03/00X03X06.HTM#%2021
-# http://www.byegm.gov.tr/YAYINLARIMIZ/CHR/ING2001/03/23x03x01.HTM#%2027
-# From Paul Eggert (2007-01-03):
-# Prefer the above source to Shanks & Pottenger for time stamps after 1990.
+# From Kıvanç Yazan (2016-09-25):
+# 1) For 1986-2006, DST started at 01:00 local and ended at 02:00 local, with
+#    no exceptions.
+# 2) 1994's lastSun was overridden with Mar 20 ...
+# Here are official papers:
+# http://www.resmigazete.gov.tr/arsiv/19032.pdf  - page 2 for 1986
+# http://www.resmigazete.gov.tr/arsiv/19400.pdf  - page 4 for 1987
+# http://www.resmigazete.gov.tr/arsiv/19752.pdf  - page 15 for 1988
+# http://www.resmigazete.gov.tr/arsiv/20102.pdf  - page 6 for 1989
+# http://www.resmigazete.gov.tr/arsiv/20464.pdf  - page 1 for 1990 - 1992
+# http://www.resmigazete.gov.tr/arsiv/21531.pdf  - page 15 for 1993 - 1995
+# http://www.resmigazete.gov.tr/arsiv/21879.pdf  - page 1 for overriding 1994
+# http://www.resmigazete.gov.tr/arsiv/22588.pdf  - page 1 for 1996, 1997
+# http://www.resmigazete.gov.tr/arsiv/23286.pdf  - page 10 for 1998 - 2000
+# http://www.resmigazete.gov.tr/eskiler/2001/03/20010324.htm#2  - for 2001
+# http://www.resmigazete.gov.tr/eskiler/2002/03/20020316.htm#2  - for 2002-2006
+# From Paul Eggert (2016-09-25):
+# Prefer the above sources to Shanks & Pottenger for time stamps after 1985.
 
 # From Steffen Thorsen (2007-03-09):
 # Starting 2007 though, it seems that they are adopting EU's 1:00 UTC
@@ -3574,10 +3576,10 @@
 Rule	Turkey	1983	only	-	Oct	 2	0:00	0	-
 Rule	Turkey	1985	only	-	Apr	20	0:00	1:00	S
 Rule	Turkey	1985	only	-	Sep	28	0:00	0	-
-Rule	Turkey	1986	1990	-	Mar	lastSun	2:00s	1:00	S
-Rule	Turkey	1986	1990	-	Sep	lastSun	2:00s	0	-
-Rule	Turkey	1991	2006	-	Mar	lastSun	1:00s	1:00	S
-Rule	Turkey	1991	1995	-	Sep	lastSun	1:00s	0	-
+Rule	Turkey	1986	1993	-	Mar	lastSun	1:00s	1:00	S
+Rule	Turkey	1986	1995	-	Sep	lastSun	1:00s	0	-
+Rule	Turkey	1994	only	-	Mar	20	1:00s	1:00	S
+Rule	Turkey	1995	2006	-	Mar	lastSun	1:00s	1:00	S
 Rule	Turkey	1996	2006	-	Oct	lastSun	1:00s	0	-
 # Zone	NAME		GMTOFF	RULES	FORMAT	[UNTIL]
 Zone	Europe/Istanbul	1:55:52 -	LMT	1880
--- a/make/data/tzdata/northamerica	Mon Oct 31 14:50:09 2016 +0300
+++ b/make/data/tzdata/northamerica	Mon Oct 31 10:39:14 2016 -0700
@@ -47,8 +47,32 @@
 # was the result of his proposals at the Convention of Railroad Trunk Lines
 # in New York City (1869-10).  His 1870 proposal was based on Washington, DC,
 # but in 1872-05 he moved the proposed origin to Greenwich.
-# His proposal was adopted by the railroads on 1883-11-18 at 12:00,
-# and the most of the country soon followed suit.
+
+# From Paul Eggert (2016-09-21):
+# Dowd's proposal left many details unresolved, such as where to draw
+# lines between time zones.  The key individual who made time zones
+# work in the US was William Frederick Allen - railway engineer,
+# managing editor of the Travelers' Guide, and secretary of the
+# General Time Convention, a railway standardization group.  Allen
+# spent months in dialogs with scientific and railway leaders,
+# developed a workable plan to institute time zones, and presented it
+# to the General Time Convention on 1883-04-11, saying that his plan
+# meant "local time would be practically abolished" - a plus for
+# railway scheduling.  By the next convention on 1883-10-11 nearly all
+# railroads had agreed and it took effect on 1883-11-18 at 12:00.
+# That Sunday was called the "day of two noons", as the eastern parts
+# of the new zones observed noon twice.  Allen witnessed the
+# transition in New York City, writing:
+#
+#   I heard the bells of St. Paul's strike on the old time.  Four
+#   minutes later, obedient to the electrical signal from the Naval
+#   Observatory ... the time-ball made its rapid descent, the chimes
+#   of old Trinity rang twelve measured strokes, and local time was
+#   abandoned, probably forever.
+#
+# Most of the US soon followed suit.  See:
+# Bartky IR. The adoption of standard time. Technol Cult 1989 Jan;30(1):25-56.
+# http://dx.doi.org/10.2307/3105430
 
 # From Paul Eggert (2005-04-16):
 # That 1883 transition occurred at 12:00 new time, not at 12:00 old time.
--- a/src/java.base/share/classes/java/io/FilePermission.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/src/java.base/share/classes/java/io/FilePermission.java	Mon Oct 31 10:39:14 2016 -0700
@@ -173,6 +173,7 @@
     private transient Path npath;       // normalized dir path.
     private transient Path npath2;      // alternative normalized dir path.
     private transient boolean allFiles; // whether this is <<ALL FILES>>
+    private transient boolean invalid;  // whether input path is invalid
 
     // static Strings used by init(int mask)
     private static final char RECURSIVE_CHAR = '-';
@@ -218,11 +219,12 @@
      * A private constructor like a clone, only npath2 is not touched.
      * @param input
      */
-    private FilePermission(FilePermission input) {
-        super(input.getName());
+    private FilePermission(String name, FilePermission input) {
+        super(name);
         this.npath = input.npath;
         this.actions = input.actions;
         this.allFiles = input.allFiles;
+        this.invalid = input.invalid;
         this.recursive = input.recursive;
         this.directory = input.directory;
         this.cpath = input.cpath;
@@ -255,7 +257,12 @@
                     if (input.npath2 == null && !input.allFiles) {
                         Path npath2 = altPath(input.npath);
                         if (npath2 != null) {
-                            FilePermission np = new FilePermission(input);
+                            // Please note the name of the new permission is
+                            // different than the original so that when one is
+                            // added to a FilePermissionCollection it will not
+                            // be merged with the original one.
+                            FilePermission np = new FilePermission(
+                                    input.getName()+"#plus", input);
                             np.npath2 = npath2;
                             return np;
                         }
@@ -266,7 +273,9 @@
                     if (!input.allFiles) {
                         Path npath2 = altPath(input.npath);
                         if (npath2 != null) {
-                            FilePermission np = new FilePermission(input);
+                            // New name, see above.
+                            FilePermission np = new FilePermission(
+                                    input.getName()+"#using", input);
                             np.npath = npath2;
                             return np;
                         }
@@ -318,11 +327,12 @@
                 // Windows. Some JDK codes generate such illegal names.
                 npath = builtInFS.getPath(new File(name).getPath())
                         .normalize();
+                invalid = false;
             } catch (InvalidPathException ipe) {
                 // Still invalid. For compatibility reason, accept it
                 // but make this permission useless.
                 npath = builtInFS.getPath("-u-s-e-l-e-s-s-");
-                this.mask = NONE;
+                invalid = true;
             }
 
             // lastName should always be non-null now
@@ -540,6 +550,12 @@
      */
     boolean impliesIgnoreMask(FilePermission that) {
         if (FilePermCompat.nb) {
+            if (this == that) {
+                return true;
+            }
+            if (this.invalid || that.invalid) {
+                return false;
+            }
             if (allFiles) {
                 return true;
             }
@@ -687,9 +703,13 @@
         FilePermission that = (FilePermission) obj;
 
         if (FilePermCompat.nb) {
+            if (this.invalid || that.invalid) {
+                return false;
+            }
             return (this.mask == that.mask) &&
                     (this.allFiles == that.allFiles) &&
                     this.npath.equals(that.npath) &&
+                    Objects.equals(npath2, that.npath2) &&
                     (this.directory == that.directory) &&
                     (this.recursive == that.recursive);
         } else {
@@ -708,7 +728,8 @@
     @Override
     public int hashCode() {
         if (FilePermCompat.nb) {
-            return Objects.hash(mask, allFiles, directory, recursive, npath);
+            return Objects.hash(
+                    mask, allFiles, directory, recursive, npath, npath2, invalid);
         } else {
             return 0;
         }
--- a/src/java.base/share/classes/java/lang/ClassLoader.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/src/java.base/share/classes/java/lang/ClassLoader.java	Mon Oct 31 10:39:14 2016 -0700
@@ -1681,6 +1681,15 @@
      * this method during startup should take care not to cache the return
      * value until the system is fully initialized.
      *
+     * <p> The class path used by the built-in system class loader is determined
+     * by the system property "{@code java.class.path}" during early
+     * initialization of the VM. If the system property is not defined,
+     * or its value is an empty string, then there is no class path
+     * when the initial module is a module on the application module path,
+     * i.e. <em>a named module</em>. If the initial module is not on
+     * the application module path then the class path defaults to
+     * the current working directory.
+     *
      * @return  The system <tt>ClassLoader</tt> for delegation
      *
      * @throws  SecurityException
--- a/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java	Mon Oct 31 10:39:14 2016 -0700
@@ -31,6 +31,7 @@
 import java.io.UncheckedIOException;
 import java.net.URI;
 import java.nio.ByteBuffer;
+import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -1997,6 +1998,13 @@
                 public Optional<ModuleHashes> hashes(ModuleDescriptor descriptor) {
                     return descriptor.hashes();
                 }
+
+                @Override
+                public ModuleFinder newModulePath(Runtime.Version version,
+                                                  boolean isLinkPhase,
+                                                  Path... entries) {
+                    return new ModulePath(version, isLinkPhase, entries);
+                }
             });
     }
 
--- a/src/java.base/share/classes/java/lang/module/ModuleFinder.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/src/java.base/share/classes/java/lang/module/ModuleFinder.java	Mon Oct 31 10:39:14 2016 -0700
@@ -228,7 +228,7 @@
      *
      *         <li><p> If the name matches the regular expression {@code
      *         "-(\\d+(\\.|$))"} then the module name will be derived from the
-     *         subsequence proceeding the hyphen of the first occurrence. The
+     *         subsequence preceding the hyphen of the first occurrence. The
      *         subsequence after the hyphen is parsed as a {@link
      *         ModuleDescriptor.Version} and ignored if it cannot be parsed as
      *         a {@code Version}. </p></li>
@@ -248,18 +248,23 @@
      *     <li><p> It {@link ModuleDescriptor#requires() requires} {@code
      *     java.base}. </p></li>
      *
-     *     <li><p> All entries in the JAR file with names ending with {@code
-     *     .class} are assumed to be class files where the name corresponds
-     *     to the fully qualified name of the class. The packages of all
-     *     classes are {@link ModuleDescriptor#exports() exported}. </p></li>
+     *     <li><p> The set of packages in the module is derived from the names
+     *     of non-directory entries in the JAR file. A candidate package name
+     *     is derived from an entry using the characters up to, but not
+     *     including, the last forward slash. All remaining forward slashes are
+     *     replaced with dot ({@code "."}). If the resulting string is a valid
+     *     Java identifier then it is assumed to be a package name. For example,
+     *     if the JAR file contains an entry "{@code p/q/Foo.class}" then the
+     *     package name derived is "{@code p.q}". All packages are {@link
+     *     ModuleDescriptor#exports() exported}. </p></li>
      *
-     *     <li><p> The contents of all entries starting with {@code
+     *     <li><p> The contents of entries starting with {@code
      *     META-INF/services/} are assumed to be service configuration files
-     *     (see {@link java.util.ServiceLoader}). The name of the file
-     *     (that follows {@code META-INF/services/}) is assumed to be the
-     *     fully-qualified binary name of a service type. The entries in the
-     *     file are assumed to be the fully-qualified binary names of
-     *     provider classes. </p></li>
+     *     (see {@link java.util.ServiceLoader}). If the name of a file
+     *     (that follows {@code META-INF/services/}) is a legal Java identifier
+     *     then it is assumed to be the fully-qualified binary name of a
+     *     service type. The entries in the file are assumed to be the
+     *     fully-qualified binary names of provider classes. </p></li>
      *
      *     <li><p> If the JAR file has a {@code Main-Class} attribute in its
      *     main manifest then its value is the {@link
@@ -271,8 +276,8 @@
      * {@link ModuleDescriptor.Builder ModuleDescriptor.Builder} API) for an
      * automatic module then {@code FindException} is thrown. This can arise,
      * for example, when a legal Java identifier name cannot be derived from
-     * the file name of the JAR file or where a package name derived from an
-     * entry ending with {@code .class} is not a legal Java identifier. </p>
+     * the file name of the JAR file or where the JAR file contains a {@code
+     * .class} in the top-level directory of the JAR file. </p>
      *
      * <p> In addition to JAR files, an implementation may also support modules
      * that are packaged in other implementation specific module formats. When
@@ -283,8 +288,10 @@
      *
      * <p> As with automatic modules, the contents of a packaged or exploded
      * module may need to be <em>scanned</em> in order to determine the packages
-     * in the module. If a {@code .class} file that corresponds to a class in an
-     * unnamed package is encountered then {@code FindException} is thrown. </p>
+     * in the module. If a {@code .class} file (other than {@code
+     * module-info.class}) is found in the top-level directory then it is
+     * assumed to be a class in the unnamed package and so {@code FindException}
+     * is thrown. </p>
      *
      * <p> Finders created by this method are lazy and do not eagerly check
      * that the given file paths are directories or packaged modules.
@@ -341,7 +348,7 @@
      * @return A {@code ModuleFinder} that composes a sequence of module finders
      */
     static ModuleFinder compose(ModuleFinder... finders) {
-        // copy the list, also checking for nulls
+        // copy the list and check for nulls
         final List<ModuleFinder> finderList = List.of(finders);
 
         return new ModuleFinder() {
--- a/src/java.base/share/classes/java/lang/module/ModulePath.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/src/java.base/share/classes/java/lang/module/ModulePath.java	Mon Oct 31 10:39:14 2016 -0700
@@ -33,10 +33,12 @@
 import java.io.InputStreamReader;
 import java.io.UncheckedIOException;
 import java.lang.module.ModuleDescriptor.Requires;
+import java.net.URI;
 import java.nio.file.DirectoryStream;
 import java.nio.file.Files;
 import java.nio.file.NoSuchFileException;
 import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.nio.file.attribute.BasicFileAttributes;
 import java.util.Collections;
 import java.util.HashMap;
@@ -52,49 +54,53 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
-import java.util.stream.Stream;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 
 import jdk.internal.jmod.JmodFile;
 import jdk.internal.jmod.JmodFile.Section;
-import jdk.internal.module.ConfigurableModuleFinder;
+import jdk.internal.module.Checks;
 import jdk.internal.perf.PerfCounter;
+import jdk.internal.util.jar.VersionedStream;
 
 
 /**
  * A {@code ModuleFinder} that locates modules on the file system by searching
  * a sequence of directories or packaged modules.
  *
- * The {@code ModuleFinder} can be configured to work in either the run-time
+ * The {@code ModuleFinder} can be created to work in either the run-time
  * or link-time phases. In both cases it locates modular JAR and exploded
- * modules. When configured for link-time then it additionally locates
+ * modules. When created for link-time then it additionally locates
  * modules in JMOD files.
  */
 
-class ModulePath implements ConfigurableModuleFinder {
+class ModulePath implements ModuleFinder {
     private static final String MODULE_INFO = "module-info.class";
 
+    // the version to use for multi-release modular JARs
+    private final Runtime.Version releaseVersion;
+
+    // true for the link phase (supports modules packaged in JMOD format)
+    private final boolean isLinkPhase;
+
     // the entries on this module path
     private final Path[] entries;
     private int next;
 
-    // true if in the link phase
-    private boolean isLinkPhase;
-
     // map of module name to module reference map for modules already located
     private final Map<String, ModuleReference> cachedModules = new HashMap<>();
 
-    ModulePath(Path... entries) {
+    ModulePath(Runtime.Version version, boolean isLinkPhase, Path... entries) {
+        this.releaseVersion = version;
+        this.isLinkPhase = isLinkPhase;
         this.entries = entries.clone();
         for (Path entry : this.entries) {
             Objects.requireNonNull(entry);
         }
     }
 
-    @Override
-    public void configurePhase(Phase phase) {
-        isLinkPhase = (phase == Phase.LINK_TIME);
+    ModulePath(Path... entries) {
+        this(JarFile.runtimeVersion(), false, entries);
     }
 
     @Override
@@ -239,9 +245,13 @@
                 if (mref != null) {
                     // can have at most one version of a module in the directory
                     String name = mref.descriptor().name();
-                    if (nameToReference.put(name, mref) != null) {
+                    ModuleReference previous = nameToReference.put(name, mref);
+                    if (previous != null) {
+                        String fn1 = fileName(mref);
+                        String fn2 = fileName(previous);
                         throw new FindException("Two versions of module "
-                                                  + name + " found in " + dir);
+                                                 + name + " found in " + dir
+                                                 + " (" + fn1 + " and " + fn2 + ")");
                     }
                 }
             }
@@ -294,6 +304,25 @@
     }
 
 
+    /**
+     * Returns a string with the file name of the module if possible.
+     * If the module location is not a file URI then return the URI
+     * as a string.
+     */
+    private String fileName(ModuleReference mref) {
+        URI uri = mref.location().orElse(null);
+        if (uri != null) {
+            if (uri.getScheme().equalsIgnoreCase("file")) {
+                Path file = Paths.get(uri);
+                return file.getFileName().toString();
+            } else {
+                return uri.toString();
+            }
+        } else {
+            return "<unknown>";
+        }
+    }
+
     // -- jmod files --
 
     private Set<String> jmodPackages(JmodFile jf) {
@@ -301,7 +330,7 @@
             .filter(e -> e.section() == Section.CLASSES)
             .map(JmodFile.Entry::name)
             .map(this::toPackageName)
-            .filter(pkg -> pkg.length() > 0) // module-info
+            .flatMap(Optional::stream)
             .collect(Collectors.toSet());
     }
 
@@ -328,8 +357,8 @@
     private static final String SERVICES_PREFIX = "META-INF/services/";
 
     /**
-     * Returns a container with the service type corresponding to the name of
-     * a services configuration file.
+     * Returns the service type corresponding to the name of a services
+     * configuration file if it is a valid Java identifier.
      *
      * For example, if called with "META-INF/services/p.S" then this method
      * returns a container with the value "p.S".
@@ -341,7 +370,8 @@
             String prefix = cf.substring(0, index);
             if (prefix.equals(SERVICES_PREFIX)) {
                 String sn = cf.substring(index);
-                return Optional.of(sn);
+                if (Checks.isJavaIdentifier(sn))
+                    return Optional.of(sn);
             }
         }
         return Optional.empty();
@@ -416,28 +446,28 @@
         if (vs != null)
             builder.version(vs);
 
-        // scan the entries in the JAR file to locate the .class and service
-        // configuration file
-        Map<Boolean, Set<String>> map =
-            versionedStream(jf)
-              .map(JarEntry::getName)
-              .filter(s -> (s.endsWith(".class") ^ s.startsWith(SERVICES_PREFIX)))
-              .collect(Collectors.partitioningBy(s -> s.endsWith(".class"),
-                                                 Collectors.toSet()));
-        Set<String> classFiles = map.get(Boolean.TRUE);
-        Set<String> configFiles = map.get(Boolean.FALSE);
+        // scan the names of the entries in the JAR file
+        Map<Boolean, Set<String>> map = VersionedStream.stream(jf)
+                .filter(e -> !e.isDirectory())
+                .map(JarEntry::getName)
+                .collect(Collectors.partitioningBy(e -> e.startsWith(SERVICES_PREFIX),
+                                                   Collectors.toSet()));
+
+        Set<String> resources = map.get(Boolean.FALSE);
+        Set<String> configFiles = map.get(Boolean.TRUE);
 
         // all packages are exported
-        classFiles.stream()
-            .map(c -> toPackageName(c))
-            .distinct()
-            .forEach(builder::exports);
+        resources.stream()
+                .map(this::toPackageName)
+                .flatMap(Optional::stream)
+                .distinct()
+                .forEach(builder::exports);
 
         // map names of service configuration files to service names
         Set<String> serviceNames = configFiles.stream()
-            .map(this::toServiceName)
-            .flatMap(Optional::stream)
-            .collect(Collectors.toSet());
+                .map(this::toServiceName)
+                .flatMap(Optional::stream)
+                .collect(Collectors.toSet());
 
         // parse each service configuration file
         for (String sn : serviceNames) {
@@ -502,25 +532,13 @@
         return mn;
     }
 
-    private Stream<JarEntry> versionedStream(JarFile jf) {
-        if (jf.isMultiRelease()) {
-            // a stream of JarEntries whose names are base names and whose
-            // contents are from the corresponding versioned entries in
-            // a multi-release jar file
-            return jf.stream().map(JarEntry::getName)
-                    .filter(name -> !name.startsWith("META-INF/versions/"))
-                    .map(jf::getJarEntry);
-        } else {
-            return jf.stream();
-        }
-    }
-
     private Set<String> jarPackages(JarFile jf) {
-        return versionedStream(jf)
-            .filter(e -> e.getName().endsWith(".class"))
-            .map(e -> toPackageName(e.getName()))
-            .filter(pkg -> pkg.length() > 0)   // module-info
-            .collect(Collectors.toSet());
+        return VersionedStream.stream(jf)
+                .filter(e -> !e.isDirectory())
+                .map(JarEntry::getName)
+                .map(this::toPackageName)
+                .flatMap(Optional::stream)
+                .collect(Collectors.toSet());
     }
 
     /**
@@ -535,7 +553,7 @@
         try (JarFile jf = new JarFile(file.toFile(),
                                       true,               // verify
                                       ZipFile.OPEN_READ,
-                                      JarFile.runtimeVersion()))
+                                      releaseVersion))
         {
             ModuleDescriptor md;
             JarEntry entry = jf.getJarEntry(MODULE_INFO);
@@ -565,11 +583,11 @@
     private Set<String> explodedPackages(Path dir) {
         try {
             return Files.find(dir, Integer.MAX_VALUE,
-                              ((path, attrs) -> attrs.isRegularFile() &&
-                               path.toString().endsWith(".class")))
-                .map(path -> toPackageName(dir.relativize(path)))
-                .filter(pkg -> pkg.length() > 0)   // module-info
-                .collect(Collectors.toSet());
+                              ((path, attrs) -> attrs.isRegularFile()))
+                    .map(path -> dir.relativize(path))
+                    .map(this::toPackageName)
+                    .flatMap(Optional::stream)
+                    .collect(Collectors.toSet());
         } catch (IOException x) {
             throw new UncheckedIOException(x);
         }
@@ -595,29 +613,62 @@
         return ModuleReferences.newExplodedModule(md, dir);
     }
 
+    /**
+     * Maps the name of an entry in a JAR or ZIP file to a package name.
+     *
+     * @throws IllegalArgumentException if the name is a class file in
+     *         the top-level directory of the JAR/ZIP file (and it's
+     *         not module-info.class)
+     */
+    private Optional<String> toPackageName(String name) {
+        assert !name.endsWith("/");
 
-    //
+        int index = name.lastIndexOf("/");
+        if (index == -1) {
+            if (name.endsWith(".class") && !name.equals(MODULE_INFO)) {
+                throw new IllegalArgumentException(name
+                        + " found in top-level directory:"
+                        + " (unnamed package not allowed in module)");
+            }
+            return Optional.empty();
+        }
 
-    // p/q/T.class => p.q
-    private String toPackageName(String cn) {
-        assert cn.endsWith(".class");
-        int start = 0;
-        int index = cn.lastIndexOf("/");
-        if (index > start) {
-            return cn.substring(start, index).replace('/', '.');
+        String pn = name.substring(0, index).replace('/', '.');
+        if (Checks.isJavaIdentifier(pn)) {
+            return Optional.of(pn);
         } else {
-            return "";
+            // not a valid package name
+            return Optional.empty();
         }
     }
 
-    private String toPackageName(Path path) {
-        String name = path.toString();
-        assert name.endsWith(".class");
-        int index = name.lastIndexOf(File.separatorChar);
-        if (index != -1) {
-            return name.substring(0, index).replace(File.separatorChar, '.');
+    /**
+     * Maps the relative path of an entry in an exploded module to a package
+     * name.
+     *
+     * @throws IllegalArgumentException if the name is a class file in
+     *         the top-level directory (and it's not module-info.class)
+     */
+    private Optional<String> toPackageName(Path file) {
+        assert file.getRoot() == null;
+
+        Path parent = file.getParent();
+        if (parent == null) {
+            String name = file.toString();
+            if (name.endsWith(".class") && !name.equals(MODULE_INFO)) {
+                throw new IllegalArgumentException(name
+                        + " found in in top-level directory"
+                        + " (unnamed package not allowed in module)");
+            }
+            return Optional.empty();
+        }
+
+        String pn = parent.toString().replace(File.separatorChar, '.');
+        if (Checks.isJavaIdentifier(pn)) {
+            return Optional.of(pn);
         } else {
-            return "";
+            // not a valid package name
+            return Optional.empty();
         }
     }
 
--- a/src/java.base/share/classes/java/lang/module/ModuleReader.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/src/java.base/share/classes/java/lang/module/ModuleReader.java	Mon Oct 31 10:39:14 2016 -0700
@@ -32,6 +32,7 @@
 import java.nio.ByteBuffer;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.stream.Stream;
 
 
 /**
@@ -44,6 +45,11 @@
  * module. A module reader is also intended to be used by {@code ClassLoader}
  * implementations that load classes and resources from modules. </p>
  *
+ * <p> A resource in a module is identified by a name that is a
+ * '{@code /}'-separated path string. For example, module {@code java.base} may
+ * have a resource "{@code java/lang/Object.class}" that, by convention, is the
+ * class file for {@code java.lang.Object}. </p>
+ *
  * <p> A {@code ModuleReader} is {@linkplain ModuleReference#open open} upon
  * creation and is closed by invoking the {@link #close close} method.  Failure
  * to close a module reader may result in a resource leak.  The {@code
@@ -52,8 +58,8 @@
  *
  * <p> A {@code ModuleReader} implementation may require permissions to access
  * resources in the module. Consequently the {@link #find find}, {@link #open
- * open} and {@link #read read} methods may throw {@code SecurityException} if
- * access is denied by the security manager. </p>
+ * open}, {@link #read read}, and {@link #list list} methods may throw {@code
+ * SecurityException} if access is denied by the security manager. </p>
  *
  * @see ModuleReference
  * @since 9
@@ -84,6 +90,9 @@
      * Opens a resource, returning an input stream to read the resource in
      * the module.
      *
+     * <p> The behavior of the input stream when used after the module reader
+     * is closed is implementation specific and therefore not specified. </p>
+     *
      * @implSpec The default implementation invokes the {@link #find(String)
      * find} method to get a URI to the resource. If found, then it attempts
      * to construct a {@link java.net.URL URL} and open a connection to the
@@ -172,17 +181,37 @@
     }
 
     /**
+     * Lists the contents of the module, returning a stream of elements that
+     * are the names of all resources in the module.
+     *
+     * <p> In lazy implementations then an {@code IOException} may be thrown
+     * when using the stream to list the module contents. If this occurs then
+     * the {@code IOException} will be wrapped in an {@link
+     * java.io.UncheckedIOException} and thrown from the method that caused the
+     * access to be attempted. {@code SecurityException} may also be thrown
+     * when using the stream to list the module contents and access is denied
+     * by the security manager. </p>
+     *
+     * <p> The behavior of the stream when used after the module reader is
+     * closed is implementation specific and therefore not specified. </p>
+     *
+     * @return A stream of elements that are the names of all resources
+     *         in the module
+     *
+     * @throws IOException
+     *         If an I/O error occurs or the module reader is closed
+     * @throws SecurityException
+     *         If denied by the security manager
+     */
+    Stream<String> list() throws IOException;
+
+    /**
      * Closes the module reader. Once closed then subsequent calls to locate or
-     * read a resource will fail by returning {@code Optional.empty()} or
-     * throwing {@code IOException}.
+     * read a resource will fail by throwing {@code IOException}.
      *
      * <p> A module reader is not required to be asynchronously closeable. If a
      * thread is reading a resource and another thread invokes the close method,
-     * then the second thread may block until the read operation is complete.
-     *
-     * <p> The behavior of {@code InputStream}s obtained using the {@link
-     * #open(String) open} method and used after the module reader is closed
-     * is implementation specific and therefore not specified.
+     * then the second thread may block until the read operation is complete. </p>
      */
     @Override
     void close() throws IOException;
--- a/src/java.base/share/classes/java/lang/module/ModuleReferences.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/src/java.base/share/classes/java/lang/module/ModuleReferences.java	Mon Oct 31 10:39:14 2016 -0700
@@ -35,6 +35,7 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.concurrent.locks.Lock;
@@ -43,14 +44,17 @@
 import java.util.function.Supplier;
 import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
-import java.util.zip.ZipEntry;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 import java.util.zip.ZipFile;
 
+import jdk.internal.jmod.JmodFile;
 import jdk.internal.misc.JavaLangAccess;
 import jdk.internal.misc.SharedSecrets;
 import jdk.internal.module.ModuleHashes;
 import jdk.internal.module.ModuleHashes.HashSupplier;
 import jdk.internal.module.ModulePatcher;
+import jdk.internal.util.jar.VersionedStream;
 import sun.net.www.ParseUtil;
 
 
@@ -140,6 +144,13 @@
         abstract Optional<InputStream> implOpen(String name) throws IOException;
 
         /**
+         * Returns a stream of the names of resources in the module. This
+         * method is invoked by the list method to do the actual work of
+         * creating the stream.
+         */
+        abstract Stream<String> implList() throws IOException;
+
+        /**
          * Closes the module reader. This method is invoked by close to do the
          * actual work of closing the module reader.
          */
@@ -175,7 +186,21 @@
         }
 
         @Override
-        public void close() throws IOException {
+        public final Stream<String> list() throws IOException {
+            readLock.lock();
+            try {
+                if (!closed) {
+                    return implList();
+                } else {
+                    throw new IOException("ModuleReader is closed");
+                }
+            } finally {
+                readLock.unlock();
+            }
+        }
+
+        @Override
+        public final void close() throws IOException {
             writeLock.lock();
             try {
                 if (!closed) {
@@ -241,6 +266,16 @@
         }
 
         @Override
+        Stream<String> implList() throws IOException {
+            // take snapshot to avoid async close
+            List<String> names = VersionedStream.stream(jf)
+                    .filter(e -> !e.isDirectory())
+                    .map(JarEntry::getName)
+                    .collect(Collectors.toList());
+            return names.stream();
+        }
+
+        @Override
         void implClose() throws IOException {
             jf.close();
         }
@@ -251,30 +286,31 @@
      * A ModuleReader for a JMOD file.
      */
     static class JModModuleReader extends SafeCloseModuleReader {
-        private final ZipFile zf;
+        private final JmodFile jf;
         private final URI uri;
 
-        static ZipFile newZipFile(Path path) {
+        static JmodFile newJmodFile(Path path) {
             try {
-                return new ZipFile(path.toFile());
+                return new JmodFile(path);
             } catch (IOException ioe) {
                 throw new UncheckedIOException(ioe);
             }
         }
 
         JModModuleReader(Path path, URI uri) {
-            this.zf = newZipFile(path);
+            this.jf = newJmodFile(path);
             this.uri = uri;
         }
 
-        private ZipEntry getEntry(String name) {
-            return zf.getEntry("classes/" + Objects.requireNonNull(name));
+        private JmodFile.Entry getEntry(String name) {
+            Objects.requireNonNull(name);
+            return jf.getEntry(JmodFile.Section.CLASSES, name);
         }
 
         @Override
         Optional<URI> implFind(String name) {
-            ZipEntry ze = getEntry(name);
-            if (ze != null) {
+            JmodFile.Entry je = getEntry(name);
+            if (je != null) {
                 String encodedPath = ParseUtil.encodePath(name, false);
                 String uris = "jmod:" + uri + "!/" + encodedPath;
                 return Optional.of(URI.create(uris));
@@ -285,17 +321,27 @@
 
         @Override
         Optional<InputStream> implOpen(String name) throws IOException {
-            ZipEntry ze = getEntry(name);
-            if (ze != null) {
-                return Optional.of(zf.getInputStream(ze));
+            JmodFile.Entry je = getEntry(name);
+            if (je != null) {
+                return Optional.of(jf.getInputStream(je));
             } else {
                 return Optional.empty();
             }
         }
 
         @Override
+        Stream<String> implList() throws IOException {
+            // take snapshot to avoid async close
+            List<String> names = jf.stream()
+                    .filter(e -> e.section() == JmodFile.Section.CLASSES)
+                    .map(JmodFile.Entry::name)
+                    .collect(Collectors.toList());
+            return names.stream();
+        }
+
+        @Override
         void implClose() throws IOException {
-            zf.close();
+            jf.close();
         }
     }
 
@@ -378,6 +424,17 @@
         }
 
         @Override
+        public Stream<String> list() throws IOException {
+            ensureOpen();
+            // sym links not followed
+            return Files.find(dir, Integer.MAX_VALUE,
+                              (path, attrs) -> attrs.isRegularFile())
+                    .map(f -> dir.relativize(f)
+                                 .toString()
+                                 .replace(File.separatorChar, '/'));
+        }
+
+        @Override
         public void close() {
             closed = true;
         }
--- a/src/java.base/share/classes/java/lang/module/SystemModuleFinder.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/src/java.base/share/classes/java/lang/module/SystemModuleFinder.java	Mon Oct 31 10:39:14 2016 -0700
@@ -32,14 +32,21 @@
 import java.net.URI;
 import java.net.URLConnection;
 import java.nio.ByteBuffer;
+import java.util.ArrayDeque;
 import java.util.Collections;
+import java.util.Deque;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
+import java.util.Spliterator;
+import java.util.function.Consumer;
 import java.util.function.Supplier;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
 
 import jdk.internal.jimage.ImageLocation;
 import jdk.internal.jimage.ImageReader;
@@ -62,6 +69,8 @@
 
 class SystemModuleFinder implements ModuleFinder {
 
+    private static final JavaNetUriAccess JNUA = SharedSecrets.getJavaNetUriAccess();
+
     private static final PerfCounter initTime
         = PerfCounter.newPerfCounter("jdk.module.finder.jimage.initTime");
     private static final PerfCounter moduleCount
@@ -73,8 +82,6 @@
     // ImageReader used to access all modules in the image
     private static final ImageReader imageReader;
 
-    private static final JavaNetUriAccess jnua = SharedSecrets.getJavaNetUriAccess();
-
     // the set of modules in the run-time image
     private static final Set<ModuleReference> modules;
 
@@ -170,8 +177,7 @@
                                                      HashSupplier hash)
     {
         String mn = md.name();
-
-        URI uri = jnua.create("jrt", "/".concat(mn));
+        URI uri = JNUA.create("jrt", "/".concat(mn));
 
         Supplier<ModuleReader> readerSupplier = new Supplier<>() {
             @Override
@@ -332,10 +338,101 @@
         }
 
         @Override
+        public Stream<String> list() throws IOException {
+            if (closed)
+                throw new IOException("ModuleReader is closed");
+
+            Spliterator<String> s = new ModuleContentSpliterator(module);
+            return StreamSupport.stream(s, false);
+        }
+
+        @Override
         public void close() {
             // nothing else to do
             closed = true;
         }
     }
 
+    /**
+     * A Spliterator for traversing the resources of a module linked into the
+     * run-time image.
+     */
+    static class ModuleContentSpliterator implements Spliterator<String> {
+        final String moduleRoot;
+        final Deque<ImageReader.Node> stack;
+        Iterator<ImageReader.Node> iterator;
+
+        ModuleContentSpliterator(String module) throws IOException {
+            moduleRoot = "/modules/" + module;
+            stack = new ArrayDeque<>();
+
+            // push the root node to the stack to get started
+            ImageReader.Node dir = imageReader.findNode(moduleRoot);
+            if (dir == null || !dir.isDirectory())
+                throw new IOException(moduleRoot + " not a directory");
+            stack.push(dir);
+            iterator = Collections.emptyIterator();
+        }
+
+        /**
+         * Returns the name of the next non-directory node or {@code null} if
+         * there are no remaining nodes to visit.
+         */
+        private String next() throws IOException {
+            for (;;) {
+                while (iterator.hasNext()) {
+                    ImageReader.Node node = iterator.next();
+                    String name = node.getName();
+                    if (node.isDirectory()) {
+                        // build node
+                        ImageReader.Node dir = imageReader.findNode(name);
+                        assert dir.isDirectory();
+                        stack.push(dir);
+                    } else {
+                        // strip /modules/$MODULE/ prefix
+                        return name.substring(moduleRoot.length() + 1);
+                    }
+                }
+
+                if (stack.isEmpty()) {
+                    return null;
+                } else {
+                    ImageReader.Node dir = stack.poll();
+                    assert dir.isDirectory();
+                    iterator = dir.getChildren().iterator();
+                }
+            }
+        }
+
+        @Override
+        public boolean tryAdvance(Consumer<? super String> action) {
+            String next;
+            try {
+                next = next();
+            } catch (IOException ioe) {
+                throw new UncheckedIOException(ioe);
+            }
+            if (next != null) {
+                action.accept(next);
+                return true;
+            } else {
+                return false;
+            }
+        }
+
+        @Override
+        public Spliterator<String> trySplit() {
+            return null;
+        }
+
+        @Override
+        public int characteristics() {
+            return Spliterator.DISTINCT + Spliterator.NONNULL + Spliterator.IMMUTABLE;
+        }
+
+        @Override
+        public long estimateSize() {
+            return Long.MAX_VALUE;
+        }
+    }
 }
--- a/src/java.base/share/classes/java/util/Scanner.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/src/java.base/share/classes/java/util/Scanner.java	Mon Oct 31 10:39:14 2016 -0700
@@ -1267,6 +1267,9 @@
     // The next operation should occur in the specified radix but
     // the default is left untouched.
     private void setRadix(int radix) {
+        if ((radix < Character.MIN_RADIX) || (radix > Character.MAX_RADIX))
+            throw new IllegalArgumentException("radix:"+radix);
+
         if (this.radix != radix) {
             // Force rebuilding and recompilation of radix dependent patterns
             integerPattern = null;
@@ -1811,10 +1814,15 @@
      * interpreted as a byte value in the specified radix using the
      * {@link #nextByte} method. The scanner does not advance past any input.
      *
+     * <p>If the radix is less than {@link Character#MIN_RADIX Character.MIN_RADIX}
+     * or greater than {@link Character#MAX_RADIX Character.MAX_RADIX}, then an
+     * {@code IllegalArgumentException} is thrown.
+     *
      * @param radix the radix used to interpret the token as a byte value
      * @return true if and only if this scanner's next token is a valid
      *         byte value
      * @throws IllegalStateException if this scanner is closed
+     * @throws IllegalArgumentException if the radix is out of range
      */
     public boolean hasNextByte(int radix) {
         setRadix(radix);
@@ -1869,6 +1877,10 @@
      * {@link Byte#parseByte(String, int) Byte.parseByte} with the
      * specified radix.
      *
+     * <p>If the radix is less than {@link Character#MIN_RADIX Character.MIN_RADIX}
+     * or greater than {@link Character#MAX_RADIX Character.MAX_RADIX}, then an
+     * {@code IllegalArgumentException} is thrown.
+     *
      * @param radix the radix used to interpret the token as a byte value
      * @return the {@code byte} scanned from the input
      * @throws InputMismatchException
@@ -1876,6 +1888,7 @@
      *         regular expression, or is out of range
      * @throws NoSuchElementException if input is exhausted
      * @throws IllegalStateException if this scanner is closed
+     * @throws IllegalArgumentException if the radix is out of range
      */
     public byte nextByte(int radix) {
         // Check cached result
@@ -1917,10 +1930,15 @@
      * interpreted as a short value in the specified radix using the
      * {@link #nextShort} method. The scanner does not advance past any input.
      *
+     * <p>If the radix is less than {@link Character#MIN_RADIX Character.MIN_RADIX}
+     * or greater than {@link Character#MAX_RADIX Character.MAX_RADIX}, then an
+     * {@code IllegalArgumentException} is thrown.
+     *
      * @param radix the radix used to interpret the token as a short value
      * @return true if and only if this scanner's next token is a valid
      *         short value in the specified radix
      * @throws IllegalStateException if this scanner is closed
+     * @throws IllegalArgumentException if the radix is out of range
      */
     public boolean hasNextShort(int radix) {
         setRadix(radix);
@@ -1975,6 +1993,10 @@
      * {@link Short#parseShort(String, int) Short.parseShort} with the
      * specified radix.
      *
+     * <p>If the radix is less than {@link Character#MIN_RADIX Character.MIN_RADIX}
+     * or greater than {@link Character#MAX_RADIX Character.MAX_RADIX}, then an
+     * {@code IllegalArgumentException} is thrown.
+     *
      * @param radix the radix used to interpret the token as a short value
      * @return the {@code short} scanned from the input
      * @throws InputMismatchException
@@ -1982,6 +2004,7 @@
      *         regular expression, or is out of range
      * @throws NoSuchElementException if input is exhausted
      * @throws IllegalStateException if this scanner is closed
+     * @throws IllegalArgumentException if the radix is out of range
      */
     public short nextShort(int radix) {
         // Check cached result
@@ -2023,10 +2046,15 @@
      * interpreted as an int value in the specified radix using the
      * {@link #nextInt} method. The scanner does not advance past any input.
      *
+     * <p>If the radix is less than {@link Character#MIN_RADIX Character.MIN_RADIX}
+     * or greater than {@link Character#MAX_RADIX Character.MAX_RADIX}, then an
+     * {@code IllegalArgumentException} is thrown.
+     *
      * @param radix the radix used to interpret the token as an int value
      * @return true if and only if this scanner's next token is a valid
      *         int value
      * @throws IllegalStateException if this scanner is closed
+     * @throws IllegalArgumentException if the radix is out of range
      */
     public boolean hasNextInt(int radix) {
         setRadix(radix);
@@ -2105,6 +2133,10 @@
      * {@link Integer#parseInt(String, int) Integer.parseInt} with the
      * specified radix.
      *
+     * <p>If the radix is less than {@link Character#MIN_RADIX Character.MIN_RADIX}
+     * or greater than {@link Character#MAX_RADIX Character.MAX_RADIX}, then an
+     * {@code IllegalArgumentException} is thrown.
+     *
      * @param radix the radix used to interpret the token as an int value
      * @return the {@code int} scanned from the input
      * @throws InputMismatchException
@@ -2112,6 +2144,7 @@
      *         regular expression, or is out of range
      * @throws NoSuchElementException if input is exhausted
      * @throws IllegalStateException if this scanner is closed
+     * @throws IllegalArgumentException if the radix is out of range
      */
     public int nextInt(int radix) {
         // Check cached result
@@ -2153,10 +2186,15 @@
      * interpreted as a long value in the specified radix using the
      * {@link #nextLong} method. The scanner does not advance past any input.
      *
+     * <p>If the radix is less than {@link Character#MIN_RADIX Character.MIN_RADIX}
+     * or greater than {@link Character#MAX_RADIX Character.MAX_RADIX}, then an
+     * {@code IllegalArgumentException} is thrown.
+     *
      * @param radix the radix used to interpret the token as a long value
      * @return true if and only if this scanner's next token is a valid
      *         long value
      * @throws IllegalStateException if this scanner is closed
+     * @throws IllegalArgumentException if the radix is out of range
      */
     public boolean hasNextLong(int radix) {
         setRadix(radix);
@@ -2211,6 +2249,10 @@
      * {@link Long#parseLong(String, int) Long.parseLong} with the
      * specified radix.
      *
+     * <p>If the radix is less than {@link Character#MIN_RADIX Character.MIN_RADIX}
+     * or greater than {@link Character#MAX_RADIX Character.MAX_RADIX}, then an
+     * {@code IllegalArgumentException} is thrown.
+     *
      * @param radix the radix used to interpret the token as an int value
      * @return the {@code long} scanned from the input
      * @throws InputMismatchException
@@ -2218,6 +2260,7 @@
      *         regular expression, or is out of range
      * @throws NoSuchElementException if input is exhausted
      * @throws IllegalStateException if this scanner is closed
+     * @throws IllegalArgumentException if the radix is out of range
      */
     public long nextLong(int radix) {
         // Check cached result
@@ -2450,10 +2493,15 @@
      * the {@link #nextBigInteger} method. The scanner does not advance past
      * any input.
      *
+     * <p>If the radix is less than {@link Character#MIN_RADIX Character.MIN_RADIX}
+     * or greater than {@link Character#MAX_RADIX Character.MAX_RADIX}, then an
+     * {@code IllegalArgumentException} is thrown.
+     *
      * @param radix the radix used to interpret the token as an integer
      * @return true if and only if this scanner's next token is a valid
      *         {@code BigInteger}
      * @throws IllegalStateException if this scanner is closed
+     * @throws IllegalArgumentException if the radix is out of range
      */
     public boolean hasNextBigInteger(int radix) {
         setRadix(radix);
@@ -2504,6 +2552,10 @@
      * java.math.BigInteger#BigInteger(java.lang.String)
      * BigInteger(String, int)} constructor with the specified radix.
      *
+     * <p>If the radix is less than {@link Character#MIN_RADIX Character.MIN_RADIX}
+     * or greater than {@link Character#MAX_RADIX Character.MAX_RADIX}, then an
+     * {@code IllegalArgumentException} is thrown.
+     *
      * @param radix the radix used to interpret the token
      * @return the {@code BigInteger} scanned from the input
      * @throws InputMismatchException
@@ -2511,6 +2563,7 @@
      *         regular expression, or is out of range
      * @throws NoSuchElementException if the input is exhausted
      * @throws IllegalStateException if this scanner is closed
+     * @throws IllegalArgumentException if the radix is out of range
      */
     public BigInteger nextBigInteger(int radix) {
         // Check cached result
--- a/src/java.base/share/classes/jdk/internal/jmod/JmodFile.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/src/java.base/share/classes/jdk/internal/jmod/JmodFile.java	Mon Oct 31 10:39:14 2016 -0700
@@ -176,6 +176,16 @@
     }
 
     /**
+     * Returns the {@code Entry} for a resource in a JMOD file section
+     * or {@code null} if not found.
+     */
+    public Entry getEntry(Section section, String name) {
+        String entry = section.jmodDir() + "/" + name;
+        ZipEntry ze = zipfile.getEntry(entry);
+        return (ze != null) ? new Entry(ze) : null;
+    }
+
+    /**
      * Opens an {@code InputStream} for reading the named entry of the given
      * section in this jmod file.
      *
@@ -185,7 +195,6 @@
     public InputStream getInputStream(Section section, String name)
         throws IOException
     {
-
         String entry = section.jmodDir() + "/" + name;
         ZipEntry e = zipfile.getEntry(entry);
         if (e == null) {
@@ -195,6 +204,15 @@
     }
 
     /**
+     * Opens an {@code InputStream} for reading an entry in the JMOD file.
+     *
+     * @throws IOException if an I/O error occurs
+     */
+    public InputStream getInputStream(Entry entry) throws IOException {
+        return zipfile.getInputStream(entry.zipEntry());
+    }
+
+    /**
      * Returns a stream of non-directory entries in this jmod file.
      */
     public Stream<Entry> stream() {
--- a/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java	Mon Oct 31 10:39:14 2016 -0700
@@ -53,6 +53,7 @@
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.jar.Attributes;
 import java.util.jar.Manifest;
+import java.util.stream.Stream;
 
 import jdk.internal.module.ModulePatcher.PatchedModuleReader;
 import jdk.internal.misc.VM;
@@ -749,6 +750,10 @@
             return Optional.empty();
         }
         @Override
+        public Stream<String> list() {
+            return Stream.empty();
+        }
+        @Override
         public void close() {
             throw new InternalError("Should not get here");
         }
--- a/src/java.base/share/classes/jdk/internal/loader/ClassLoaders.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/src/java.base/share/classes/jdk/internal/loader/ClassLoaders.java	Mon Oct 31 10:39:14 2016 -0700
@@ -69,16 +69,17 @@
             bcp = toURLClassPath(s);
 
         // we have a class path if -cp is specified or -m is not specified.
-        // If neither is specified then default to -cp <working directory>.
+        // If neither is specified then default to -cp <working directory>
+        // If -cp is not specified and -m is specified, the value of
+        // java.class.path is an empty string, then no class path.
         URLClassPath ucp = null;
         String mainMid = System.getProperty("jdk.module.main");
         String cp = System.getProperty("java.class.path");
-        if (mainMid == null && cp == null)
+        if (cp == null)
             cp = "";
-        if (cp != null)
+        if (mainMid == null || cp.length() > 0)
             ucp = toURLClassPath(cp);
 
-
         // create the class loaders
         BOOT_LOADER = new BootClassLoader(bcp);
         PLATFORM_LOADER = new PlatformClassLoader(BOOT_LOADER);
--- a/src/java.base/share/classes/jdk/internal/loader/Loader.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/src/java.base/share/classes/jdk/internal/loader/Loader.java	Mon Oct 31 10:39:14 2016 -0700
@@ -53,6 +53,7 @@
 import java.util.Map;
 import java.util.Optional;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Stream;
 
 
 /**
@@ -534,6 +535,10 @@
             return Optional.empty();
         }
         @Override
+        public Stream<String> list() {
+            return Stream.empty();
+        }
+        @Override
         public void close() {
             throw new InternalError("Should not get here");
         }
--- a/src/java.base/share/classes/jdk/internal/logger/DefaultLoggerFinder.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/src/java.base/share/classes/jdk/internal/logger/DefaultLoggerFinder.java	Mon Oct 31 10:39:14 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, 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
@@ -137,13 +137,20 @@
     }
 
     public static boolean isSystem(Module m) {
-        ClassLoader cl = AccessController.doPrivileged(new PrivilegedAction<>() {
+        return AccessController.doPrivileged(new PrivilegedAction<>() {
             @Override
-            public ClassLoader run() {
-                return m.getClassLoader();
+            public Boolean run() {
+                final ClassLoader moduleCL = m.getClassLoader();
+                if (moduleCL == null) return true;
+                ClassLoader cl = ClassLoader.getPlatformClassLoader();
+                while (cl != null && moduleCL != cl) {
+                    cl = cl.getParent();
+                }
+                // returns true if moduleCL is the platform class loader
+                // or one of its ancestors.
+                return moduleCL == cl;
             }
         });
-        return cl == null;
     }
 
     @Override
--- a/src/java.base/share/classes/jdk/internal/misc/JavaLangModuleAccess.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/src/java.base/share/classes/jdk/internal/misc/JavaLangModuleAccess.java	Mon Oct 31 10:39:14 2016 -0700
@@ -39,6 +39,7 @@
 import java.lang.module.ModuleReader;
 import java.lang.module.ModuleReference;
 import java.net.URI;
+import java.nio.file.Path;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
@@ -103,6 +104,11 @@
                                          ModuleHashes hashes);
 
     /**
+     * Returns the object with the hashes of other modules
+     */
+    Optional<ModuleHashes> hashes(ModuleDescriptor descriptor);
+
+    /**
      * Resolves a collection of root modules, with service binding
      * and the empty configuration as the parent. The post resolution
      * checks are optionally run.
@@ -120,8 +126,10 @@
                                      Supplier<ModuleReader> readerSupplier);
 
     /**
-     * Returns the object with the hashes of other modules
+     * Creates a ModuleFinder for a module path.
      */
-    Optional<ModuleHashes> hashes(ModuleDescriptor descriptor);
+    ModuleFinder newModulePath(Runtime.Version version,
+                               boolean isLinkPhase,
+                               Path... entries);
 
 }
--- a/src/java.base/share/classes/jdk/internal/module/ConfigurableModuleFinder.java	Mon Oct 31 14:50:09 2016 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package jdk.internal.module;
-
-import java.lang.module.ModuleFinder;
-
-/**
- * A ModuleFinder that may be configured to work at either run-time
- * or link-time.
- */
-
-public interface ConfigurableModuleFinder extends ModuleFinder {
-
-    public static enum Phase {
-        RUN_TIME,
-        LINK_TIME
-    }
-
-    /**
-     * Configures this finder to work in the given phase.
-     */
-    void configurePhase(Phase phase);
-
-}
--- a/src/java.base/share/classes/jdk/internal/module/ModuleInfoWriter.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/src/java.base/share/classes/jdk/internal/module/ModuleInfoWriter.java	Mon Oct 31 10:39:14 2016 -0700
@@ -57,7 +57,15 @@
         cw.visit(Opcodes.V1_9, ACC_MODULE, name, null, null, null);
 
         cw.visitAttribute(new ModuleAttribute(md));
-        cw.visitAttribute(new ConcealedPackagesAttribute(md.conceals()));
+
+        // for tests: write the ConcealedPackages attribute when there are non-exported packages
+        long nExportedPackages = md.exports().stream()
+                .map(ModuleDescriptor.Exports::source)
+                .distinct()
+                .count();
+        if (md.packages().size() > nExportedPackages)
+            cw.visitAttribute(new ConcealedPackagesAttribute(md.packages()));
+
         md.version().ifPresent(v -> cw.visitAttribute(new VersionAttribute(v)));
         md.mainClass().ifPresent(mc -> cw.visitAttribute(new MainClassAttribute(mc)));
 
--- a/src/java.base/share/classes/jdk/internal/module/ModulePatcher.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/src/java.base/share/classes/jdk/internal/module/ModulePatcher.java	Mon Oct 31 10:39:14 2016 -0700
@@ -50,6 +50,7 @@
 import java.util.Set;
 import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
+import java.util.stream.Stream;
 
 import jdk.internal.loader.Resource;
 import jdk.internal.misc.JavaLangModuleAccess;
@@ -159,21 +160,19 @@
                     // is not supported by the boot class loader
                     try (JarFile jf = new JarFile(file.toFile())) {
                         jf.stream()
-                          .filter(e -> e.getName().endsWith(".class"))
                           .map(e -> toPackageName(file, e))
-                          .filter(pn -> pn.length() > 0)
+                          .filter(Checks::isJavaIdentifier)
                           .forEach(packages::add);
                     }
 
                 } else if (Files.isDirectory(file)) {
 
-                    // exploded directory
+                    // exploded directory without following sym links
                     Path top = file;
                     Files.find(top, Integer.MAX_VALUE,
-                            ((path, attrs) -> attrs.isRegularFile() &&
-                                    path.toString().endsWith(".class")))
+                               ((path, attrs) -> attrs.isRegularFile()))
                             .map(path -> toPackageName(top, path))
-                            .filter(pn -> pn.length() > 0)
+                            .filter(Checks::isJavaIdentifier)
                             .forEach(packages::add);
 
                 }
@@ -381,6 +380,15 @@
         }
 
         @Override
+        public Stream<String> list() throws IOException {
+            Stream<String> s = delegate().list();
+            for (ResourceFinder finder : finders) {
+                s = Stream.concat(s, finder.list());
+            }
+            return s.distinct();
+        }
+
+        @Override
         public void close() throws IOException {
             closeAll(finders);
             delegate().close();
@@ -393,6 +401,7 @@
      */
     private static interface ResourceFinder extends Closeable {
         Resource find(String name) throws IOException;
+        Stream<String> list() throws IOException;
     }
 
 
@@ -453,6 +462,13 @@
                 }
             };
         }
+
+        @Override
+        public Stream<String> list() throws IOException {
+            return jf.stream()
+                    .filter(e -> !e.isDirectory())
+                    .map(JarEntry::getName);
+        }
     }
 
 
@@ -527,6 +543,15 @@
                 }
             };
         }
+
+        @Override
+        public Stream<String> list() throws IOException {
+            return Files.find(dir, Integer.MAX_VALUE,
+                              (path, attrs) -> attrs.isRegularFile())
+                    .map(f -> dir.relativize(f)
+                                 .toString()
+                                 .replace(File.separatorChar, '/'));
+        }
     }
 
 
@@ -537,7 +562,7 @@
         Path entry = top.relativize(file);
         Path parent = entry.getParent();
         if (parent == null) {
-            return warnUnnamedPackage(top, entry.toString());
+            return warnIfModuleInfo(top, entry.toString());
         } else {
             return parent.toString().replace(File.separatorChar, '.');
         }
@@ -557,14 +582,15 @@
         String name = entry.getName();
         int index = name.lastIndexOf("/");
         if (index == -1) {
-            return warnUnnamedPackage(file, name);
+            return warnIfModuleInfo(file, name);
         } else {
             return name.substring(0, index).replace('/', '.');
         }
     }
 
-    private static String warnUnnamedPackage(Path file, String e) {
-        System.err.println("WARNING: " + e + " not allowed in patch: " + file);
+    private static String warnIfModuleInfo(Path file, String e) {
+        if (e.equals("module-info.class"))
+            System.err.println("WARNING: " + e + " ignored in patch: " + file);
         return "";
     }
 
--- a/src/java.base/share/classes/sun/launcher/resources/launcher.properties	Mon Oct 31 14:50:09 2016 +0300
+++ b/src/java.base/share/classes/sun/launcher/resources/launcher.properties	Mon Oct 31 10:39:14 2016 -0700
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2007, 2016, 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
@@ -31,7 +31,7 @@
 \           (to execute the main class in a module)\n\
 where options include:\n
 
-java.launcher.opt.datamodel  =\    -d{0}\t  use a {0}-bit data model if available\n
+java.launcher.opt.datamodel  =\    -d{0}\t  Deprecated, will be removed in a future release\n
 java.launcher.opt.vmselect   =\    {0}\t  to select the "{1}" VM\n
 java.launcher.opt.hotspot    =\    {0}\t  is a synonym for the "{1}" VM  [deprecated]\n
 
--- a/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java	Mon Oct 31 10:39:14 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, 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
@@ -42,10 +42,6 @@
 
     private DTLSReassembler reassembler = null;
 
-    // Cache the session identifier for the detection of session-resuming
-    // handshake.
-    byte[]              prevSessionID = new byte[0];
-
     int                 readEpoch;
 
     int                 prevReadEpoch;
@@ -114,13 +110,7 @@
     @Override
     Plaintext acquirePlaintext() {
         if (reassembler != null) {
-            Plaintext plaintext = reassembler.acquirePlaintext();
-            if (reassembler.finished()) {
-                // discard all buffered unused message.
-                reassembler = null;
-            }
-
-            return plaintext;
+            return reassembler.acquirePlaintext();
         }
 
         return null;
@@ -149,40 +139,54 @@
         packet.get(recordEnS);
         int recordEpoch = ((recordEnS[0] & 0xFF) << 8) |
                            (recordEnS[1] & 0xFF);          // pos: 3, 4
-        long recordSeq  = Authenticator.toLong(recordEnS);
+        long recordSeq  = ((recordEnS[2] & 0xFFL) << 40) |
+                          ((recordEnS[3] & 0xFFL) << 32) |
+                          ((recordEnS[4] & 0xFFL) << 24) |
+                          ((recordEnS[5] & 0xFFL) << 16) |
+                          ((recordEnS[6] & 0xFFL) <<  8) |
+                           (recordEnS[7] & 0xFFL);         // pos: 5-10
+
         int contentLen = ((packet.get() & 0xFF) << 8) |
-                          (packet.get() & 0xFF);            // pos: 11, 12
+                          (packet.get() & 0xFF);           // pos: 11, 12
 
         if (debug != null && Debug.isOn("record")) {
-             System.out.println(Thread.currentThread().getName() +
-                    ", READ: " +
+            Debug.log("READ: " +
                     ProtocolVersion.valueOf(majorVersion, minorVersion) +
                     " " + Record.contentName(contentType) + ", length = " +
                     contentLen);
         }
 
         int recLim = srcPos + DTLSRecord.headerSize + contentLen;
-        if (this.readEpoch > recordEpoch) {
-            // Discard old records delivered before this epoch.
 
+        if (this.prevReadEpoch > recordEpoch) {
             // Reset the position of the packet buffer.
             packet.position(recLim);
+            if (debug != null && Debug.isOn("record")) {
+                Debug.printHex("READ: discard this old record", recordEnS);
+            }
             return null;
         }
 
+        // Buffer next epoch message if necessary.
         if (this.readEpoch < recordEpoch) {
-            if (contentType != Record.ct_handshake) {
-                // just discard it if not a handshake message
+            // Discard the record younger than the current epcoh if:
+            // 1. it is not a handshake message, or
+            // 2. it is not of next epoch.
+            if (((contentType != Record.ct_handshake) &&
+                    (contentType != Record.ct_change_cipher_spec)) ||
+                (this.readEpoch < (recordEpoch - 1))) {
+
                 packet.position(recLim);
+
+                if (debug != null && Debug.isOn("verbose")) {
+                    Debug.log("Premature record (epoch), discard it.");
+                }
+
                 return null;
             }
 
-            // Not ready to decrypt this record, may be encrypted Finished
+            // Not ready to decrypt this record, may be an encrypted Finished
             // message, need to buffer it.
-            if (reassembler == null) {
-               reassembler = new DTLSReassembler();
-            }
-
             byte[] fragment = new byte[contentLen];
             packet.get(fragment);              // copy the fragment
             RecordFragment buffered = new RecordFragment(fragment, contentType,
@@ -194,94 +198,130 @@
             // consume the full record in the packet buffer.
             packet.position(recLim);
 
-            Plaintext plaintext = reassembler.acquirePlaintext();
-            if (reassembler.finished()) {
-                // discard all buffered unused message.
+            return reassembler.acquirePlaintext();
+        }
+
+        //
+        // Now, the message is of this epoch or the previous epoch.
+        //
+        Authenticator decodeAuthenticator;
+        CipherBox decodeCipher;
+        if (this.readEpoch == recordEpoch) {
+            decodeAuthenticator = readAuthenticator;
+            decodeCipher = readCipher;
+        } else {                        // prevReadEpoch == recordEpoch
+            decodeAuthenticator = prevReadAuthenticator;
+            decodeCipher = prevReadCipher;
+        }
+
+        // decrypt the fragment
+        packet.limit(recLim);
+        packet.position(srcPos + DTLSRecord.headerSize);
+
+        ByteBuffer plaintextFragment;
+        try {
+            plaintextFragment = decrypt(decodeAuthenticator,
+                    decodeCipher, contentType, packet, recordEnS);
+        } catch (BadPaddingException bpe) {
+            if (debug != null && Debug.isOn("ssl")) {
+                Debug.log("Discard invalid record: " + bpe);
+            }
+
+            // invalid, discard this record [section 4.1.2.7, RFC 6347]
+            return null;
+        } finally {
+            // comsume a complete record
+            packet.limit(srcLim);
+            packet.position(recLim);
+        }
+
+        if (contentType != Record.ct_change_cipher_spec &&
+            contentType != Record.ct_handshake) {   // app data or alert
+                                                    // no retransmission
+            // Cleanup the handshake reassembler if necessary.
+            if ((reassembler != null) &&
+                    (reassembler.handshakeEpoch < recordEpoch)) {
+                if (debug != null && Debug.isOn("verbose")) {
+                    Debug.log("Cleanup the handshake reassembler");
+                }
+
                 reassembler = null;
             }
 
-            return plaintext;
+            return new Plaintext(contentType, majorVersion, minorVersion,
+                    recordEpoch, Authenticator.toLong(recordEnS),
+                    plaintextFragment);
         }
 
-        if (this.readEpoch == recordEpoch) {
-            // decrypt the fragment
-            packet.limit(recLim);
-            packet.position(srcPos + DTLSRecord.headerSize);
-
-            ByteBuffer plaintextFragment;
-            try {
-                plaintextFragment = decrypt(readAuthenticator,
-                        readCipher, contentType, packet, recordEnS);
-            } catch (BadPaddingException bpe) {
-                if (debug != null && Debug.isOn("ssl")) {
-                    System.out.println(Thread.currentThread().getName() +
-                            " discard invalid record: " + bpe);
-                }
-
-                // invalid, discard this record [section 4.1.2.7, RFC 6347]
-                return null;
-            } finally {
-                // comsume a complete record
-                packet.limit(srcLim);
-                packet.position(recLim);
-            }
-
-            if (contentType != Record.ct_change_cipher_spec &&
-                contentType != Record.ct_handshake) {   // app data or alert
-                                                        // no retransmission
-               return new Plaintext(contentType, majorVersion, minorVersion,
-                        recordEpoch, recordSeq, plaintextFragment);
-            }
-
-            if (contentType == Record.ct_change_cipher_spec) {
-                if (reassembler == null) {
+        if (contentType == Record.ct_change_cipher_spec) {
+            if (reassembler == null) {
+                if (this.readEpoch != recordEpoch) {
                     // handshake has not started, should be an
                     // old handshake message, discard it.
+
+                    if (debug != null && Debug.isOn("verbose")) {
+                        Debug.log(
+                                "Lagging behind ChangeCipherSpec, discard it.");
+                    }
+
                     return null;
                 }
 
-                reassembler.queueUpFragment(
-                        new RecordFragment(plaintextFragment, contentType,
-                                majorVersion, minorVersion,
-                                recordEnS, recordEpoch, recordSeq, false));
-            } else {    // handshake record
-                // One record may contain 1+ more handshake messages.
-                while (plaintextFragment.remaining() > 0) {
+                reassembler = new DTLSReassembler(recordEpoch);
+            }
 
-                    HandshakeFragment hsFrag = parseHandshakeMessage(
-                        contentType, majorVersion, minorVersion,
-                        recordEnS, recordEpoch, recordSeq, plaintextFragment);
+            reassembler.queueUpChangeCipherSpec(
+                    new RecordFragment(plaintextFragment, contentType,
+                            majorVersion, minorVersion,
+                            recordEnS, recordEpoch, recordSeq, false));
+        } else {    // handshake record
+            // One record may contain 1+ more handshake messages.
+            while (plaintextFragment.remaining() > 0) {
 
-                    if (hsFrag == null) {
-                        // invalid, discard this record
+                HandshakeFragment hsFrag = parseHandshakeMessage(
+                    contentType, majorVersion, minorVersion,
+                    recordEnS, recordEpoch, recordSeq, plaintextFragment);
+
+                if (hsFrag == null) {
+                    // invalid, discard this record
+                    if (debug != null && Debug.isOn("verbose")) {
+                        Debug.log("Invalid handshake message, discard it.");
+                    }
+
+                    return null;
+                }
+
+                if (reassembler == null) {
+                    if (this.readEpoch != recordEpoch) {
+                        // handshake has not started, should be an
+                        // old handshake message, discard it.
+
+                        if (debug != null && Debug.isOn("verbose")) {
+                            Debug.log(
+                                "Lagging behind handshake record, discard it.");
+                        }
+
                         return null;
                     }
 
-                    if ((reassembler == null) &&
-                            isKickstart(hsFrag.handshakeType)) {
-                       reassembler = new DTLSReassembler();
-                    }
-
-                    if (reassembler != null) {
-                        reassembler.queueUpHandshake(hsFrag);
-                    }   // else, just ignore the message.
-                }
-            }
-
-            // Completed the read of the full record. Acquire the reassembled
-            // messages.
-            if (reassembler != null) {
-                Plaintext plaintext = reassembler.acquirePlaintext();
-                if (reassembler.finished()) {
-                    // discard all buffered unused message.
-                    reassembler = null;
+                    reassembler = new DTLSReassembler(recordEpoch);
                 }
 
-                return plaintext;
+                reassembler.queueUpHandshake(hsFrag);
             }
         }
 
-        return null;    // make the complier happy
+        // Completed the read of the full record.  Acquire the reassembled
+        // messages.
+        if (reassembler != null) {
+            return reassembler.acquirePlaintext();
+        }
+
+        if (debug != null && Debug.isOn("verbose")) {
+            Debug.log("The reassembler is not initialized yet.");
+        }
+
+        return null;
     }
 
     @Override
@@ -330,12 +370,6 @@
         }
     }
 
-    private static boolean isKickstart(byte handshakeType) {
-        return (handshakeType == HandshakeMessage.ht_client_hello) ||
-               (handshakeType == HandshakeMessage.ht_hello_request) ||
-               (handshakeType == HandshakeMessage.ht_hello_verify_request);
-    }
-
     private static HandshakeFragment parseHandshakeMessage(
             byte contentType, byte majorVersion, byte minorVersion,
             byte[] recordEnS, int recordEpoch, long recordSeq,
@@ -344,9 +378,7 @@
         int remaining = plaintextFragment.remaining();
         if (remaining < handshakeHeaderSize) {
             if (debug != null && Debug.isOn("ssl")) {
-                System.out.println(
-                        Thread.currentThread().getName() +
-                        " discard invalid record: " +
+                Debug.log("Discard invalid record: " +
                         "too small record to hold a handshake fragment");
             }
 
@@ -372,9 +404,7 @@
                  (plaintextFragment.get() & 0xFF);          // pos: 9-11
         if ((remaining - handshakeHeaderSize) < fragmentLength) {
             if (debug != null && Debug.isOn("ssl")) {
-                System.out.println(
-                        Thread.currentThread().getName() +
-                        " discard invalid record: " +
+                Debug.log("Discard invalid record: " +
                         "not a complete handshake fragment in the record");
             }
 
@@ -431,7 +461,39 @@
 
         @Override
         public int compareTo(RecordFragment o) {
-            return Long.compareUnsigned(this.recordSeq, o.recordSeq);
+            if (this.contentType == Record.ct_change_cipher_spec) {
+                if (o.contentType == Record.ct_change_cipher_spec) {
+                    // Only one incoming ChangeCipherSpec message for an epoch.
+                    //
+                    // Ignore duplicated ChangeCipherSpec messages.
+                    return Integer.compare(this.recordEpoch, o.recordEpoch);
+                } else if ((this.recordEpoch == o.recordEpoch) &&
+                        (o.contentType == Record.ct_handshake)) {
+                    // ChangeCipherSpec is the latest message of an epoch.
+                    return 1;
+                }
+            } else if (o.contentType == Record.ct_change_cipher_spec) {
+                if ((this.recordEpoch == o.recordEpoch) &&
+                        (this.contentType == Record.ct_handshake)) {
+                    // ChangeCipherSpec is the latest message of an epoch.
+                    return -1;
+                } else {
+                    // different epoch or this is not a handshake message
+                    return compareToSequence(o.recordEpoch, o.recordSeq);
+                }
+            }
+
+            return compareToSequence(o.recordEpoch, o.recordSeq);
+        }
+
+        int compareToSequence(int epoch, long seq) {
+            if (this.recordEpoch > epoch) {
+                return 1;
+            } else if (this.recordEpoch == epoch) {
+                return Long.compare(this.recordSeq, seq);
+            } else {
+                return -1;
+            }
         }
     }
 
@@ -465,12 +527,24 @@
             if (o instanceof HandshakeFragment) {
                 HandshakeFragment other = (HandshakeFragment)o;
                 if (this.messageSeq != other.messageSeq) {
-                    // keep the insertion order for the same message
+                    // keep the insertion order of handshake messages
                     return this.messageSeq - other.messageSeq;
+                } else if (this.fragmentOffset != other.fragmentOffset) {
+                    // small fragment offset was transmitted first
+                    return this.fragmentOffset - other.fragmentOffset;
+                } else if (this.fragmentLength == other.fragmentLength) {
+                    // retransmissions, ignore duplicated messages.
+                    return 0;
                 }
+
+                // Should be repacked for suitable fragment length.
+                //
+                // Note that the acquiring processes will reassemble the
+                // the fragments later.
+                return compareToSequence(o.recordEpoch, o.recordSeq);
             }
 
-            return Long.compareUnsigned(this.recordSeq, o.recordSeq);
+            return super.compareTo(o);
         }
     }
 
@@ -484,24 +558,72 @@
         }
     }
 
+    private static final class HandshakeFlight implements Cloneable {
+        static final byte HF_UNKNOWN = HandshakeMessage.ht_not_applicable;
+
+        byte        handshakeType;      // handshake type
+        int         flightEpoch;        // the epoch of the first message
+        int         minMessageSeq;      // minimal message sequence
+
+        int         maxMessageSeq;      // maximum message sequence
+        int         maxRecordEpoch;     // maximum record sequence number
+        long        maxRecordSeq;       // maximum record sequence number
+
+        HashMap<Byte, List<HoleDescriptor>> holesMap;
+
+        HandshakeFlight() {
+            this.handshakeType = HF_UNKNOWN;
+            this.flightEpoch = 0;
+            this.minMessageSeq = 0;
+
+            this.maxMessageSeq = 0;
+            this.maxRecordEpoch = 0;
+            this.maxRecordSeq = -1;
+
+            this.holesMap = new HashMap<>(5);
+        }
+
+        boolean isRetransmitOf(HandshakeFlight hs) {
+            return (hs != null) &&
+                   (this.handshakeType == hs.handshakeType) &&
+                   (this.minMessageSeq == hs.minMessageSeq);
+        }
+
+        @Override
+        public Object clone() {
+            HandshakeFlight hf = new HandshakeFlight();
+
+            hf.handshakeType = this.handshakeType;
+            hf.flightEpoch = this.flightEpoch;
+            hf.minMessageSeq = this.minMessageSeq;
+
+            hf.maxMessageSeq = this.maxMessageSeq;
+            hf.maxRecordEpoch = this.maxRecordEpoch;
+            hf.maxRecordSeq = this.maxRecordSeq;
+
+            hf.holesMap = new HashMap<>(this.holesMap);
+
+            return hf;
+        }
+    }
+
     final class DTLSReassembler {
+        // The handshake epoch.
+        final int handshakeEpoch;
+
+        // The buffered fragments.
         TreeSet<RecordFragment> bufferedFragments = new TreeSet<>();
 
-        HashMap<Byte, List<HoleDescriptor>> holesMap = new HashMap<>(5);
+        // The handshake flight in progress.
+        HandshakeFlight handshakeFlight = new HandshakeFlight();
 
-        // Epoch, sequence number and handshake message sequence of the
-        // beginning message of a flight.
-        byte        flightType = (byte)0xFF;
-
-        int         flightTopEpoch = 0;
-        long        flightTopRecordSeq = -1;
-        int         flightTopMessageSeq = 0;
+        // The preceding handshake flight.
+        HandshakeFlight precedingFlight = null;
 
         // Epoch, sequence number and handshake message sequence of the
         // next message acquisition of a flight.
-        int         nextRecordEpoch = 0;    // next record epoch
+        int         nextRecordEpoch;        // next record epoch
         long        nextRecordSeq = 0;      // next record sequence number
-        int         nextMessageSeq = 0;     // next handshake message number
 
         // Expect ChangeCipherSpec and Finished messages for the final flight.
         boolean     expectCCSFlight = false;
@@ -510,65 +632,66 @@
         boolean     flightIsReady = false;
         boolean     needToCheckFlight = false;
 
-        // Is it a session-resuming abbreviated handshake.?
-        boolean     isAbbreviatedHandshake = false;
+        DTLSReassembler(int handshakeEpoch) {
+            this.handshakeEpoch = handshakeEpoch;
+            this.nextRecordEpoch = handshakeEpoch;
 
-        // The handshke fragment with the biggest record sequence number
-        // in a flight, not counting the Finished message.
-        HandshakeFragment lastHandshakeFragment = null;
-
-        // Is handshake (intput) finished?
-        boolean handshakeFinished = false;
-
-        DTLSReassembler() {
-            // blank
-        }
-
-        boolean finished() {
-            return handshakeFinished;
+            this.handshakeFlight.flightEpoch = handshakeEpoch;
         }
 
         void expectingFinishFlight() {
             expectCCSFlight = true;
         }
 
+        // Queue up a handshake message.
         void queueUpHandshake(HandshakeFragment hsf) {
-
-            if ((nextRecordEpoch > hsf.recordEpoch) ||
-                    (nextRecordSeq > hsf.recordSeq) ||
-                    (nextMessageSeq > hsf.messageSeq)) {
-                // too old, discard this record
+            if (!isDesirable(hsf)) {
+                // Not a dedired record, discard it.
                 return;
             }
 
+            // Clean up the retransmission messages if necessary.
+            cleanUpRetransmit(hsf);
+
             // Is it the first message of next flight?
-            if ((flightTopMessageSeq == hsf.messageSeq) &&
-                    (hsf.fragmentOffset == 0) && (flightTopRecordSeq == -1)) {
+            //
+            // Note: the Finished message is handled in the final CCS flight.
+            boolean isMinimalFlightMessage = false;
+            if (handshakeFlight.minMessageSeq == hsf.messageSeq) {
+                isMinimalFlightMessage = true;
+            } else if ((precedingFlight != null) &&
+                    (precedingFlight.minMessageSeq == hsf.messageSeq)) {
+                isMinimalFlightMessage = true;
+            }
 
-                flightType = hsf.handshakeType;
-                flightTopEpoch = hsf.recordEpoch;
-                flightTopRecordSeq = hsf.recordSeq;
+            if (isMinimalFlightMessage && (hsf.fragmentOffset == 0) &&
+                    (hsf.handshakeType != HandshakeMessage.ht_finished)) {
 
-                if (hsf.handshakeType == HandshakeMessage.ht_server_hello) {
-                    // Is it a session-resuming handshake?
-                    try {
-                        isAbbreviatedHandshake =
-                                isSessionResuming(hsf.fragment, prevSessionID);
-                    } catch (SSLException ssle) {
-                        if (debug != null && Debug.isOn("ssl")) {
-                            System.out.println(
-                                    Thread.currentThread().getName() +
-                                    " discard invalid record: " + ssle);
-                        }
+                // reset the handshake flight
+                handshakeFlight.handshakeType = hsf.handshakeType;
+                handshakeFlight.flightEpoch = hsf.recordEpoch;
+                handshakeFlight.minMessageSeq = hsf.messageSeq;
+            }
 
-                        // invalid, discard it [section 4.1.2.7, RFC 6347]
-                        return;
+            if (hsf.handshakeType == HandshakeMessage.ht_finished) {
+                handshakeFlight.maxMessageSeq = hsf.messageSeq;
+                handshakeFlight.maxRecordEpoch = hsf.recordEpoch;
+                handshakeFlight.maxRecordSeq = hsf.recordSeq;
+            } else {
+                if (handshakeFlight.maxMessageSeq < hsf.messageSeq) {
+                    handshakeFlight.maxMessageSeq = hsf.messageSeq;
+                }
+
+                int n = (hsf.recordEpoch - handshakeFlight.maxRecordEpoch);
+                if (n > 0) {
+                    handshakeFlight.maxRecordEpoch = hsf.recordEpoch;
+                    handshakeFlight.maxRecordSeq = hsf.recordSeq;
+                } else if (n == 0) {
+                    // the same epoch
+                    if (handshakeFlight.maxRecordSeq < hsf.recordSeq) {
+                        handshakeFlight.maxRecordSeq = hsf.recordSeq;
                     }
-
-                    if (!isAbbreviatedHandshake) {
-                        prevSessionID = getSessionID(hsf.fragment);
-                    }
-                }
+                }   // Otherwise, it is unlikely to happen.
             }
 
             boolean fragmented = false;
@@ -578,7 +701,8 @@
                 fragmented = true;
             }
 
-            List<HoleDescriptor> holes = holesMap.get(hsf.handshakeType);
+            List<HoleDescriptor> holes =
+                    handshakeFlight.holesMap.get(hsf.handshakeType);
             if (holes == null) {
                 if (!fragmented) {
                     holes = Collections.emptyList();
@@ -586,7 +710,7 @@
                     holes = new LinkedList<HoleDescriptor>();
                     holes.add(new HoleDescriptor(0, hsf.messageLength));
                 }
-                holesMap.put(hsf.handshakeType, holes);
+                handshakeFlight.holesMap.put(hsf.handshakeType, holes);
             } else if (holes.isEmpty()) {
                 // Have got the full handshake message.  This record may be
                 // a handshake message retransmission.  Discard this record.
@@ -594,20 +718,11 @@
                 // It's OK to discard retransmission as the handshake hash
                 // is computed as if each handshake message had been sent
                 // as a single fragment.
-                //
-                // Note that ClientHello messages are delivered twice in
-                // DTLS handshaking.
-                if ((hsf.handshakeType != HandshakeMessage.ht_client_hello &&
-                     hsf.handshakeType != ht_hello_verify_request) ||
-                        (nextMessageSeq != hsf.messageSeq)) {
-                    return;
+                if (debug != null && Debug.isOn("verbose")) {
+                    Debug.log("Have got the full message, discard it.");
                 }
 
-                if (fragmented) {
-                    holes = new LinkedList<HoleDescriptor>();
-                    holes.add(new HoleDescriptor(0, hsf.messageLength));
-                }
-                holesMap.put(hsf.handshakeType, holes);
+                return;
             }
 
             if (fragmented) {
@@ -628,9 +743,7 @@
                          (hole.limit < fragmentLimit))) {
 
                         if (debug != null && Debug.isOn("ssl")) {
-                            System.out.println(
-                                Thread.currentThread().getName() +
-                                " discard invalid record: " +
+                            Debug.log("Discard invalid record: " +
                                 "handshake fragment ranges are overlapping");
                         }
 
@@ -659,48 +772,205 @@
                 }
             }
 
-            // append this fragment
-            bufferedFragments.add(hsf);
-
-            if ((lastHandshakeFragment == null) ||
-                (lastHandshakeFragment.compareTo(hsf) < 0)) {
-
-                lastHandshakeFragment = hsf;
+            // buffer this fragment
+            if (hsf.handshakeType == HandshakeMessage.ht_finished) {
+                // Need no status update.
+                bufferedFragments.add(hsf);
+            } else {
+                bufferFragment(hsf);
             }
-
-            if (flightIsReady) {
-                flightIsReady = false;
-            }
-            needToCheckFlight = true;
         }
 
-        // queue up change_cipher_spec or encrypted message
-        void queueUpFragment(RecordFragment rf) {
-            if ((nextRecordEpoch > rf.recordEpoch) ||
-                    (nextRecordSeq > rf.recordSeq)) {
-                // too old, discard this record
+        // Queue up a ChangeCipherSpec message
+        void queueUpChangeCipherSpec(RecordFragment rf) {
+            if (!isDesirable(rf)) {
+                // Not a dedired record, discard it.
                 return;
             }
 
-            // Is it the first message of next flight?
-            if (expectCCSFlight &&
-                    (rf.contentType == Record.ct_change_cipher_spec)) {
+            // Clean up the retransmission messages if necessary.
+            cleanUpRetransmit(rf);
 
-                flightType = (byte)0xFE;
-                flightTopEpoch = rf.recordEpoch;
-                flightTopRecordSeq = rf.recordSeq;
+            // Is it the first message of this flight?
+            //
+            // Note: the first message of the final flight is ChangeCipherSpec.
+            if (expectCCSFlight) {
+                handshakeFlight.handshakeType = HandshakeFlight.HF_UNKNOWN;
+                handshakeFlight.flightEpoch = rf.recordEpoch;
             }
 
+            // The epoch should be the same as the first message of the flight.
+            if (handshakeFlight.maxRecordSeq < rf.recordSeq) {
+                handshakeFlight.maxRecordSeq = rf.recordSeq;
+            }
+
+            // buffer this fragment
+            bufferFragment(rf);
+        }
+
+        // Queue up a ciphertext message.
+        //
+        // Note: not yet be able to decrypt the message.
+        void queueUpFragment(RecordFragment rf) {
+            if (!isDesirable(rf)) {
+                // Not a dedired record, discard it.
+                return;
+            }
+
+            // Clean up the retransmission messages if necessary.
+            cleanUpRetransmit(rf);
+
+            // buffer this fragment
+            bufferFragment(rf);
+        }
+
+        private void bufferFragment(RecordFragment rf) {
             // append this fragment
             bufferedFragments.add(rf);
 
             if (flightIsReady) {
                 flightIsReady = false;
             }
-            needToCheckFlight = true;
+
+            if (!needToCheckFlight) {
+                needToCheckFlight = true;
+            }
         }
 
-        boolean isEmpty() {
+        private void cleanUpRetransmit(RecordFragment rf) {
+            // Does the next flight start?
+            boolean isNewFlight = false;
+            if (precedingFlight != null) {
+                if (precedingFlight.flightEpoch < rf.recordEpoch) {
+                    isNewFlight = true;
+                } else {
+                    if (rf instanceof HandshakeFragment) {
+                        HandshakeFragment hsf = (HandshakeFragment)rf;
+                        if (precedingFlight.maxMessageSeq  < hsf.messageSeq) {
+                            isNewFlight = true;
+                        }
+                    } else if (rf.contentType != Record.ct_change_cipher_spec) {
+                        // ciphertext
+                        if (precedingFlight.maxRecordEpoch < rf.recordEpoch) {
+                            isNewFlight = true;
+                        }
+                    }
+                }
+            }
+
+            if (!isNewFlight) {
+                // Need no cleanup.
+                return;
+            }
+
+            // clean up the buffer
+            for (Iterator<RecordFragment> it = bufferedFragments.iterator();
+                    it.hasNext();) {
+
+                RecordFragment frag = it.next();
+                boolean isOld = false;
+                if (frag.recordEpoch < precedingFlight.maxRecordEpoch) {
+                    isOld = true;
+                } else if (frag.recordEpoch == precedingFlight.maxRecordEpoch) {
+                    if (frag.recordSeq <= precedingFlight.maxRecordSeq) {
+                        isOld = true;
+                    }
+                }
+
+                if (!isOld && (frag instanceof HandshakeFragment)) {
+                    HandshakeFragment hsf = (HandshakeFragment)frag;
+                    isOld = (hsf.messageSeq <= precedingFlight.maxMessageSeq);
+                }
+
+                if (isOld) {
+                    it.remove();
+                } else {
+                    // Safe to break as items in the buffer are ordered.
+                    break;
+                }
+            }
+
+            // discard retransmissions of the previous flight if any.
+            precedingFlight = null;
+        }
+
+        // Is a desired record?
+        //
+        // Check for retransmission and lost records.
+        private boolean isDesirable(RecordFragment rf) {
+            //
+            // Discard records old than the previous epoch.
+            //
+            int previousEpoch = nextRecordEpoch - 1;
+            if (rf.recordEpoch < previousEpoch) {
+                // Too old to use, discard this record.
+                if (debug != null && Debug.isOn("verbose")) {
+                    Debug.log("Too old epoch to use this record, discard it.");
+                }
+
+                return false;
+            }
+
+            //
+            // Allow retransmission of last flight of the previous epoch
+            //
+            // For example, the last server delivered flight for session
+            // resuming abbreviated handshaking consist three messages:
+            //      ServerHello
+            //      [ChangeCipherSpec]
+            //      Finished
+            //
+            // The epoch number is incremented and the sequence number is reset
+            // if the ChangeCipherSpec is sent.
+            if (rf.recordEpoch == previousEpoch) {
+                boolean isDesired = true;
+                if (precedingFlight == null) {
+                    isDesired = false;
+                } else {
+                    if (rf instanceof HandshakeFragment) {
+                        HandshakeFragment hsf = (HandshakeFragment)rf;
+                        if (precedingFlight.minMessageSeq > hsf.messageSeq) {
+                            isDesired = false;
+                        }
+                    } else if (rf.contentType == Record.ct_change_cipher_spec) {
+                        // ChangeCipherSpec
+                        if (precedingFlight.flightEpoch != rf.recordEpoch) {
+                            isDesired = false;
+                        }
+                    } else {        // ciphertext
+                        if ((rf.recordEpoch < precedingFlight.maxRecordEpoch) ||
+                            (rf.recordEpoch == precedingFlight.maxRecordEpoch &&
+                                rf.recordSeq <= precedingFlight.maxRecordSeq)) {
+                            isDesired = false;
+                        }
+                    }
+                }
+
+                if (!isDesired) {
+                    // Too old to use, discard this retransmitted record
+                    if (debug != null && Debug.isOn("verbose")) {
+                        Debug.log("Too old retransmission to use, discard it.");
+                    }
+
+                    return false;
+                }
+            } else if ((rf.recordEpoch == nextRecordEpoch) &&
+                    (nextRecordSeq > rf.recordSeq)) {
+
+                // Previously disordered record for the current epoch.
+                //
+                // Should has been retransmitted. Discard this record.
+                if (debug != null && Debug.isOn("verbose")) {
+                    Debug.log("Lagging behind record (sequence), discard it.");
+                }
+
+                return false;
+            }
+
+            return true;
+        }
+
+        private boolean isEmpty() {
             return (bufferedFragments.isEmpty() ||
                     (!flightIsReady && !needToCheckFlight) ||
                     (needToCheckFlight && !flightIsReady()));
@@ -708,12 +978,9 @@
 
         Plaintext acquirePlaintext() {
             if (bufferedFragments.isEmpty()) {
-                // reset the flight
-                if (flightIsReady) {
-                    flightIsReady = false;
-                    needToCheckFlight = false;
+                if (debug != null && Debug.isOn("verbose")) {
+                    Debug.log("No received handshake messages");
                 }
-
                 return null;
             }
 
@@ -721,27 +988,103 @@
                 // check the fligth status
                 flightIsReady = flightIsReady();
 
-                // set for next flight
+                // Reset if this flight is ready.
                 if (flightIsReady) {
-                    flightTopMessageSeq = lastHandshakeFragment.messageSeq + 1;
-                    flightTopRecordSeq = -1;
+                    // Retransmitted handshake messages are not needed for
+                    // further handshaking processing.
+                    if (handshakeFlight.isRetransmitOf(precedingFlight)) {
+                        // cleanup
+                        bufferedFragments.clear();
+
+                        // Reset the next handshake flight.
+                        resetHandshakeFlight(precedingFlight);
+
+                        if (debug != null && Debug.isOn("verbose")) {
+                            Debug.log("Received a retransmission flight.");
+                        }
+
+                        return Plaintext.PLAINTEXT_NULL;
+                    }
                 }
 
                 needToCheckFlight = false;
             }
 
             if (!flightIsReady) {
+                if (debug != null && Debug.isOn("verbose")) {
+                    Debug.log("The handshake flight is not ready to use: " +
+                                handshakeFlight.handshakeType);
+                }
                 return null;
             }
 
             RecordFragment rFrag = bufferedFragments.first();
+            Plaintext plaintext;
             if (!rFrag.isCiphertext) {
                 // handshake message, or ChangeCipherSpec message
-                return acquireHandshakeMessage();
+                plaintext = acquireHandshakeMessage();
+
+                // Reset the handshake flight.
+                if (bufferedFragments.isEmpty()) {
+                    // Need not to backup the holes map.  Clear up it at first.
+                    handshakeFlight.holesMap.clear();   // cleanup holes map
+
+                    // Update the preceding flight.
+                    precedingFlight = (HandshakeFlight)handshakeFlight.clone();
+
+                    // Reset the next handshake flight.
+                    resetHandshakeFlight(precedingFlight);
+
+                    if (expectCCSFlight &&
+                            (precedingFlight.flightEpoch ==
+                                    HandshakeFlight.HF_UNKNOWN)) {
+                        expectCCSFlight = false;
+                    }
+                }
             } else {
                 // a Finished message or other ciphertexts
-                return acquireCachedMessage();
+                plaintext = acquireCachedMessage();
             }
+
+            return plaintext;
+        }
+
+        //
+        // Reset the handshake flight from a previous one.
+        //
+        private void resetHandshakeFlight(HandshakeFlight prev) {
+            // Reset the next handshake flight.
+            handshakeFlight.handshakeType = HandshakeFlight.HF_UNKNOWN;
+            handshakeFlight.flightEpoch = prev.maxRecordEpoch;
+            if (prev.flightEpoch != prev.maxRecordEpoch) {
+                // a new epoch starts
+                handshakeFlight.minMessageSeq = 0;
+            } else {
+                // stay at the same epoch
+                //
+                // The minimal message sequence number will get updated if
+                // a flight retransmission happens.
+                handshakeFlight.minMessageSeq = prev.maxMessageSeq + 1;
+            }
+
+            // cleanup the maximum sequence number and epoch number.
+            //
+            // Note: actually, we need to do nothing because the reassembler
+            // of handshake messages will reset them properly even for
+            // retransmissions.
+            //
+            handshakeFlight.maxMessageSeq = 0;
+            handshakeFlight.maxRecordEpoch = handshakeFlight.flightEpoch;
+
+            // Record sequence number cannot wrap even for retransmissions.
+            handshakeFlight.maxRecordSeq = prev.maxRecordSeq + 1;
+
+            // cleanup holes map
+            handshakeFlight.holesMap.clear();
+
+            // Ready to accept new input record.
+            flightIsReady = false;
+            needToCheckFlight = false;
         }
 
         private Plaintext acquireCachedMessage() {
@@ -750,6 +1093,9 @@
             if (readEpoch != rFrag.recordEpoch) {
                 if (readEpoch > rFrag.recordEpoch) {
                     // discard old records
+                    if (debug != null && Debug.isOn("verbose")) {
+                        Debug.log("Discard old buffered ciphertext fragments.");
+                    }
                     bufferedFragments.remove(rFrag);    // popup the fragment
                 }
 
@@ -757,6 +1103,10 @@
                 if (flightIsReady) {
                     flightIsReady = false;
                 }
+
+                if (debug != null && Debug.isOn("verbose")) {
+                    Debug.log("Not yet ready to decrypt the cached fragments.");
+                }
                 return null;
             }
 
@@ -768,9 +1118,8 @@
                 plaintextFragment = decrypt(readAuthenticator, readCipher,
                         rFrag.contentType, fragment, rFrag.recordEnS);
             } catch (BadPaddingException bpe) {
-                if (debug != null && Debug.isOn("ssl")) {
-                    System.out.println(Thread.currentThread().getName() +
-                            " discard invalid record: " + bpe);
+                if (debug != null && Debug.isOn("verbose")) {
+                    Debug.log("Discard invalid record: " + bpe);
                 }
 
                 // invalid, discard this record [section 4.1.2.7, RFC 6347]
@@ -782,7 +1131,6 @@
             // beginning of the next flight) message.  Need not to check
             // any ChangeCipherSpec message.
             if (rFrag.contentType == Record.ct_handshake) {
-                HandshakeFragment finFrag = null;
                 while (plaintextFragment.remaining() > 0) {
                     HandshakeFragment hsFrag = parseHandshakeMessage(
                             rFrag.contentType,
@@ -792,66 +1140,31 @@
 
                     if (hsFrag == null) {
                         // invalid, discard this record
+                        if (debug != null && Debug.isOn("verbose")) {
+                            Debug.printHex(
+                                    "Invalid handshake fragment, discard it",
+                                    plaintextFragment);
+                        }
                         return null;
                     }
 
-                    if (hsFrag.handshakeType == HandshakeMessage.ht_finished) {
-                        finFrag = hsFrag;
-
-                        // reset for the next flight
-                        this.flightType = (byte)0xFF;
-                        this.flightTopEpoch = rFrag.recordEpoch;
-                        this.flightTopMessageSeq = hsFrag.messageSeq + 1;
-                        this.flightTopRecordSeq = -1;
-                    } else {
-                        // reset the flight
-                        if (flightIsReady) {
-                            flightIsReady = false;
-                        }
-                        queueUpHandshake(hsFrag);
+                    queueUpHandshake(hsFrag);
+                    // The flight ready status (flightIsReady) should have
+                    // been checked and updated for the Finished handshake
+                    // message before the decryption.  Please don't update
+                    // flightIsReady for Finished messages.
+                    if (hsFrag.handshakeType != HandshakeMessage.ht_finished) {
+                        flightIsReady = false;
+                        needToCheckFlight = true;
                     }
                 }
 
-                this.nextRecordSeq = rFrag.recordSeq + 1;
-                this.nextMessageSeq = 0;
-
-                if (finFrag != null) {
-                    this.nextRecordEpoch = finFrag.recordEpoch;
-                    this.nextRecordSeq = finFrag.recordSeq + 1;
-                    this.nextMessageSeq = finFrag.messageSeq + 1;
-
-                    // Finished message does not fragment.
-                    byte[] recordFrag = new byte[finFrag.messageLength + 4];
-                    Plaintext plaintext = new Plaintext(finFrag.contentType,
-                            finFrag.majorVersion, finFrag.minorVersion,
-                            finFrag.recordEpoch, finFrag.recordSeq,
-                            ByteBuffer.wrap(recordFrag));
-
-                    // fill the handshake fragment of the record
-                    recordFrag[0] = finFrag.handshakeType;
-                    recordFrag[1] =
-                            (byte)((finFrag.messageLength >>> 16) & 0xFF);
-                    recordFrag[2] =
-                            (byte)((finFrag.messageLength >>> 8) & 0xFF);
-                    recordFrag[3] = (byte)(finFrag.messageLength & 0xFF);
-
-                    System.arraycopy(finFrag.fragment, 0,
-                            recordFrag, 4, finFrag.fragmentLength);
-
-                    // handshake hashing
-                    handshakeHashing(finFrag, plaintext);
-
-                    // input handshake finished
-                    handshakeFinished = true;
-
-                    return plaintext;
-                } else {
-                    return acquirePlaintext();
-                }
+                return acquirePlaintext();
             } else {
                 return new Plaintext(rFrag.contentType,
                         rFrag.majorVersion, rFrag.minorVersion,
-                        rFrag.recordEpoch, rFrag.recordSeq,
+                        rFrag.recordEpoch,
+                        Authenticator.toLong(rFrag.recordEnS),
                         plaintextFragment);
             }
         }
@@ -861,17 +1174,23 @@
             RecordFragment rFrag = bufferedFragments.first();
             if (rFrag.contentType == Record.ct_change_cipher_spec) {
                 this.nextRecordEpoch = rFrag.recordEpoch + 1;
+
+                // For retransmissions, the next record sequence number is a
+                // positive value.  Don't worry about it as the acquiring of
+                // the immediately followed Finished handshake message will
+                // reset the next record sequence number correctly.
                 this.nextRecordSeq = 0;
-                // no change on next handshake message sequence number
 
-                bufferedFragments.remove(rFrag);        // popup the fragment
+                // Popup the fragment.
+                bufferedFragments.remove(rFrag);
 
                 // Reload if this message has been reserved for handshake hash.
                 handshakeHash.reload();
 
                 return new Plaintext(rFrag.contentType,
                         rFrag.majorVersion, rFrag.minorVersion,
-                        rFrag.recordEpoch, rFrag.recordSeq,
+                        rFrag.recordEpoch,
+                        Authenticator.toLong(rFrag.recordEnS),
                         ByteBuffer.wrap(rFrag.fragment));
             } else {    // rFrag.contentType == Record.ct_handshake
                 HandshakeFragment hsFrag = (HandshakeFragment)rFrag;
@@ -882,13 +1201,13 @@
 
                     // this.nextRecordEpoch = hsFrag.recordEpoch;
                     this.nextRecordSeq = hsFrag.recordSeq + 1;
-                    this.nextMessageSeq = hsFrag.messageSeq + 1;
 
                     // Note: may try to avoid byte array copy in the future.
                     byte[] recordFrag = new byte[hsFrag.messageLength + 4];
                     Plaintext plaintext = new Plaintext(hsFrag.contentType,
                             hsFrag.majorVersion, hsFrag.minorVersion,
-                            hsFrag.recordEpoch, hsFrag.recordSeq,
+                            hsFrag.recordEpoch,
+                            Authenticator.toLong(hsFrag.recordEnS),
                             ByteBuffer.wrap(recordFrag));
 
                     // fill the handshake fragment of the record
@@ -913,7 +1232,8 @@
                     byte[] recordFrag = new byte[hsFrag.messageLength + 4];
                     Plaintext plaintext = new Plaintext(hsFrag.contentType,
                             hsFrag.majorVersion, hsFrag.minorVersion,
-                            hsFrag.recordEpoch, hsFrag.recordSeq,
+                            hsFrag.recordEpoch,
+                            Authenticator.toLong(hsFrag.recordEnS),
                             ByteBuffer.wrap(recordFrag));
 
                     // fill the handshake fragment of the record
@@ -957,7 +1277,6 @@
                     handshakeHashing(hsFrag, plaintext);
 
                     this.nextRecordSeq = maxRecodeSN + 1;
-                    this.nextMessageSeq = msgSeq + 1;
 
                     return plaintext;
                 }
@@ -966,15 +1285,26 @@
 
         boolean flightIsReady() {
 
-            //
-            // the ChangeCipherSpec/Finished flight
-            //
-            if (expectCCSFlight) {
-                // Have the ChangeCipherSpec/Finished messages been received?
-                return hasFinisedMessage(bufferedFragments);
-            }
+            byte flightType = handshakeFlight.handshakeType;
+            if (flightType == HandshakeFlight.HF_UNKNOWN) {
+                //
+                // the ChangeCipherSpec/Finished flight
+                //
+                if (expectCCSFlight) {
+                    // Have the ChangeCipherSpec/Finished flight been received?
+                    boolean isReady = hasFinishedMessage(bufferedFragments);
+                    if (debug != null && Debug.isOn("verbose")) {
+                        Debug.log(
+                            "Has the final flight been received? " + isReady);
+                    }
 
-            if (flightType == (byte)0xFF) {
+                    return isReady;
+                }
+
+                if (debug != null && Debug.isOn("verbose")) {
+                    Debug.log("No flight is received yet.");
+                }
+
                 return false;
             }
 
@@ -983,7 +1313,12 @@
                 (flightType == HandshakeMessage.ht_hello_verify_request)) {
 
                 // single handshake message flight
-                return hasCompleted(holesMap.get(flightType));
+                boolean isReady = hasCompleted(flightType);
+                if (debug != null && Debug.isOn("verbose")) {
+                    Debug.log("Is the handshake message completed? " + isReady);
+                }
+
+                return isReady;
             }
 
             //
@@ -991,31 +1326,52 @@
             //
             if (flightType == HandshakeMessage.ht_server_hello) {
                 // Firstly, check the first flight handshake message.
-                if (!hasCompleted(holesMap.get(flightType))) {
+                if (!hasCompleted(flightType)) {
+                    if (debug != null && Debug.isOn("verbose")) {
+                        Debug.log(
+                            "The ServerHello message is not completed yet.");
+                    }
+
                     return false;
                 }
 
                 //
                 // an abbreviated handshake
                 //
-                if (isAbbreviatedHandshake) {
-                    // Ready to use the flight if received the
-                    // ChangeCipherSpec and Finished messages.
-                    return hasFinisedMessage(bufferedFragments);
+                if (hasFinishedMessage(bufferedFragments)) {
+                    if (debug != null && Debug.isOn("verbose")) {
+                        Debug.log("It's an abbreviated handshake.");
+                    }
+
+                    return true;
                 }
 
                 //
                 // a full handshake
                 //
-                if (lastHandshakeFragment.handshakeType !=
-                        HandshakeMessage.ht_server_hello_done) {
+                List<HoleDescriptor> holes = handshakeFlight.holesMap.get(
+                        HandshakeMessage.ht_server_hello_done);
+                if ((holes == null) || !holes.isEmpty()) {
                     // Not yet got the final message of the flight.
+                    if (debug != null && Debug.isOn("verbose")) {
+                        Debug.log("Not yet got the ServerHelloDone message");
+                    }
+
                     return false;
                 }
 
                 // Have all handshake message been received?
-                return hasCompleted(bufferedFragments,
-                    flightTopMessageSeq, lastHandshakeFragment.messageSeq);
+                boolean isReady = hasCompleted(bufferedFragments,
+                            handshakeFlight.minMessageSeq,
+                            handshakeFlight.maxMessageSeq);
+                if (debug != null && Debug.isOn("verbose")) {
+                    Debug.log("Is the ServerHello flight (message " +
+                            handshakeFlight.minMessageSeq + "-" +
+                            handshakeFlight.maxMessageSeq +
+                            ") completed? " + isReady);
+                }
+
+                return isReady;
             }
 
             //
@@ -1029,92 +1385,65 @@
                 (flightType == HandshakeMessage.ht_client_key_exchange)) {
 
                 // Firstly, check the first flight handshake message.
-                if (!hasCompleted(holesMap.get(flightType))) {
+                if (!hasCompleted(flightType)) {
+                    if (debug != null && Debug.isOn("verbose")) {
+                        Debug.log(
+                            "The ClientKeyExchange or client Certificate " +
+                            "message is not completed yet.");
+                    }
+
                     return false;
                 }
 
-                if (!hasFinisedMessage(bufferedFragments)) {
-                    // not yet got the ChangeCipherSpec/Finished messages
-                    return false;
+                // Is client CertificateVerify a mandatory message?
+                if (flightType == HandshakeMessage.ht_certificate) {
+                    if (needClientVerify(bufferedFragments) &&
+                        !hasCompleted(ht_certificate_verify)) {
+
+                        if (debug != null && Debug.isOn("verbose")) {
+                            Debug.log(
+                                "Not yet have the CertificateVerify message");
+                        }
+
+                        return false;
+                    }
                 }
 
-                if (flightType == HandshakeMessage.ht_client_key_exchange) {
-                    // single handshake message flight
-                    return true;
-                }
+                if (!hasFinishedMessage(bufferedFragments)) {
+                    // not yet have the ChangeCipherSpec/Finished messages
+                    if (debug != null && Debug.isOn("verbose")) {
+                        Debug.log(
+                            "Not yet have the ChangeCipherSpec and " +
+                            "Finished messages");
+                    }
 
-                //
-                // flightType == HandshakeMessage.ht_certificate
-                //
-                // We don't support certificates containing fixed
-                // Diffie-Hellman parameters.  Therefore, CertificateVerify
-                // message is required if client Certificate message presents.
-                //
-                if (lastHandshakeFragment.handshakeType !=
-                        HandshakeMessage.ht_certificate_verify) {
-                    // Not yet got the final message of the flight.
                     return false;
                 }
 
                 // Have all handshake message been received?
-                return hasCompleted(bufferedFragments,
-                    flightTopMessageSeq, lastHandshakeFragment.messageSeq);
+                boolean isReady = hasCompleted(bufferedFragments,
+                            handshakeFlight.minMessageSeq,
+                            handshakeFlight.maxMessageSeq);
+                if (debug != null && Debug.isOn("verbose")) {
+                    Debug.log("Is the ClientKeyExchange flight (message " +
+                            handshakeFlight.minMessageSeq + "-" +
+                            handshakeFlight.maxMessageSeq +
+                            ") completed? " + isReady);
+                }
+
+                return isReady;
             }
 
             //
             // Otherwise, need to receive more handshake messages.
             //
-            return false;
-        }
-
-        private boolean isSessionResuming(
-                byte[] fragment, byte[] prevSid) throws SSLException {
-
-            // As the first fragment of ServerHello should be big enough
-            // to hold the session_id field, need not to worry about the
-            // fragmentation here.
-            if ((fragment == null) || (fragment.length < 38)) {
-                                    // 38: the minimal ServerHello body length
-                throw new SSLException(
-                        "Invalid ServerHello message: no sufficient data");
-            }
-
-            int sidLen = fragment[34];          // 34: the length field
-            if (sidLen > 32) {                  // opaque SessionID<0..32>
-                throw new SSLException(
-                        "Invalid ServerHello message: invalid session id");
-            }
-
-            if (fragment.length < 38 + sidLen) {
-                throw new SSLException(
-                        "Invalid ServerHello message: no sufficient data");
-            }
-
-            if (sidLen != 0 && (prevSid.length == sidLen)) {
-                // may be a session-resuming handshake
-                for (int i = 0; i < sidLen; i++) {
-                    if (prevSid[i] != fragment[35 + i]) {
-                                                // 35: the session identifier
-                        return false;
-                    }
-                }
-
-                return true;
+            if (debug != null && Debug.isOn("verbose")) {
+                Debug.log("Need to receive more handshake messages");
             }
 
             return false;
         }
 
-        private byte[] getSessionID(byte[] fragment) {
-            // The validity has been checked in the call to isSessionResuming().
-            int sidLen = fragment[34];      // 34: the sessionID length field
-
-            byte[] temporary = new byte[sidLen];
-            System.arraycopy(fragment, 35, temporary, 0, sidLen);
-
-            return temporary;
-        }
-
         // Looking for the ChangeCipherSpec and Finished messages.
         //
         // As the cached Finished message should be a ciphertext, we don't
@@ -1122,8 +1451,7 @@
         // to the spec of TLS/DTLS handshaking, a Finished message is always
         // sent immediately after a ChangeCipherSpec message.  The first
         // ciphertext handshake message should be the expected Finished message.
-        private boolean hasFinisedMessage(
-                Set<RecordFragment> fragments) {
+        private boolean hasFinishedMessage(Set<RecordFragment> fragments) {
 
             boolean hasCCS = false;
             boolean hasFin = false;
@@ -1147,7 +1475,35 @@
             return hasFin && hasCCS;
         }
 
-        private boolean hasCompleted(List<HoleDescriptor> holes) {
+        // Is client CertificateVerify a mandatory message?
+        //
+        // In the current implementation, client CertificateVerify is a
+        // mandatory message if the client Certificate is not empty.
+        private boolean needClientVerify(Set<RecordFragment> fragments) {
+
+            // The caller should have checked the completion of the first
+            // present handshake message.  Need not to check it again.
+            for (RecordFragment rFrag : fragments) {
+                if ((rFrag.contentType != Record.ct_handshake) ||
+                        rFrag.isCiphertext) {
+                    break;
+                }
+
+                HandshakeFragment hsFrag = (HandshakeFragment)rFrag;
+                if (hsFrag.handshakeType != HandshakeMessage.ht_certificate) {
+                    continue;
+                }
+
+                return (rFrag.fragment != null) &&
+                   (rFrag.fragment.length > DTLSRecord.minCertPlaintextSize);
+            }
+
+            return false;
+        }
+
+        private boolean hasCompleted(byte handshakeType) {
+            List<HoleDescriptor> holes =
+                    handshakeFlight.holesMap.get(handshakeType);
             if (holes == null) {
                 // not yet received this kind of handshake message
                 return false;
@@ -1173,7 +1529,7 @@
                     continue;
                 } else if (hsFrag.messageSeq == (presentMsgSeq + 1)) {
                     // check the completion of the handshake message
-                    if (!hasCompleted(holesMap.get(hsFrag.handshakeType))) {
+                    if (!hasCompleted(hsFrag.handshakeType)) {
                         return false;
                     }
 
--- a/src/java.base/share/classes/sun/security/ssl/DTLSOutputRecord.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/DTLSOutputRecord.java	Mon Oct 31 10:39:14 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2016, 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
@@ -279,6 +279,16 @@
         fragmenter = null;
     }
 
+    @Override
+    void launchRetransmission() {
+        // Note: Please don't retransmit if there are handshake messages
+        // or alerts waiting in the queue.
+        if (((alertMemos == null) || alertMemos.isEmpty()) &&
+                (fragmenter != null) && fragmenter.isRetransmittable()) {
+            fragmenter.setRetransmission();
+        }
+    }
+
     // buffered record fragment
     private static class RecordMemo {
         byte            contentType;
--- a/src/java.base/share/classes/sun/security/ssl/DTLSRecord.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/DTLSRecord.java	Mon Oct 31 10:39:14 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2016, 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
@@ -84,4 +84,18 @@
                                     + maxPadding            // padding
                                     + maxMacSize;           // MAC
 
+    /*
+     * Minimum record size of Certificate handshake message.
+     * Client sends a certificate message containing no certificates if no
+     * suitable certificate is available.  That is, the certificate_list
+     * structure has a length of zero.
+     *
+     *   struct {
+     *       ASN.1Cert certificate_list<0..2^24-1>;
+     *   } Certificate;
+     */
+    static final int    minCertPlaintextSize =
+                                      headerSize            // record header
+                                    + handshakeHeaderSize   // handshake header
+                                    + 3;                    // cert list length
 }
--- a/src/java.base/share/classes/sun/security/ssl/Debug.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/Debug.java	Mon Oct 31 10:39:14 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2016, 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
@@ -145,6 +145,13 @@
     }
 
     /**
+     * Print a message to stdout.
+     */
+    static void log(String message) {
+        System.out.println(Thread.currentThread().getName() + ": " + message);
+    }
+
+    /**
      * print a blank line to stderr that is prefixed with the prefix.
      */
 
@@ -156,7 +163,6 @@
     /**
      * print a message to stderr that is prefixed with the prefix.
      */
-
     public static void println(String prefix, String message)
     {
         System.err.println(prefix + ": "+message);
--- a/src/java.base/share/classes/sun/security/ssl/OutputRecord.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/OutputRecord.java	Mon Oct 31 10:39:14 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2016, 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
@@ -194,6 +194,11 @@
         // blank
     }
 
+    // apply to DTLS SSLEngine
+    void launchRetransmission() {
+        // blank
+    }
+
     @Override
     public synchronized void close() throws IOException {
         if (!isClosed) {
@@ -224,6 +229,9 @@
             sequenceNumber = authenticator.sequenceNumber();
         }
 
+        // The sequence number may be shared for different purpose.
+        boolean sharedSequenceNumber = false;
+
         // "flip" but skip over header again, add MAC & encrypt
         if (authenticator instanceof MAC) {
             MAC signer = (MAC)authenticator;
@@ -243,6 +251,11 @@
                 // reset the position and limit
                 destination.limit(destination.position());
                 destination.position(dstContent);
+
+                // The signer has used and increased the sequence number.
+                if (isDTLS) {
+                    sharedSequenceNumber = true;
+                }
             }
         }
 
@@ -261,6 +274,11 @@
 
             // Encrypt may pad, so again the limit may be changed.
             encCipher.encrypt(destination, dstLim);
+
+            // The cipher has used and increased the sequence number.
+            if (isDTLS && encCipher.isAEADMode()) {
+                sharedSequenceNumber = true;
+            }
         } else {
             destination.position(destination.limit());
         }
@@ -290,8 +308,10 @@
             destination.put(headerOffset + 11, (byte)(fragLen >> 8));
             destination.put(headerOffset + 12, (byte)fragLen);
 
-            // Increase the sequence number for next use.
-            authenticator.increaseSequenceNumber();
+            // Increase the sequence number for next use if it is not shared.
+            if (!sharedSequenceNumber) {
+                authenticator.increaseSequenceNumber();
+            }
         }
 
         // Update destination position to reflect the amount of data produced.
--- a/src/java.base/share/classes/sun/security/ssl/Plaintext.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/Plaintext.java	Mon Oct 31 10:39:14 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, 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
@@ -38,7 +38,7 @@
     byte            majorVersion;
     byte            minorVersion;
     int             recordEpoch;    // incremented on every cipher state change
-    long            recordSN;
+    long            recordSN;       // contains epcoh number (epoch | sequence)
     ByteBuffer      fragment;       // null if need to be reassembled
 
     HandshakeStatus handshakeStatus;    // null if not used or not handshaking
--- a/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java	Mon Oct 31 10:39:14 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2016, 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
@@ -994,7 +994,22 @@
 
         // plainText should never be null for TLS protocols
         HandshakeStatus hsStatus = null;
-        if (!isDTLS || plainText != null) {
+        if (plainText == Plaintext.PLAINTEXT_NULL) {
+            // Only happens for DTLS protocols.
+            //
+            // Received a retransmitted flight, and need to retransmit the
+            // previous delivered handshake flight messages.
+            if (enableRetransmissions) {
+                if (debug != null && Debug.isOn("verbose")) {
+                    Debug.log(
+                        "Retransmit the previous handshake flight messages.");
+                }
+
+                synchronized (this) {
+                    outputRecord.launchRetransmission();
+                }
+            }   // Otherwise, discard the retransmitted flight.
+        } else if (!isDTLS || plainText != null) {
             hsStatus = processInputRecord(plainText, appData, offset, length);
         }
 
@@ -1003,7 +1018,7 @@
         }
 
         if (plainText == null) {
-            plainText = new Plaintext();
+            plainText = Plaintext.PLAINTEXT_NULL;
         }
         plainText.handshakeStatus = hsStatus;
 
@@ -1378,7 +1393,8 @@
             // Acquire the buffered to-be-delivered records or retransmissions.
             //
             // May have buffered records, or need retransmission if handshaking.
-            if (!outputRecord.isEmpty() || (handshaker != null)) {
+            if (!outputRecord.isEmpty() ||
+                    (enableRetransmissions && handshaker != null)) {
                 ciphertext = outputRecord.acquireCiphertext(netData);
             }
 
@@ -1403,13 +1419,36 @@
 
         HandshakeStatus hsStatus = null;
         Ciphertext.RecordType recordType = ciphertext.recordType;
-        if ((handshaker != null) &&
-                (recordType.contentType == Record.ct_handshake) &&
-                (recordType.handshakeType == HandshakeMessage.ht_finished) &&
-                handshaker.isDone() && outputRecord.isEmpty()) {
+        if ((recordType.contentType == Record.ct_handshake) &&
+            (recordType.handshakeType == HandshakeMessage.ht_finished) &&
+            outputRecord.isEmpty()) {
 
-            hsStatus = finishHandshake();
-            connectionState = cs_DATA;
+            if (handshaker == null) {
+                hsStatus = HandshakeStatus.FINISHED;
+            } else if (handshaker.isDone()) {
+                hsStatus = finishHandshake();
+                connectionState = cs_DATA;
+
+                // Retransmit the last flight twice.
+                //
+                // The application data transactions may begin immediately
+                // after the last flight.  If the last flight get lost, the
+                // application data may be discarded accordingly.  As could
+                // be an issue for some applications.  This impact can be
+                // mitigated by sending the last fligth twice.
+                if (isDTLS && enableRetransmissions) {
+                    if (debug != null && Debug.isOn("verbose")) {
+                        Debug.log(
+                            "Retransmit the last flight messages.");
+                    }
+
+                    synchronized (this) {
+                        outputRecord.launchRetransmission();
+                    }
+
+                    hsStatus = HandshakeStatus.NEED_WRAP;
+                }
+            }
         }   // Otherwise, the followed call to getHSStatus() will help.
 
         /*
--- a/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java	Mon Oct 31 10:39:14 2016 -0700
@@ -558,73 +558,6 @@
             applicationProtocol = "";
         }
 
-        // cookie exchange
-        if (isDTLS) {
-             HelloCookieManager hcMgr = sslContext.getHelloCookieManager();
-             if ((mesg.cookie == null) || (mesg.cookie.length == 0) ||
-                    (!hcMgr.isValid(mesg))) {
-
-                //
-                // Perform cookie exchange for DTLS handshaking if no cookie
-                // or the cookie is invalid in the ClientHello message.
-                //
-                HelloVerifyRequest m0 = new HelloVerifyRequest(hcMgr, mesg);
-
-                if (debug != null && Debug.isOn("handshake")) {
-                    m0.print(System.out);
-                }
-
-                m0.write(output);
-                handshakeState.update(m0, resumingSession);
-                output.flush();
-
-                return;
-            }
-        }
-
-        /*
-         * FIRST, construct the ServerHello using the options and priorities
-         * from the ClientHello.  Update the (pending) cipher spec as we do
-         * so, and save the client's version to protect against rollback
-         * attacks.
-         *
-         * There are a bunch of minor tasks here, and one major one: deciding
-         * if the short or the full handshake sequence will be used.
-         */
-        ServerHello m1 = new ServerHello();
-
-        clientRequestedVersion = mesg.protocolVersion;
-
-        // select a proper protocol version.
-        ProtocolVersion selectedVersion =
-               selectProtocolVersion(clientRequestedVersion);
-        if (selectedVersion == null ||
-                selectedVersion.v == ProtocolVersion.SSL20Hello.v) {
-            fatalSE(Alerts.alert_handshake_failure,
-                "Client requested protocol " + clientRequestedVersion +
-                " not enabled or not supported");
-        }
-
-        handshakeHash.protocolDetermined(selectedVersion);
-        setVersion(selectedVersion);
-
-        m1.protocolVersion = protocolVersion;
-
-        //
-        // random ... save client and server values for later use
-        // in computing the master secret (from pre-master secret)
-        // and thence the other crypto keys.
-        //
-        // NOTE:  this use of three inputs to generating _each_ set
-        // of ciphers slows things down, but it does increase the
-        // security since each connection in the session can hold
-        // its own authenticated (and strong) keys.  One could make
-        // creation of a session a rare thing...
-        //
-        clnt_random = mesg.clnt_random;
-        svr_random = new RandomCookie(sslContext.getSecureRandom());
-        m1.svr_random = svr_random;
-
         session = null; // forget about the current session
         //
         // Here we go down either of two paths:  (a) the fast one, where
@@ -732,6 +665,73 @@
             }
         }   // else client did not try to resume
 
+        // cookie exchange
+        if (isDTLS && !resumingSession) {
+             HelloCookieManager hcMgr = sslContext.getHelloCookieManager();
+             if ((mesg.cookie == null) || (mesg.cookie.length == 0) ||
+                    (!hcMgr.isValid(mesg))) {
+
+                //
+                // Perform cookie exchange for DTLS handshaking if no cookie
+                // or the cookie is invalid in the ClientHello message.
+                //
+                HelloVerifyRequest m0 = new HelloVerifyRequest(hcMgr, mesg);
+
+                if (debug != null && Debug.isOn("handshake")) {
+                    m0.print(System.out);
+                }
+
+                m0.write(output);
+                handshakeState.update(m0, resumingSession);
+                output.flush();
+
+                return;
+            }
+        }
+
+        /*
+         * FIRST, construct the ServerHello using the options and priorities
+         * from the ClientHello.  Update the (pending) cipher spec as we do
+         * so, and save the client's version to protect against rollback
+         * attacks.
+         *
+         * There are a bunch of minor tasks here, and one major one: deciding
+         * if the short or the full handshake sequence will be used.
+         */
+        ServerHello m1 = new ServerHello();
+
+        clientRequestedVersion = mesg.protocolVersion;
+
+        // select a proper protocol version.
+        ProtocolVersion selectedVersion =
+               selectProtocolVersion(clientRequestedVersion);
+        if (selectedVersion == null ||
+                selectedVersion.v == ProtocolVersion.SSL20Hello.v) {
+            fatalSE(Alerts.alert_handshake_failure,
+                "Client requested protocol " + clientRequestedVersion +
+                " not enabled or not supported");
+        }
+
+        handshakeHash.protocolDetermined(selectedVersion);
+        setVersion(selectedVersion);
+
+        m1.protocolVersion = protocolVersion;
+
+        //
+        // random ... save client and server values for later use
+        // in computing the master secret (from pre-master secret)
+        // and thence the other crypto keys.
+        //
+        // NOTE:  this use of three inputs to generating _each_ set
+        // of ciphers slows things down, but it does increase the
+        // security since each connection in the session can hold
+        // its own authenticated (and strong) keys.  One could make
+        // creation of a session a rare thing...
+        //
+        clnt_random = mesg.clnt_random;
+        svr_random = new RandomCookie(sslContext.getSecureRandom());
+        m1.svr_random = svr_random;
+
         //
         // If client hasn't specified a session we can resume, start a
         // new one and choose its cipher suite and compression options.
--- a/src/java.base/share/conf/security/java.security	Mon Oct 31 14:50:09 2016 +0300
+++ b/src/java.base/share/conf/security/java.security	Mon Oct 31 10:39:14 2016 -0700
@@ -645,6 +645,9 @@
 # before larger keysize constraints of the same algorithm.  For example:
 # "RSA keySize < 1024 & jdkCA, RSA keySize < 2048".
 #
+# Note: The algorithm restrictions do not apply to trust anchors or
+# self-signed certificates.
+#
 # Note: This property is currently used by Oracle's PKIX implementation. It
 # is not guaranteed to be examined and used by other implementations.
 #
@@ -714,6 +717,9 @@
 # See the specification of "jdk.certpath.disabledAlgorithms" for the
 # syntax of the disabled algorithm string.
 #
+# Note: The algorithm restrictions do not apply to trust anchors or
+# self-signed certificates.
+#
 # Note: This property is currently used by Oracle's JSSE implementation.
 # It is not guaranteed to be examined and used by other implementations.
 #
--- a/src/java.base/share/lib/security/default.policy	Mon Oct 31 14:50:09 2016 +0300
+++ b/src/java.base/share/lib/security/default.policy	Mon Oct 31 10:39:14 2016 -0700
@@ -32,8 +32,22 @@
     permission javax.smartcardio.CardPermission "*", "*";
     permission java.lang.RuntimePermission "loadLibrary.j2pcsc";
     permission java.lang.RuntimePermission
-                   "accessClassInPackage.sun.security.*";
-    permission java.util.PropertyPermission "*", "read";
+                   "accessClassInPackage.sun.security.jca";
+    permission java.lang.RuntimePermission
+                   "accessClassInPackage.sun.security.util";
+    permission java.util.PropertyPermission
+                   "javax.smartcardio.TerminalFactory.DefaultType", "read";
+    permission java.util.PropertyPermission "os.name", "read";
+    permission java.util.PropertyPermission "os.arch", "read";
+    permission java.util.PropertyPermission "sun.arch.data.model", "read";
+    permission java.util.PropertyPermission
+                   "sun.security.smartcardio.library", "read";
+    permission java.util.PropertyPermission
+                   "sun.security.smartcardio.t0GetResponse", "read";
+    permission java.util.PropertyPermission
+                   "sun.security.smartcardio.t1GetResponse", "read";
+    permission java.util.PropertyPermission
+                   "sun.security.smartcardio.t1StripLe", "read";
     // needed for looking up native PC/SC library
     permission java.io.FilePermission "<<ALL FILES>>","read";
     permission java.security.SecurityPermission "putProviderProperty.SunPCSC";
--- a/src/java.base/share/native/launcher/defines.h	Mon Oct 31 14:50:09 2016 +0300
+++ b/src/java.base/share/native/launcher/defines.h	Mon Oct 31 10:39:14 2016 -0700
@@ -51,16 +51,6 @@
 static char* const_progname = NULL;
 #endif
 static const char* const_jargs[] = JAVA_ARGS;
-/*
- * ApplicationHome is prepended to each of these entries; the resulting
- * strings are concatenated (separated by PATH_SEPARATOR) and used as the
- * value of -cp option to the launcher.
- */
-#ifndef APP_CLASSPATH
-static const char* const_appclasspath[] = { NULL };
-#else
-static const char* const_appclasspath[] = APP_CLASSPATH;
-#endif /* APP_CLASSPATH */
 #else  /* !JAVA_ARGS */
 #define HAS_JAVA_ARGS JNI_FALSE
 static const char* const_progname = "java";
--- a/src/java.base/share/native/launcher/main.c	Mon Oct 31 14:50:09 2016 +0300
+++ b/src/java.base/share/native/launcher/main.c	Mon Oct 31 10:39:14 2016 -0700
@@ -83,7 +83,7 @@
 int WINAPI
 WinMain(HINSTANCE inst, HINSTANCE previnst, LPSTR cmdline, int cmdshow)
 {
-    int margc, appclassc;
+    int margc;
     char** margv;
     const jboolean const_javaw = JNI_TRUE;
 
@@ -93,7 +93,7 @@
 int
 main(int argc, char **argv)
 {
-    int margc, appclassc;
+    int margc;
     char** margv;
     const jboolean const_javaw = JNI_FALSE;
 #endif /* JAVAW */
@@ -148,14 +148,9 @@
         margv = args->elements;
     }
 #endif /* WIN32 */
-    if (const_appclasspath[0] == NULL) {
-        appclassc = 0;
-    } else {
-        appclassc = sizeof(const_appclasspath) / sizeof(char *);
-    }
     return JLI_Launch(margc, margv,
                    sizeof(const_jargs) / sizeof(char *), const_jargs,
-                   appclassc, const_appclasspath,
+                   0, NULL,
                    VERSION_STRING,
                    DOT_VERSION,
                    (const_progname != NULL) ? const_progname : *margv,
--- a/src/java.base/share/native/libjli/java.c	Mon Oct 31 14:50:09 2016 +0300
+++ b/src/java.base/share/native/libjli/java.c	Mon Oct 31 10:39:14 2016 -0700
@@ -1664,19 +1664,21 @@
     AddOption(apphome, NULL);
 
     /* How big is the application's classpath? */
-    size = 40;                                 /* 40: "-Djava.class.path=" */
-    for (i = 0; i < cpathc; i++) {
-        size += (int)JLI_StrLen(home) + (int)JLI_StrLen(cpathv[i]) + 1; /* 1: separator */
+    if (cpathc > 0) {
+        size = 40;                                 /* 40: "-Djava.class.path=" */
+        for (i = 0; i < cpathc; i++) {
+            size += (int)JLI_StrLen(home) + (int)JLI_StrLen(cpathv[i]) + 1; /* 1: separator */
+        }
+        appcp = (char *)JLI_MemAlloc(size + 1);
+        JLI_StrCpy(appcp, "-Djava.class.path=");
+        for (i = 0; i < cpathc; i++) {
+            JLI_StrCat(appcp, home);                        /* c:\program files\myapp */
+            JLI_StrCat(appcp, cpathv[i]);           /* \lib\myapp.jar         */
+            JLI_StrCat(appcp, separator);           /* ;                      */
+        }
+        appcp[JLI_StrLen(appcp)-1] = '\0';  /* remove trailing path separator */
+        AddOption(appcp, NULL);
     }
-    appcp = (char *)JLI_MemAlloc(size + 1);
-    JLI_StrCpy(appcp, "-Djava.class.path=");
-    for (i = 0; i < cpathc; i++) {
-        JLI_StrCat(appcp, home);                        /* c:\program files\myapp */
-        JLI_StrCat(appcp, cpathv[i]);           /* \lib\myapp.jar         */
-        JLI_StrCat(appcp, separator);           /* ;                      */
-    }
-    appcp[JLI_StrLen(appcp)-1] = '\0';  /* remove trailing path separator */
-    AddOption(appcp, NULL);
     return JNI_TRUE;
 }
 
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java	Mon Oct 31 10:39:14 2016 -0700
@@ -42,8 +42,6 @@
 import java.util.*;
 import java.util.stream.Collectors;
 
-import jdk.internal.module.ConfigurableModuleFinder;
-import jdk.internal.module.ConfigurableModuleFinder.Phase;
 import jdk.tools.jlink.internal.TaskHelper.BadArgs;
 import static jdk.tools.jlink.internal.TaskHelper.JLINK_BUNDLE;
 import jdk.tools.jlink.internal.Jlink.JlinkConfiguration;
@@ -54,6 +52,7 @@
 import jdk.tools.jlink.plugin.PluginException;
 import jdk.tools.jlink.builder.DefaultImageBuilder;
 import jdk.tools.jlink.plugin.Plugin;
+import jdk.internal.misc.SharedSecrets;
 
 /**
  * Implementation for the jlink tool.
@@ -252,8 +251,9 @@
             throw new Exception("Empty module paths");
         }
 
-        ModuleFinder finder
-                = newModuleFinder(config.getModulepaths(), config.getLimitmods(), config.getModules());
+        ModuleFinder finder = newModuleFinder(config.getModulepaths(),
+                                              config.getLimitmods(),
+                                              config.getModules());
 
         // First create the image provider
         ImageProvider imageProvider
@@ -328,24 +328,41 @@
         return addMods;
     }
 
-    public static ModuleFinder newModuleFinder(List<Path> paths,
-                                               Set<String> limitMods,
-                                               Set<String> addMods)
-    {
-        ModuleFinder finder = ModuleFinder.of(paths.toArray(new Path[0]));
+    /**
+     * Returns a module finder to find the observable modules specified in
+     * the --module-path and --limit-modules options
+     */
+    private ModuleFinder modulePathFinder() {
+        Path[] entries = options.modulePath.toArray(new Path[0]);
+        ModuleFinder finder = SharedSecrets.getJavaLangModuleAccess()
+            .newModulePath(Runtime.version(), true, entries);
 
-        // jmods are located at link-time
-        if (finder instanceof ConfigurableModuleFinder) {
-            ((ConfigurableModuleFinder) finder).configurePhase(Phase.LINK_TIME);
-        }
-
-        // if limitmods is specified then limit the universe
-        if (!limitMods.isEmpty()) {
-            finder = limitFinder(finder, limitMods, addMods);
+        if (!options.limitMods.isEmpty()) {
+            finder = limitFinder(finder, options.limitMods, Collections.emptySet());
         }
         return finder;
     }
 
+    /**
+     * Returns a module finder of the given module path that limits
+     * the observable modules to those in the transitive closure of
+     * the modules specified in {@code limitMods} plus other modules
+     * specified in the {@code roots} set.
+     */
+    public static ModuleFinder newModuleFinder(List<Path> paths,
+                                               Set<String> limitMods,
+                                               Set<String> roots)
+    {
+        Path[] entries = paths.toArray(new Path[0]);
+        ModuleFinder finder = SharedSecrets.getJavaLangModuleAccess()
+            .newModulePath(Runtime.version(), true, entries);
+
+        // if limitmods is specified then limit the universe
+        if (!limitMods.isEmpty()) {
+            finder = limitFinder(finder, limitMods, roots);
+        }
+        return finder;
+    }
 
     private static Path toPathLocation(ResolvedModule m) {
         Optional<URI> ouri = m.reference().location();
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/TaskHelper.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/TaskHelper.java	Mon Oct 31 10:39:14 2016 -0700
@@ -47,8 +47,6 @@
 import java.util.ResourceBundle;
 import java.util.Set;
 
-import jdk.internal.module.ConfigurableModuleFinder;
-import jdk.internal.module.ConfigurableModuleFinder.Phase;
 import jdk.tools.jlink.internal.plugins.ExcludeFilesPlugin;
 import jdk.tools.jlink.internal.plugins.ExcludeJmodSectionPlugin;
 import jdk.tools.jlink.plugin.Plugin;
@@ -60,6 +58,7 @@
 import jdk.tools.jlink.internal.plugins.PluginsResourceBundle;
 import jdk.tools.jlink.internal.plugins.DefaultCompressPlugin;
 import jdk.tools.jlink.internal.plugins.StripDebugPlugin;
+import jdk.internal.misc.SharedSecrets;
 
 /**
  *
@@ -726,14 +725,10 @@
     }
 
     static Layer createPluginsLayer(List<Path> paths) {
-        Path[] arr = new Path[paths.size()];
-        paths.toArray(arr);
-        ModuleFinder finder = ModuleFinder.of(arr);
 
-        // jmods are located at link-time
-        if (finder instanceof ConfigurableModuleFinder) {
-            ((ConfigurableModuleFinder) finder).configurePhase(Phase.LINK_TIME);
-        }
+        Path[] dirs = paths.toArray(new Path[0]);
+        ModuleFinder finder = SharedSecrets.getJavaLangModuleAccess()
+            .newModulePath(Runtime.version(), true, dirs);
 
         Configuration bootConfiguration = Layer.boot().configuration();
         try {
--- a/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java	Mon Oct 31 10:39:14 2016 -0700
@@ -98,8 +98,6 @@
 import jdk.internal.joptsimple.ValueConverter;
 import jdk.internal.misc.JavaLangModuleAccess;
 import jdk.internal.misc.SharedSecrets;
-import jdk.internal.module.ConfigurableModuleFinder;
-import jdk.internal.module.ConfigurableModuleFinder.Phase;
 import jdk.internal.module.ModuleHashes;
 import jdk.internal.module.ModuleInfoExtender;
 import jdk.tools.jlink.internal.Utils;
@@ -1298,9 +1296,7 @@
                 options.manPages = opts.valuesOf(manPages);
             if (opts.has(modulePath)) {
                 Path[] dirs = opts.valuesOf(modulePath).toArray(new Path[0]);
-                options.moduleFinder = ModuleFinder.of(dirs);
-                if (options.moduleFinder instanceof ConfigurableModuleFinder)
-                    ((ConfigurableModuleFinder)options.moduleFinder).configurePhase(Phase.LINK_TIME);
+                options.moduleFinder = JLMA.newModulePath(Runtime.version(), true, dirs);
             }
             if (opts.has(moduleVersion))
                 options.moduleVersion = opts.valueOf(moduleVersion);
--- a/src/jdk.jsobject/share/classes/netscape/javascript/JSObject.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/src/jdk.jsobject/share/classes/netscape/javascript/JSObject.java	Mon Oct 31 10:39:14 2016 -0700
@@ -156,6 +156,7 @@
      */
 
     @Deprecated(since = "9")
+    @SuppressWarnings("exports")
     public static JSObject getWindow(Applet applet) throws JSException {
         return ProviderLoader.callGetWindow(applet);
     }
--- a/test/ProblemList.txt	Mon Oct 31 14:50:09 2016 +0300
+++ b/test/ProblemList.txt	Mon Oct 31 10:39:14 2016 -0700
@@ -184,10 +184,6 @@
 java/nio/file/WatchService/MayFlies.java                        7158947 solaris-all Solaris 11
 java/nio/file/WatchService/LotsOfEvents.java                    7158947 solaris-all Solaris 11
 
-java/nio/charset/coders/BashStreams.java                        8149712 generic-all
-
-java/nio/file/WatchService/DeleteInterference.java              8156511 linux-all
-
 ############################################################################
 
 # jdk_rmi
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/io/FilePermission/FilePermissionCollectionMerge.java	Mon Oct 31 10:39:14 2016 -0700
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2016, 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 8168127
+ * @summary FilePermissionCollection merges incorrectly
+ * @modules java.base/sun.security.util
+ * @library /test/lib
+ */
+
+import sun.security.util.FilePermCompat;
+import java.io.FilePermission;
+import java.security.Permissions;
+import jdk.test.lib.Asserts;
+
+public class FilePermissionCollectionMerge {
+
+    public static void main(String[] args) throws Exception {
+        test("x");
+        test("x/*");
+        test("x/-");
+        test("*");
+        test("-");
+        test("/x");
+        test("/x/*");
+        test("/x/-");
+    }
+
+    static void test(String arg) {
+
+        FilePermission fp1 = new FilePermission(arg, "read");
+        FilePermission fp2 = (FilePermission)
+                FilePermCompat.newPermUsingAltPath(fp1);
+        FilePermission fp3 = (FilePermission)
+                FilePermCompat.newPermPlusAltPath(fp1);
+
+        // All 3 are different
+        Asserts.assertNE(fp1, fp2);
+        Asserts.assertNE(fp1.hashCode(), fp2.hashCode());
+
+        Asserts.assertNE(fp1, fp3);
+        Asserts.assertNE(fp1.hashCode(), fp3.hashCode());
+
+        Asserts.assertNE(fp2, fp3);
+        Asserts.assertNE(fp2.hashCode(), fp3.hashCode());
+
+        // The plus one implies the other 2
+        Asserts.assertTrue(fp3.implies(fp1));
+        Asserts.assertTrue(fp3.implies(fp2));
+
+        // The using one different from original
+        Asserts.assertFalse(fp2.implies(fp1));
+        Asserts.assertFalse(fp1.implies(fp2));
+
+        // FilePermssionCollection::implies always works
+        testMerge(fp1);
+        testMerge(fp2);
+        testMerge(fp3);
+        testMerge(fp1, fp2);
+        testMerge(fp1, fp3);
+        testMerge(fp2, fp1);
+        testMerge(fp2, fp3);
+        testMerge(fp3, fp1);
+        testMerge(fp3, fp2);
+        testMerge(fp1, fp2, fp3);
+        testMerge(fp2, fp3, fp1);
+        testMerge(fp3, fp1, fp2);
+    }
+
+    // Add all into a collection, and check if it implies the last one.
+    static void testMerge(FilePermission... fps) {
+        java.security.Permissions perms = new Permissions();
+        FilePermission last = null;
+        for (FilePermission fp : fps) {
+            perms.add(fp);
+            last = fp;
+        }
+        Asserts.assertTrue(perms.implies(last));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/io/FilePermission/Invalid.java	Mon Oct 31 10:39:14 2016 -0700
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2016, 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 8167646
+ * @summary Better invalid FilePermission
+ * @library /test/lib
+ */
+
+import jdk.test.lib.Asserts;
+
+import java.io.FilePermission;
+
+public class Invalid {
+
+    public static void main(String args[]) throws Exception {
+
+        // Normal
+        FilePermission fp = new FilePermission("a", "read");
+
+        // Invalid
+        FilePermission fp1 = new FilePermission("a\000", "read");
+        FilePermission fp2 = new FilePermission("a\000", "read");
+        FilePermission fp3 = new FilePermission("b\000", "read");
+
+        // Invalid equals to itself
+        Asserts.assertEQ(fp1, fp1);
+
+        // and not equals to anything else, including other invalid ones
+        Asserts.assertNE(fp, fp1);
+        Asserts.assertNE(fp1, fp);
+        Asserts.assertNE(fp1, fp2);
+        Asserts.assertNE(fp1, fp3);
+
+        // Invalid implies itself
+        Asserts.assertTrue(fp1.implies(fp1));
+
+        // and not implies or implied by anything else, including other
+        // invalid ones
+        Asserts.assertFalse(fp.implies(fp1));
+        Asserts.assertFalse(fp1.implies(fp));
+        Asserts.assertFalse(fp1.implies(fp2));
+        Asserts.assertFalse(fp1.implies(fp3));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/lang/System/LoggerFinder/internal/SystemLoggerInPlatformLoader/SystemLoggerInPlatformLoader.java	Mon Oct 31 10:39:14 2016 -0700
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * @test 8163162
+ * @summary Checks that LazyLoggers are returned for System.Logger instances
+ *          created by modules in the platform class loader.
+ * @build systempkg.log.SystemLoggerAccessor SystemLoggerInPlatformLoader
+ * @run main/othervm SystemLoggerInPlatformLoader
+ * @author danielfuchs
+ */
+public class SystemLoggerInPlatformLoader {
+
+    static final class PlatformClassLoaderChild extends ClassLoader {
+        private PlatformClassLoaderChild() {
+            super(ClassLoader.getPlatformClassLoader());
+        }
+        public Class<?> definePlatformClass(String name) throws IOException {
+            String testClasses = System.getProperty("test.classes", "./build/classes");
+            String fname = name.replace('.', '/').concat(".class");
+            try (InputStream is = new FileInputStream(new File(testClasses, fname))) {
+                byte[] b = is.readAllBytes();
+                ClassLoader parent = getParent();
+                try {
+                    Method m = ClassLoader.class.getDeclaredMethod("defineClass",
+                        String.class, byte[].class, int.class, int.class);
+                    m.setAccessible(true);
+                    return (Class<?>)m.invoke(parent, name, b, 0, b.length);
+                } catch (NoSuchMethodException
+                        | IllegalAccessException
+                        | InvocationTargetException ex) {
+                    throw new IOException(ex);
+                }
+            }
+        }
+        static final PlatformClassLoaderChild INSTANCE = new PlatformClassLoaderChild();
+        static Class<?> loadLoggerAccessor() throws IOException {
+            return INSTANCE.definePlatformClass("systempkg.log.SystemLoggerAccessor");
+        }
+    }
+
+    static final Class<?> LOGGER_ACCESSOR_CLASS;
+    static {
+        try {
+            LOGGER_ACCESSOR_CLASS = PlatformClassLoaderChild.loadLoggerAccessor();
+            ClassLoader platformCL = ClassLoader.getPlatformClassLoader();
+            if (LOGGER_ACCESSOR_CLASS.getClassLoader() != platformCL) {
+                throw new ExceptionInInitializerError(
+                    "Could not load accessor class in platform class loader: "
+                     + LOGGER_ACCESSOR_CLASS.getClassLoader());
+            }
+        } catch (IOException ex) {
+            throw new ExceptionInInitializerError(ex);
+        }
+    }
+
+    // Returns a system logger created on behalf of a class loaded by the
+    // Platform ClassLoader
+    static System.Logger getSystemLogger(String name) {
+        try {
+            return (System.Logger)LOGGER_ACCESSOR_CLASS.getMethod(
+                    "getSystemLogger", String.class).invoke(null, name);
+        } catch (NoSuchMethodException
+                | IllegalAccessException
+                | InvocationTargetException ex) {
+            throw new RuntimeException("Failed to invoke LoggerAccessor.getJULLogger", ex);
+        }
+    }
+
+    public static void main(String[] args) {
+        System.Logger splogger = getSystemLogger("bar"); // for a platform class
+        System.Logger slogger = System.getLogger("bar"); // for an application class
+        if (slogger == splogger) {
+            throw new RuntimeException("Same loggers");
+        }
+        Class sploggerType = splogger.getClass();
+        System.out.println("splogger: " + sploggerType);
+        if (!sploggerType.getSimpleName().equals("JdkLazyLogger")) {
+            throw new RuntimeException(sploggerType.getSimpleName()
+                      + ": unexpected class for splogger"
+                      + " (expected a lazy logger for a platform class)");
+        }
+        Class sloggerType = slogger.getClass();
+        System.out.println("slogger: " + sloggerType);
+        if (sloggerType.equals(sploggerType)) {
+            throw new RuntimeException(sloggerType
+                      + ": unexpected class for slogger"
+                      + " (a lazy logger was not expected"
+                      + " for a non platform class)");
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/lang/System/LoggerFinder/internal/SystemLoggerInPlatformLoader/systempkg/log/SystemLoggerAccessor.java	Mon Oct 31 10:39:14 2016 -0700
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package systempkg.log;
+
+
+/**
+ * Utility class that returns loggers created for classes in the
+ * systempkg.log package;
+ * This class should be loaded in the {@linkplain
+ * ClassLoader#getPlatformClassLoader() platform class loader}
+ * for the purpose of the test.
+ * @author danielfuchs
+ */
+public class SystemLoggerAccessor {
+    public static System.Logger getSystemLogger(String name) {
+        return System.getLogger(name);
+    }
+}
--- a/test/java/lang/module/AutomaticModulesTest.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/test/java/lang/module/AutomaticModulesTest.java	Mon Oct 31 10:39:14 2016 -0700
@@ -158,40 +158,85 @@
      */
     public void testPackages() throws IOException {
         Path dir = Files.createTempDirectory(USER_DIR, "mods");
-        createDummyJarFile(dir.resolve("m1.jar"),
+        createDummyJarFile(dir.resolve("m.jar"),
                            "p/C1.class", "p/C2.class", "q/C1.class");
 
         ModuleFinder finder = ModuleFinder.of(dir);
+        Optional<ModuleReference> mref = finder.find("m");
+        assertTrue(mref.isPresent(), "m not found");
 
-        Configuration parent = Layer.boot().configuration();
-        Configuration cf = resolve(parent, finder, "m1");
+        ModuleDescriptor descriptor = mref.get().descriptor();
 
-        ModuleDescriptor m1 = findDescriptor(cf, "m1");
+        assertTrue(descriptor.packages().size() == 2);
+        assertTrue(descriptor.packages().contains("p"));
+        assertTrue(descriptor.packages().contains("q"));
 
-        Set<String> exports
-            = m1.exports().stream().map(Exports::source).collect(Collectors.toSet());
-
+        Set<String> exports = descriptor.exports().stream()
+                .map(Exports::source)
+                .collect(Collectors.toSet());
         assertTrue(exports.size() == 2);
         assertTrue(exports.contains("p"));
         assertTrue(exports.contains("q"));
-        assertTrue(m1.conceals().isEmpty());
     }
 
+    /**
+     * Test class files in JAR file where the entry does not correspond to a
+     * legal package name.
+     */
+    public void testBadPackage() throws IOException {
+        Path dir = Files.createTempDirectory(USER_DIR, "mods");
+        createDummyJarFile(dir.resolve("m.jar"), "p/C1.class", "p-/C2.class");
+
+        ModuleFinder finder = ModuleFinder.of(dir);
+        Optional<ModuleReference> mref = finder.find("m");
+        assertTrue(mref.isPresent(), "m not found");
+
+        ModuleDescriptor descriptor = mref.get().descriptor();
+
+        assertTrue(descriptor.packages().size() == 1);
+        assertTrue(descriptor.packages().contains("p"));
+
+        Set<String> exports = descriptor.exports().stream()
+                .map(Exports::source)
+                .collect(Collectors.toSet());
+        assertTrue(exports.size() == 1);
+        assertTrue(exports.contains("p"));
+    }
 
     /**
-     * Test class file in JAR file where the entry does not correspond to a
-     * legal package name.
+     * Test non-class resources in a JAR file.
+     */
+    public void testNonClassResources() throws IOException {
+        Path dir = Files.createTempDirectory(USER_DIR, "mods");
+        createDummyJarFile(dir.resolve("m.jar"),
+                "LICENSE",
+                "README",
+                "WEB-INF/tags",
+                "p/Type.class",
+                "p/resources/m.properties");
+
+        ModuleFinder finder = ModuleFinder.of(dir);
+        Optional<ModuleReference> mref = finder.find("m");
+        assertTrue(mref.isPresent(), "m not found");
+
+        ModuleDescriptor descriptor = mref.get().descriptor();
+
+        assertTrue(descriptor.packages().size() == 2);
+        assertTrue(descriptor.packages().contains("p"));
+        assertTrue(descriptor.packages().contains("p.resources"));
+    }
+
+    /**
+     * Test .class file in unnamed package (top-level directory)
      */
     @Test(expectedExceptions = FindException.class)
-    public void testBadPackage() throws IOException {
+    public void testClassInUnnamedPackage() throws IOException {
         Path dir = Files.createTempDirectory(USER_DIR, "mods");
-        createDummyJarFile(dir.resolve("m1.jar"), "p-/T.class");
-
-        // should throw FindException
-        ModuleFinder.of(dir).findAll();
+        createDummyJarFile(dir.resolve("m.jar"), "Mojo.class");
+        ModuleFinder finder = ModuleFinder.of(dir);
+        finder.findAll();
     }
 
-
     /**
      * Test JAR file with META-INF/services configuration file
      */
@@ -204,12 +249,12 @@
         Files.createDirectories(services);
         Files.write(services.resolve(service), Set.of(provider));
         Path dir = Files.createTempDirectory(USER_DIR, "mods");
-        JarUtils.createJarFile(dir.resolve("m1.jar"), tmpdir);
+        JarUtils.createJarFile(dir.resolve("m.jar"), tmpdir);
 
         ModuleFinder finder = ModuleFinder.of(dir);
 
-        Optional<ModuleReference> mref = finder.find("m1");
-        assertTrue(mref.isPresent(), "m1 not found");
+        Optional<ModuleReference> mref = finder.find("m");
+        assertTrue(mref.isPresent(), "m not found");
 
         ModuleDescriptor descriptor = mref.get().descriptor();
         assertTrue(descriptor.provides().size() == 1);
@@ -220,17 +265,12 @@
     }
 
 
-    // META-INF/services configuration file/entries that are not legal
-    @DataProvider(name = "badproviders")
-    public Object[][] createProviders() {
+    // META-INF/services files that don't map to legal service names
+    @DataProvider(name = "badservices")
+    public Object[][] createBadServices() {
         return new Object[][] {
 
                 // service type         provider type
-
-                { "p.S",                "-" },
-                { "p.S",                ".S1" },
-                { "p.S",                "S1." },
-
                 { "-",                  "p.S1" },
                 { ".S",                 "p.S1" },
         };
@@ -240,8 +280,8 @@
      * Test JAR file with META-INF/services configuration file with bad
      * values or names.
      */
-    @Test(dataProvider = "badproviders", expectedExceptions = FindException.class)
-    public void testBadServicesConfiguration(String service, String provider)
+    @Test(dataProvider = "badservices")
+    public void testBadServicesNames(String service, String provider)
         throws IOException
     {
         Path tmpdir = Files.createTempDirectory(USER_DIR, "tmp");
@@ -249,7 +289,41 @@
         Files.createDirectories(services);
         Files.write(services.resolve(service), Set.of(provider));
         Path dir = Files.createTempDirectory(USER_DIR, "mods");
-        JarUtils.createJarFile(dir.resolve("m1.jar"), tmpdir);
+        JarUtils.createJarFile(dir.resolve("m.jar"), tmpdir);
+
+        Optional<ModuleReference> omref = ModuleFinder.of(dir).find("m");
+        assertTrue(omref.isPresent());
+        ModuleDescriptor descriptor = omref.get().descriptor();
+        assertTrue(descriptor.provides().isEmpty());
+    }
+
+
+    // META-INF/services configuration file entries that are not legal
+    @DataProvider(name = "badproviders")
+    public Object[][] createBadProviders() {
+        return new Object[][] {
+
+                // service type         provider type
+                { "p.S",                "-" },
+                { "p.S",                ".S1" },
+                { "p.S",                "S1." },
+        };
+    }
+
+    /**
+     * Test JAR file with META-INF/services configuration file with bad
+     * values or names.
+     */
+    @Test(dataProvider = "badproviders", expectedExceptions = FindException.class)
+    public void testBadProvideNames(String service, String provider)
+        throws IOException
+    {
+        Path tmpdir = Files.createTempDirectory(USER_DIR, "tmp");
+        Path services = tmpdir.resolve("META-INF").resolve("services");
+        Files.createDirectories(services);
+        Files.write(services.resolve(service), Set.of(provider));
+        Path dir = Files.createTempDirectory(USER_DIR, "mods");
+        JarUtils.createJarFile(dir.resolve("m.jar"), tmpdir);
 
         // should throw FindException
         ModuleFinder.of(dir).findAll();
--- a/test/java/lang/module/ModuleFinderTest.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/test/java/lang/module/ModuleFinderTest.java	Mon Oct 31 10:39:14 2016 -0700
@@ -29,6 +29,7 @@
  * @summary Basic tests for java.lang.module.ModuleFinder
  */
 
+import java.io.File;
 import java.io.OutputStream;
 import java.lang.module.FindException;
 import java.lang.module.InvalidModuleDescriptorException;
@@ -38,6 +39,7 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.Optional;
 import java.util.Set;
 import java.util.jar.JarEntry;
 import java.util.jar.JarOutputStream;
@@ -317,6 +319,111 @@
 
 
     /**
+     * Test ModuleFinder with a JAR file containing a mix of class and
+     * non-class resources.
+     */
+    public void testOfOneJarFileWithResources() throws Exception {
+        Path dir = Files.createTempDirectory(USER_DIR, "mods");
+        Path jar = createModularJar(dir.resolve("m.jar"), "m",
+                "LICENSE",
+                "README",
+                "WEB-INF/tags",
+                "p/Type.class",
+                "p/resources/m.properties",
+                "q-/Type.class",                // not a legal package name
+                "q-/resources/m/properties");
+
+        ModuleFinder finder = ModuleFinder.of(jar);
+        Optional<ModuleReference> mref = finder.find("m");
+        assertTrue(mref.isPresent(), "m not found");
+
+        ModuleDescriptor descriptor = mref.get().descriptor();
+
+        assertTrue(descriptor.packages().size() == 2);
+        assertTrue(descriptor.packages().contains("p"));
+        assertTrue(descriptor.packages().contains("p.resources"));
+    }
+
+
+    /**
+     * Test ModuleFinder with an exploded module containing a mix of class
+     * and non-class resources
+     */
+    public void testOfOneExplodedModuleWithResources() throws Exception {
+        Path dir = Files.createTempDirectory(USER_DIR, "mods");
+        Path m_dir = createExplodedModule(dir.resolve("m"), "m",
+                "LICENSE",
+                "README",
+                "WEB-INF/tags",
+                "p/Type.class",
+                "p/resources/m.properties",
+                "q-/Type.class",                 // not a legal package name
+                "q-/resources/m/properties");
+
+        ModuleFinder finder = ModuleFinder.of(m_dir);
+        Optional<ModuleReference> mref = finder.find("m");
+        assertTrue(mref.isPresent(), "m not found");
+
+        ModuleDescriptor descriptor = mref.get().descriptor();
+
+        assertTrue(descriptor.packages().size() == 2);
+        assertTrue(descriptor.packages().contains("p"));
+        assertTrue(descriptor.packages().contains("p.resources"));
+    }
+
+
+    /**
+     * Test ModuleModule with a JAR file containing a .class file in the top
+     * level directory.
+     */
+    public void testOfOneJarFileWithTopLevelClass() throws Exception {
+        Path dir = Files.createTempDirectory(USER_DIR, "mods");
+        Path jar = createModularJar(dir.resolve("m.jar"), "m", "Mojo.class");
+
+        ModuleFinder finder = ModuleFinder.of(jar);
+        try {
+            finder.find("m");
+            assertTrue(false);
+        } catch (FindException e) {
+            assertTrue(e.getCause() instanceof InvalidModuleDescriptorException);
+        }
+
+        finder = ModuleFinder.of(jar);
+        try {
+            finder.findAll();
+            assertTrue(false);
+        } catch (FindException e) {
+            assertTrue(e.getCause() instanceof InvalidModuleDescriptorException);
+        }
+    }
+
+    /**
+     * Test ModuleModule with a JAR file containing a .class file in the top
+     * level directory.
+     */
+    public void testOfOneExplodedModuleWithTopLevelClass() throws Exception {
+        Path dir = Files.createTempDirectory(USER_DIR, "mods");
+        Path m_dir = createExplodedModule(dir.resolve("m"), "m", "Mojo.class");
+
+        ModuleFinder finder = ModuleFinder.of(m_dir);
+        try {
+            finder.find("m");
+            assertTrue(false);
+        } catch (FindException e) {
+            assertTrue(e.getCause() instanceof InvalidModuleDescriptorException);
+        }
+
+        finder = ModuleFinder.of(m_dir);
+        try {
+            finder.findAll();
+            assertTrue(false);
+        } catch (FindException e) {
+            assertTrue(e.getCause() instanceof InvalidModuleDescriptorException);
+        }
+    }
+
+
+    /**
      * Test ModuleFinder.of with a path to a file that does not exist.
      */
     public void testOfWithDoesNotExistEntry() throws Exception {
@@ -641,7 +748,7 @@
             vs = mid.substring(i+1);
         }
         ModuleDescriptor.Builder builder
-                = new ModuleDescriptor.Builder(mn).requires("java.base");
+            = new ModuleDescriptor.Builder(mn).requires("java.base");
         if (vs != null)
             builder.version(vs);
         return builder.build();
@@ -651,13 +758,22 @@
      * Creates an exploded module in the given directory and containing a
      * module descriptor with the given module name/version.
      */
-    static Path createExplodedModule(Path dir, String mid) throws Exception {
+    static Path createExplodedModule(Path dir, String mid, String... entries)
+        throws Exception
+    {
         ModuleDescriptor descriptor = newModuleDescriptor(mid);
         Files.createDirectories(dir);
         Path mi = dir.resolve("module-info.class");
         try (OutputStream out = Files.newOutputStream(mi)) {
             ModuleInfoWriter.write(descriptor, out);
         }
+
+        for (String entry : entries) {
+            Path file = dir.resolve(entry.replace('/', File.separatorChar));
+            Files.createDirectories(file.getParent());
+            Files.createFile(file);
+        }
+
         return dir;
     }
 
--- a/test/java/lang/module/ModuleReader/ModuleReaderTest.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/test/java/lang/module/ModuleReader/ModuleReaderTest.java	Mon Oct 31 10:39:14 2016 -0700
@@ -24,9 +24,8 @@
 /**
  * @test
  * @library /lib/testlibrary
- * @modules java.base/jdk.internal.module
+ * @modules java.base/jdk.internal.misc
  *          jdk.compiler
- *          jdk.jlink
  * @build ModuleReaderTest CompilerUtils JarUtils
  * @run testng ModuleReaderTest
  * @summary Basic tests for java.lang.module.ModuleReader
@@ -47,11 +46,14 @@
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
 import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
 import java.util.spi.ToolProvider;
 
-import jdk.internal.module.ConfigurableModuleFinder;
-import jdk.internal.module.ConfigurableModuleFinder.Phase;
+import jdk.internal.misc.SharedSecrets;
 
 import org.testng.annotations.BeforeTest;
 import org.testng.annotations.Test;
@@ -101,7 +103,7 @@
     /**
      * Test ModuleReader to module in runtime image
      */
-    public void testImage() throws Exception {
+    public void testImage() throws IOException {
 
         ModuleFinder finder = ModuleFinder.ofSystem();
         ModuleReference mref = finder.find(BASE_MODULE).get();
@@ -119,6 +121,7 @@
                 testFind(reader, name, expectedBytes);
                 testOpen(reader, name, expectedBytes);
                 testRead(reader, name, expectedBytes);
+                testList(reader, name);
 
             }
 
@@ -168,7 +171,7 @@
     /**
      * Test ModuleReader to exploded module
      */
-    public void testExplodedModule() throws Exception {
+    public void testExplodedModule() throws IOException {
         test(MODS_DIR);
     }
 
@@ -176,7 +179,7 @@
     /**
      * Test ModuleReader to modular JAR
      */
-    public void testModularJar() throws Exception {
+    public void testModularJar() throws IOException {
         Path dir = Files.createTempDirectory(USER_DIR, "mlib");
 
         // jar cf mlib/${TESTMODULE}.jar -C mods .
@@ -190,7 +193,7 @@
     /**
      * Test ModuleReader to JMOD
      */
-    public void testJMod() throws Exception {
+    public void testJMod() throws IOException {
         Path dir = Files.createTempDirectory(USER_DIR, "mlib");
 
         // jmod create --class-path mods/${TESTMODULE}  mlib/${TESTMODULE}.jmod
@@ -211,13 +214,10 @@
      * The test module is found on the given module path. Open a ModuleReader
      * to the test module and test the reader.
      */
-    void test(Path mp) throws Exception {
+    void test(Path mp) throws IOException {
 
-        ModuleFinder finder = ModuleFinder.of(mp);
-        if (finder instanceof ConfigurableModuleFinder) {
-            // need ModuleFinder to be in the phase to find JMOD files
-            ((ConfigurableModuleFinder)finder).configurePhase(Phase.LINK_TIME);
-        }
+        ModuleFinder finder = SharedSecrets.getJavaLangModuleAccess()
+            .newModulePath(Runtime.version(), true, mp);
 
         ModuleReference mref = finder.find(TEST_MODULE).get();
         ModuleReader reader = mref.open();
@@ -234,6 +234,7 @@
                 testFind(reader, name, expectedBytes);
                 testOpen(reader, name, expectedBytes);
                 testRead(reader, name, expectedBytes);
+                testList(reader, name);
             }
 
             // test "not found"
@@ -275,13 +276,18 @@
             reader.read(TEST_RESOURCES[0]);
             assertTrue(false);
         } catch (IOException expected) { }
+
+        try {
+            reader.list();
+            assertTrue(false);
+        } catch (IOException expected) { }
     }
 
     /**
      * Test ModuleReader#find
      */
     void testFind(ModuleReader reader, String name, byte[] expectedBytes)
-        throws Exception
+        throws IOException
     {
         Optional<URI> ouri = reader.find(name);
         assertTrue(ouri.isPresent());
@@ -301,7 +307,7 @@
      * Test ModuleReader#open
      */
     void testOpen(ModuleReader reader, String name, byte[] expectedBytes)
-        throws Exception
+        throws IOException
     {
         Optional<InputStream> oin = reader.open(name);
         assertTrue(oin.isPresent());
@@ -317,7 +323,7 @@
      * Test ModuleReader#read
      */
     void testRead(ModuleReader reader, String name, byte[] expectedBytes)
-        throws Exception
+        throws IOException
     {
         Optional<ByteBuffer> obb = reader.read(name);
         assertTrue(obb.isPresent());
@@ -334,4 +340,24 @@
         }
     }
 
+    /**
+     * Test ModuleReader#list
+     */
+    void testList(ModuleReader reader, String name) throws IOException {
+        List<String> list = reader.list().collect(Collectors.toList());
+        Set<String> names = new HashSet<>(list);
+        assertTrue(names.size() == list.size()); // no duplicates
+
+        assertTrue(names.contains("module-info.class"));
+        assertTrue(names.contains(name));
+
+        // all resources should be locatable via find
+        for (String e : names) {
+            assertTrue(reader.find(e).isPresent());
+        }
+
+        // should not contain directories
+        names.forEach(e -> assertFalse(e.endsWith("/")));
+    }
+
 }
--- a/test/java/lang/module/ModuleReader/MultiReleaseJarTest.java	Mon Oct 31 14:50:09 2016 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,216 +0,0 @@
-/*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-/**
- * @test
- * @library /lib/testlibrary
- * @modules java.base/jdk.internal.module
- * @build MultiReleaseJarTest JarUtils
- * @run testng MultiReleaseJarTest
- * @run testng/othervm -Djdk.util.jar.enableMultiRelease=false MultiReleaseJarTest
- * @summary Basic test of ModuleReader with a modular JAR that is also a
- *          multi-release JAR
- */
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.lang.module.ModuleDescriptor;
-import java.lang.module.ModuleFinder;
-import java.lang.module.ModuleReader;
-import java.lang.module.ModuleReference;
-import java.net.URI;
-import java.net.URLConnection;
-import java.nio.ByteBuffer;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Optional;
-import java.util.Set;
-import java.util.jar.Attributes;
-import java.util.jar.Manifest;
-import java.util.stream.Collectors;
-
-import jdk.internal.module.ModuleInfoWriter;
-
-import org.testng.annotations.Test;
-import static org.testng.Assert.*;
-
-/**
- * Exercises ModuleReader with a modular JAR containing the following files:
- *
- * <pre>{@code
- *     module-info.class
- *     META-INF/versions/<version>/module.class
- * }</pre>
- *
- * The module-info.class in the top-level directory is the binary form of:
- * <pre>{@code
- *     module jdk.test {
- *         requires java.base;
- *     }
- * }</pre>
- *
- * The module-info.class in the versioned section is the binary form of:
- * <pre>{@code
- *     module jdk.test {
- *         requires java.base;
- *         requires jdk.unsupported;
- *     }
- * }</pre>
- */
-
-@Test
-public class MultiReleaseJarTest {
-
-    // Java SE/JDK major release
-    private static final int RELEASE = Runtime.version().major();
-
-    // the name of the test module
-    private static final String MODULE_NAME = "jdk.test";
-
-    private static final String MODULE_INFO_CLASS = "module-info.class";
-
-    /**
-     * Uses the ModuleFinder API to locate the module packaged as a modular
-     * and mutli-release JAR and then creates a ModuleReader to access the
-     * contents of the module.
-     */
-    public void testMultiReleaseJar() throws IOException {
-
-        // are multi-release JARs enabled?
-        String s = System.getProperty("jdk.util.jar.enableMultiRelease");
-        boolean multiRelease = (s == null || Boolean.parseBoolean(s));
-
-        // create the multi-release modular JAR
-        Path jarfile = createJarFile();
-
-        // find the module
-        ModuleFinder finder = ModuleFinder.of(jarfile);
-        Optional<ModuleReference> omref = finder.find(MODULE_NAME);
-        assertTrue((omref.isPresent()));
-        ModuleReference mref = omref.get();
-
-        // test that correct module-info.class was read
-        checkDescriptor(mref.descriptor(), multiRelease);
-
-        // test ModuleReader
-        try (ModuleReader reader = mref.open()) {
-
-            // open resource
-            Optional<InputStream> oin = reader.open(MODULE_INFO_CLASS);
-            assertTrue(oin.isPresent());
-            try (InputStream in = oin.get()) {
-                checkDescriptor(ModuleDescriptor.read(in), multiRelease);
-            }
-
-            // read resource
-            Optional<ByteBuffer> obb = reader.read(MODULE_INFO_CLASS);
-            assertTrue(obb.isPresent());
-            ByteBuffer bb = obb.get();
-            try {
-                checkDescriptor(ModuleDescriptor.read(bb), multiRelease);
-            } finally {
-                reader.release(bb);
-            }
-
-            // find resource
-            Optional<URI> ouri = reader.find(MODULE_INFO_CLASS);
-            assertTrue(ouri.isPresent());
-            URI uri = ouri.get();
-
-            String expectedTail = "!/";
-            if (multiRelease)
-                expectedTail += "META-INF/versions/" + RELEASE + "/";
-            expectedTail += MODULE_INFO_CLASS;
-            assertTrue(uri.toString().endsWith(expectedTail));
-
-            URLConnection uc = uri.toURL().openConnection();
-            uc.setUseCaches(false);
-            try (InputStream in = uc.getInputStream()) {
-                checkDescriptor(ModuleDescriptor.read(in), multiRelease);
-            }
-
-        }
-
-    }
-
-    /**
-     * Checks that the module descriptor is the expected module descriptor.
-     * When the multi release JAR feature is enabled then the module
-     * descriptor is expected to have been read from the versioned section
-     * of the JAR file.
-     */
-    private void checkDescriptor(ModuleDescriptor descriptor, boolean multiRelease) {
-        Set<String> requires = descriptor.requires().stream()
-                .map(ModuleDescriptor.Requires::name)
-                .collect(Collectors.toSet());
-        assertTrue(requires.contains("java.base"));
-        assertTrue(requires.contains("jdk.unsupported") == multiRelease);
-    }
-
-    /**
-     * Creates the modular JAR for the test, returning the Path to the JAR file.
-     */
-    private Path createJarFile() throws IOException {
-
-        // module descriptor for top-level directory
-        ModuleDescriptor descriptor1
-            = new ModuleDescriptor.Builder(MODULE_NAME)
-                .requires("java.base")
-                .build();
-
-        // module descriptor for versioned section
-        ModuleDescriptor descriptor2
-            = new ModuleDescriptor.Builder(MODULE_NAME)
-                .requires("java.base")
-                .requires("jdk.unsupported")
-                .build();
-
-        Path top = Paths.get(MODULE_NAME);
-        Files.createDirectories(top);
-
-        Path mi1 = Paths.get(MODULE_INFO_CLASS);
-        try (OutputStream out = Files.newOutputStream(top.resolve(mi1))) {
-            ModuleInfoWriter.write(descriptor1, out);
-        }
-
-        Path vdir = Paths.get("META-INF", "versions", Integer.toString(RELEASE));
-        Files.createDirectories(top.resolve(vdir));
-
-        Path mi2 = vdir.resolve(MODULE_INFO_CLASS);
-        try (OutputStream out = Files.newOutputStream(top.resolve(mi2))) {
-            ModuleInfoWriter.write(descriptor2, out);
-        }
-
-        Manifest man = new Manifest();
-        Attributes attrs = man.getMainAttributes();
-        attrs.put(Attributes.Name.MANIFEST_VERSION, "1.0");
-        attrs.put(Attributes.Name.MULTI_RELEASE, "true");
-
-        Path jarfile = Paths.get(MODULE_NAME + ".jar");
-        JarUtils.createJarFile(jarfile, man, top, mi1, mi2);
-
-        return jarfile;
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/lang/module/MultiReleaseJarTest.java	Mon Oct 31 10:39:14 2016 -0700
@@ -0,0 +1,336 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @library /lib/testlibrary
+ * @modules java.base/jdk.internal.module
+ * @build MultiReleaseJarTest JarUtils
+ * @run testng MultiReleaseJarTest
+ * @run testng/othervm -Djdk.util.jar.enableMultiRelease=false MultiReleaseJarTest
+ * @summary Basic test of modular JARs as multi-release JARs
+ */
+
+import java.io.File;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.module.ModuleDescriptor;
+import java.lang.module.ModuleFinder;
+import java.lang.module.ModuleReader;
+import java.lang.module.ModuleReference;
+import java.net.URI;
+import java.net.URLConnection;
+import java.nio.ByteBuffer;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+
+import jdk.internal.module.ModuleInfoWriter;
+
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+
+@Test
+public class MultiReleaseJarTest {
+
+    private static final String MODULE_INFO = "module-info.class";
+
+    private static final int RELEASE = Runtime.version().major();
+
+    // are multi-release JARs enabled?
+    private static final boolean MULTI_RELEASE;
+    static {
+        String s = System.getProperty("jdk.util.jar.enableMultiRelease");
+        MULTI_RELEASE = (s == null || Boolean.parseBoolean(s));
+    }
+
+    /**
+     * Basic test of a multi-release JAR.
+     */
+    public void testBasic() throws Exception {
+        String name = "m1";
+
+        ModuleDescriptor descriptor = new ModuleDescriptor.Builder(name)
+                .requires("java.base")
+                .build();
+
+        Path jar = new JarBuilder(name)
+                .moduleInfo("module-info.class", descriptor)
+                .resource("p/Main.class")
+                .resource("p/Helper.class")
+                .resource("META-INF/versions/9/p/Helper.class")
+                .resource("META-INF/versions/9/p/internal/Helper9.class")
+                .build();
+
+        // find the module
+        ModuleFinder finder = ModuleFinder.of(jar);
+        Optional<ModuleReference> omref = finder.find(name);
+        assertTrue((omref.isPresent()));
+        ModuleReference mref = omref.get();
+
+        // check module packages
+        descriptor = mref.descriptor();
+        Set<String> packages = descriptor.packages();
+        assertTrue(packages.contains("p"));
+        if (MULTI_RELEASE) {
+            assertTrue(packages.size() == 2);
+            assertTrue(packages.contains("p.internal"));
+        } else {
+            assertTrue(packages.size() == 1);
+        }
+    }
+
+    /**
+     * Test a multi-release JAR with a module-info.class in the versioned
+     * section of the JAR.
+     */
+    public void testModuleInfoInVersionedSection() throws Exception {
+        String name = "m1";
+
+        ModuleDescriptor descriptor1 = new ModuleDescriptor.Builder(name)
+                .requires("java.base")
+                .build();
+
+        // module descriptor for versioned section
+        ModuleDescriptor descriptor2 = new ModuleDescriptor.Builder(name)
+                .requires("java.base")
+                .requires("jdk.unsupported")
+                .build();
+
+        Path jar = new JarBuilder(name)
+                .moduleInfo(MODULE_INFO, descriptor1)
+                .resource("p/Main.class")
+                .resource("p/Helper.class")
+                .moduleInfo("META-INF/versions/9/" + MODULE_INFO, descriptor2)
+                .resource("META-INF/versions/9/p/Helper.class")
+                .resource("META-INF/versions/9/p/internal/Helper9.class")
+                .build();
+
+        // find the module
+        ModuleFinder finder = ModuleFinder.of(jar);
+        Optional<ModuleReference> omref = finder.find(name);
+        assertTrue((omref.isPresent()));
+        ModuleReference mref = omref.get();
+
+        // ensure that the right module-info.class is loaded
+        ModuleDescriptor descriptor = mref.descriptor();
+        assertEquals(descriptor.name(), name);
+        if (MULTI_RELEASE) {
+            assertEquals(descriptor.requires(), descriptor2.requires());
+        } else {
+            assertEquals(descriptor.requires(), descriptor1.requires());
+        }
+    }
+
+    /**
+     * Test multi-release JAR as an automatic module.
+     */
+    public void testAutomaticModule() throws Exception {
+        String name = "m";
+
+        Path jar = new JarBuilder(name)
+                .resource("p/Main.class")
+                .resource("p/Helper.class")
+                .resource("META-INF/versions/9/p/Helper.class")
+                .resource("META-INF/versions/9/p/internal/Helper9.class")
+                .build();
+
+        // find the module
+        ModuleFinder finder = ModuleFinder.of(jar);
+        Optional<ModuleReference> omref = finder.find(name);
+        assertTrue((omref.isPresent()));
+        ModuleReference mref = omref.get();
+
+        // check module packages
+        ModuleDescriptor descriptor = mref.descriptor();
+        Set<String> packages = descriptor.packages();
+        if (MULTI_RELEASE) {
+            assertTrue(packages.size() == 2);
+            assertTrue(packages.contains("p.internal"));
+        } else {
+            assertTrue(packages.size() == 1);
+        }
+    }
+
+    /**
+     * Exercise ModuleReader on a multi-release JAR
+     */
+    public void testModuleReader() throws Exception {
+        String name = "m1";
+
+        ModuleDescriptor descriptor1 = new ModuleDescriptor.Builder(name)
+                .requires("java.base")
+                .build();
+
+        // module descriptor for versioned section
+        ModuleDescriptor descriptor2 = new ModuleDescriptor.Builder(name)
+                .requires("java.base")
+                .requires("jdk.unsupported")
+                .build();
+
+        Path jar = new JarBuilder(name)
+                .moduleInfo(MODULE_INFO, descriptor1)
+                .moduleInfo("META-INF/versions/9/" + MODULE_INFO, descriptor2)
+                .build();
+
+        // find the module
+        ModuleFinder finder = ModuleFinder.of(jar);
+        Optional<ModuleReference> omref = finder.find(name);
+        assertTrue((omref.isPresent()));
+        ModuleReference mref = omref.get();
+
+        ModuleDescriptor expected;
+        if (MULTI_RELEASE) {
+            expected = descriptor2;
+        } else {
+            expected = descriptor1;
+        }
+
+        // test ModuleReader by reading module-info.class resource
+        try (ModuleReader reader = mref.open()) {
+
+            // open resource
+            Optional<InputStream> oin = reader.open(MODULE_INFO);
+            assertTrue(oin.isPresent());
+            try (InputStream in = oin.get()) {
+                checkRequires(ModuleDescriptor.read(in), expected);
+            }
+
+            // read resource
+            Optional<ByteBuffer> obb = reader.read(MODULE_INFO);
+            assertTrue(obb.isPresent());
+            ByteBuffer bb = obb.get();
+            try {
+                checkRequires(ModuleDescriptor.read(bb), expected);
+            } finally {
+                reader.release(bb);
+            }
+
+            // find resource
+            Optional<URI> ouri = reader.find(MODULE_INFO);
+            assertTrue(ouri.isPresent());
+            URI uri = ouri.get();
+
+            String expectedTail = "!/";
+            if (MULTI_RELEASE)
+                expectedTail += "META-INF/versions/" + RELEASE + "/";
+            expectedTail += MODULE_INFO;
+            assertTrue(uri.toString().endsWith(expectedTail));
+
+            URLConnection uc = uri.toURL().openConnection();
+            uc.setUseCaches(false);
+            try (InputStream in = uc.getInputStream()) {
+                checkRequires(ModuleDescriptor.read(in), expected);
+            }
+
+        }
+    }
+
+    /**
+     * Check that two ModuleDescriptor have the same requires
+     */
+    static void checkRequires(ModuleDescriptor md1, ModuleDescriptor md2) {
+        assertEquals(md1.requires(), md2.requires());
+    }
+
+    /**
+     * A builder of multi-release JAR files.
+     */
+    static class JarBuilder {
+        private String name;
+        private Set<String> resources = new HashSet<>();
+        private Map<String, ModuleDescriptor> descriptors = new HashMap<>();
+
+        JarBuilder(String name) {
+            this.name = name;
+        }
+
+        /**
+         * Adds a module-info.class to the JAR file.
+         */
+        JarBuilder moduleInfo(String name, ModuleDescriptor descriptor) {
+            descriptors.put(name, descriptor);
+            return this;
+        }
+
+        /**
+         * Adds a dummy resource to the JAR file.
+         */
+        JarBuilder resource(String name) {
+            resources.add(name);
+            return this;
+        }
+
+        /**
+         * Create the multi-release JAR, returning its file path.
+         */
+        Path build() throws Exception {
+            Path dir = Files.createTempDirectory(Paths.get(""), "jar");
+            List<Path> files = new ArrayList<>();
+
+            // write the module-info.class
+            for (Map.Entry<String, ModuleDescriptor> e : descriptors.entrySet()) {
+                String name = e.getKey();
+                ModuleDescriptor descriptor = e.getValue();
+                Path mi = Paths.get(name.replace('/', File.separatorChar));
+                Path parent = dir.resolve(mi).getParent();
+                if (parent != null)
+                    Files.createDirectories(parent);
+                try (OutputStream out = Files.newOutputStream(dir.resolve(mi))) {
+                    ModuleInfoWriter.write(descriptor, out);
+                }
+                files.add(mi);
+            }
+
+            // write the dummy resources
+            for (String name : resources) {
+                Path file = Paths.get(name.replace('/', File.separatorChar));
+                // create dummy resource
+                Path parent = dir.resolve(file).getParent();
+                if (parent != null)
+                    Files.createDirectories(parent);
+                Files.createFile(dir.resolve(file));
+                files.add(file);
+            }
+
+            Manifest man = new Manifest();
+            Attributes attrs = man.getMainAttributes();
+            attrs.put(Attributes.Name.MANIFEST_VERSION, "1.0");
+            attrs.put(Attributes.Name.MULTI_RELEASE, "true");
+
+            Path jarfile = Paths.get(name + ".jar");
+            JarUtils.createJarFile(jarfile, man, dir, files.toArray(new Path[0]));
+            return jarfile;
+        }
+    }
+}
--- a/test/java/util/ResourceBundle/modules/basic/src/asiabundles/jdk/test/resources/MyResources_ja_JP.properties	Mon Oct 31 14:50:09 2016 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-#
-# Copyright (c) 2015, 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.
-#
-
-# This resource bundle is located at jdk/test/resources to demonstrate
-# the unique package requirement is not applicable to .properties bundles.
-
-key=ja-JP: message
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/util/ResourceBundle/modules/basic/src/asiabundles/jdk/test/resources/asia/MyResources_ja_JP.properties	Mon Oct 31 10:39:14 2016 -0700
@@ -0,0 +1,27 @@
+#
+# Copyright (c) 2015, 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.
+#
+
+# This resource bundle is located at jdk/test/resources to demonstrate
+# the unique package requirement is not applicable to .properties bundles.
+
+key=ja-JP: message
--- a/test/java/util/ResourceBundle/modules/basic/src/mainbundles/jdk/test/resources/MyResourcesProvider.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/test/java/util/ResourceBundle/modules/basic/src/mainbundles/jdk/test/resources/MyResourcesProvider.java	Mon Oct 31 10:39:14 2016 -0700
@@ -62,9 +62,7 @@
 
     @Override
     protected String toBundleName(String baseName, Locale locale) {
-        // The resource bundle for Locale.JAPAN is loccated at jdk.test.resources
-        // in module "asiabundles".
-        String name = locale.equals(Locale.JAPAN) ? baseName : addRegion(baseName);
+        String name = addRegion(baseName);
         return Control.getControl(Control.FORMAT_DEFAULT).toBundleName(name, locale);
     }
 
--- a/test/java/util/Scanner/ScanTest.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/test/java/util/Scanner/ScanTest.java	Mon Oct 31 10:39:14 2016 -0700
@@ -24,7 +24,7 @@
 /**
  * @test
  * @bug 4313885 4926319 4927634 5032610 5032622 5049968 5059533 6223711 6277261 6269946 6288823
- *      8072722 8139414
+ *      8072722 8139414 8166261
  * @summary Basic tests of java.util.Scanner methods
  * @key randomness
  * @modules jdk.localedata
@@ -36,6 +36,7 @@
 import java.nio.*;
 import java.text.*;
 import java.util.*;
+import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 import java.util.regex.*;
 import java.util.stream.*;
@@ -79,6 +80,7 @@
             resetTest();
             streamCloseTest();
             streamComodTest();
+            outOfRangeRadixTest();
 
             for (int j = 0; j < NUM_SOURCE_TYPES; j++) {
                 hasNextTest(j);
@@ -1509,6 +1511,48 @@
         report("Reset test");
     }
 
+    static List<BiConsumer <Scanner, Integer>> methodWRList = Arrays.asList(
+        (s, r) -> s.hasNextByte(r),
+        (s, r) -> s.nextByte(r),
+        (s, r) -> s.hasNextShort(r),
+        (s, r) -> s.nextShort(r),
+        (s, r) -> s.hasNextInt(r),
+        (s, r) -> s.nextInt(r),
+        (s, r) -> s.hasNextLong(r),
+        (s, r) -> s.nextLong(r),
+        (s, r) -> s.hasNextBigInteger(r),
+        (s, r) -> s.nextBigInteger(r)
+    );
+
+    /*
+     * Test that setting the radix to an out of range value triggers
+     * an IllegalArgumentException
+     */
+    public static void outOfRangeRadixTest() throws Exception {
+        int[] bad = new int[] { -1, 0,  1, 37, 38 };
+        int[] good = IntStream.rangeClosed(Character.MIN_RADIX, Character.MAX_RADIX)
+                              .toArray();
+
+        methodWRList.stream().forEach( m -> {
+            for (int r : bad) {
+                try (Scanner sc = new Scanner("10 10 10 10")) {
+                    m.accept(sc, r);
+                    failCount++;
+                } catch (IllegalArgumentException ise) {}
+            }
+        });
+        methodWRList.stream().forEach( m -> {
+            for (int r : good) {
+                try (Scanner sc = new Scanner("10 10 10 10")) {
+                    m.accept(sc, r);
+                } catch (Exception x) {
+                    failCount++;
+                }
+            }
+        });
+        report("Radix out of range test");
+    }
+
     /*
      * Test that closing the stream also closes the underlying Scanner.
      * The cases of attempting to open streams on a closed Scanner are
--- a/test/javax/net/ssl/DTLS/DTLSOverDatagram.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/test/javax/net/ssl/DTLS/DTLSOverDatagram.java	Mon Oct 31 10:39:14 2016 -0700
@@ -48,10 +48,6 @@
  */
 public class DTLSOverDatagram {
 
-    static {
-        System.setProperty("javax.net.debug", "ssl");
-    }
-
     private static int MAX_HANDSHAKE_LOOPS = 200;
     private static int MAX_APP_READ_LOOPS = 60;
     private static int SOCKET_TIMEOUT = 10 * 1000; // in millis
@@ -160,6 +156,7 @@
             }
 
             SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
+            log(side, "=======handshake(" + loops + ", " + hs + ")=======");
             if (hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP ||
                 hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP_AGAIN) {
 
@@ -239,6 +236,7 @@
                 boolean finished = produceHandshakePackets(
                     engine, peerAddr, side, packets);
 
+                log(side, "Produced " + packets.size() + " packets");
                 for (DatagramPacket p : packets) {
                     socket.send(p);
                 }
@@ -252,14 +250,16 @@
             } else if (hs == SSLEngineResult.HandshakeStatus.NEED_TASK) {
                 runDelegatedTasks(engine);
             } else if (hs == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
-                log(side, "Handshake status is NOT_HANDSHAKING, finish the loop");
+                log(side,
+                    "Handshake status is NOT_HANDSHAKING, finish the loop");
                 endLoops = true;
             } else if (hs == SSLEngineResult.HandshakeStatus.FINISHED) {
                 throw new Exception(
                         "Unexpected status, SSLEngine.getHandshakeStatus() "
                                 + "shouldn't return FINISHED");
             } else {
-                throw new Exception("Can't reach here, handshake status is " + hs);
+                throw new Exception(
+                        "Can't reach here, handshake status is " + hs);
             }
         }
 
@@ -279,7 +279,9 @@
         log(side, "Negotiated cipher suite is " + session.getCipherSuite());
 
         // handshake status should be NOT_HANDSHAKING
-        // according to the spec, SSLEngine.getHandshakeStatus() can't return FINISHED
+        //
+        // According to the spec, SSLEngine.getHandshakeStatus() can't
+        // return FINISHED.
         if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
             throw new Exception("Unexpected handshake status " + hs);
         }
@@ -348,13 +350,16 @@
 
             SSLEngineResult.Status rs = r.getStatus();
             SSLEngineResult.HandshakeStatus hs = r.getHandshakeStatus();
+            log(side, "====packet(" + loops + ", " + rs + ", " + hs + ")====");
             if (rs == SSLEngineResult.Status.BUFFER_OVERFLOW) {
                 // the client maximum fragment size config does not work?
                 throw new Exception("Buffer overflow: " +
                             "incorrect server maximum fragment size");
             } else if (rs == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
-                log(side, "Produce handshake packets: BUFFER_UNDERFLOW occured");
-                log(side, "Produce handshake packets: Handshake status: " + hs);
+                log(side,
+                        "Produce handshake packets: BUFFER_UNDERFLOW occured");
+                log(side,
+                        "Produce handshake packets: Handshake status: " + hs);
                 // bad packet, or the client maximum fragment size
                 // config does not work?
                 if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
@@ -453,6 +458,53 @@
         return packets;
     }
 
+    // Get a datagram packet for the specified handshake type.
+    static DatagramPacket getPacket(
+            List<DatagramPacket> packets, byte handshakeType) {
+        boolean matched = false;
+        for (DatagramPacket packet : packets) {
+            byte[] data = packet.getData();
+            int offset = packet.getOffset();
+            int length = packet.getLength();
+
+            // Normally, this pakcet should be a handshake message
+            // record.  However, even if the underlying platform
+            // splits the record more, we don't really worry about
+            // the improper packet loss because DTLS implementation
+            // should be able to handle packet loss properly.
+            //
+            // See RFC 6347 for the detailed format of DTLS records.
+            if (handshakeType == -1) {      // ChangeCipherSpec
+                // Is it a ChangeCipherSpec message?
+                matched = (length == 14) && (data[offset] == 0x14);
+            } else if ((length >= 25) &&    // 25: handshake mini size
+                (data[offset] == 0x16)) {   // a handshake message
+
+                // check epoch number for initial handshake only
+                if (data[offset + 3] == 0x00) {     // 3,4: epoch
+                    if (data[offset + 4] == 0x00) { // plaintext
+                        matched =
+                            (data[offset + 13] == handshakeType);
+                    } else {                        // cipherext
+                        // The 1st ciphertext is a Finished message.
+                        //
+                        // If it is not proposed to loss the Finished
+                        // message, it is not necessary to check the
+                        // following packets any mroe as a Finished
+                        // message is the last handshake message.
+                        matched = (handshakeType == 20);
+                    }
+                }
+            }
+
+            if (matched) {
+                return packet;
+            }
+        }
+
+        return null;
+    }
+
     // run delegated tasks
     void runDelegatedTasks(SSLEngine engine) throws Exception {
         Runnable runnable;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/net/ssl/DTLS/PacketLossRetransmission.java	Mon Oct 31 10:39:14 2016 -0700
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2016, 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.
+ */
+
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+
+/*
+ * @test
+ * @bug 8161086
+ * @summary DTLS handshaking fails if some messages were lost
+ * @modules java.base/sun.security.util
+ * @build DTLSOverDatagram
+ *
+ * @run main/othervm PacketLossRetransmission client 0 hello_request
+ * @run main/othervm PacketLossRetransmission client 1 client_hello
+ * @run main/othervm PacketLossRetransmission client 2 server_hello
+ * @run main/othervm PacketLossRetransmission client 3 hello_verify_request
+ * @run main/othervm PacketLossRetransmission client 4 new_session_ticket
+ * @run main/othervm PacketLossRetransmission client 11 certificate
+ * @run main/othervm PacketLossRetransmission client 12 server_key_exchange
+ * @run main/othervm PacketLossRetransmission client 13 certificate_request
+ * @run main/othervm PacketLossRetransmission client 14 server_hello_done
+ * @run main/othervm PacketLossRetransmission client 15 certificate_verify
+ * @run main/othervm PacketLossRetransmission client 16 client_key_exchange
+ * @run main/othervm PacketLossRetransmission client 20 finished
+ * @run main/othervm PacketLossRetransmission client 21 certificate_url
+ * @run main/othervm PacketLossRetransmission client 22 certificate_status
+ * @run main/othervm PacketLossRetransmission client 23 supplemental_data
+ * @run main/othervm PacketLossRetransmission client -1 change_cipher_spec
+ * @run main/othervm PacketLossRetransmission server 0 hello_request
+ * @run main/othervm PacketLossRetransmission server 1 client_hello
+ * @run main/othervm PacketLossRetransmission server 2 server_hello
+ * @run main/othervm PacketLossRetransmission server 3 hello_verify_request
+ * @run main/othervm PacketLossRetransmission server 4 new_session_ticket
+ * @run main/othervm PacketLossRetransmission server 11 certificate
+ * @run main/othervm PacketLossRetransmission server 12 server_key_exchange
+ * @run main/othervm PacketLossRetransmission server 13 certificate_request
+ * @run main/othervm PacketLossRetransmission server 14 server_hello_done
+ * @run main/othervm PacketLossRetransmission server 15 certificate_verify
+ * @run main/othervm PacketLossRetransmission server 16 client_key_exchange
+ * @run main/othervm PacketLossRetransmission server 20 finished
+ * @run main/othervm PacketLossRetransmission server 21 certificate_url
+ * @run main/othervm PacketLossRetransmission server 22 certificate_status
+ * @run main/othervm PacketLossRetransmission server 23 supplemental_data
+ * @run main/othervm PacketLossRetransmission server -1 change_cipher_spec
+ */
+
+import java.util.List;
+import java.util.ArrayList;
+import java.net.DatagramPacket;
+import java.net.SocketAddress;
+import javax.net.ssl.SSLEngine;
+
+/**
+ * Test that DTLS implementation is able to do retransmission internally
+ * automatically if packet get lost.
+ */
+public class PacketLossRetransmission extends DTLSOverDatagram {
+    private static boolean isClient;
+    private static byte handshakeType;
+
+    private boolean needPacketLoss = true;
+
+    public static void main(String[] args) throws Exception {
+        isClient = args[0].equals("client");
+        handshakeType = Byte.valueOf(args[1]);
+
+        PacketLossRetransmission testCase = new PacketLossRetransmission();
+        testCase.runTest(testCase);
+    }
+
+    @Override
+    boolean produceHandshakePackets(SSLEngine engine, SocketAddress socketAddr,
+            String side, List<DatagramPacket> packets) throws Exception {
+
+        boolean finished = super.produceHandshakePackets(
+                engine, socketAddr, side, packets);
+
+        if (needPacketLoss && (!(isClient ^ engine.getUseClientMode()))) {
+            DatagramPacket packet = getPacket(packets, handshakeType);
+            if (packet != null) {
+                needPacketLoss = false;
+
+                System.out.println("Loss a packet of handshake messahe");
+                packets.remove(packet);
+            }
+        }
+
+        return finished;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/net/ssl/DTLS/RespondToRetransmit.java	Mon Oct 31 10:39:14 2016 -0700
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2016, 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.
+ */
+
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+
+/*
+ * @test
+ * @bug 8161086
+ * @summary DTLS handshaking fails if some messages were lost
+ * @modules java.base/sun.security.util
+ * @build DTLSOverDatagram
+ *
+ * @run main/othervm RespondToRetransmit client 0 hello_request
+ * @run main/othervm RespondToRetransmit client 1 client_hello
+ * @run main/othervm RespondToRetransmit client 2 server_hello
+ * @run main/othervm RespondToRetransmit client 3 hello_verify_request
+ * @run main/othervm RespondToRetransmit client 4 new_session_ticket
+ * @run main/othervm RespondToRetransmit client 11 certificate
+ * @run main/othervm RespondToRetransmit client 12 server_key_exchange
+ * @run main/othervm RespondToRetransmit client 13 certificate_request
+ * @run main/othervm RespondToRetransmit client 14 server_hello_done
+ * @run main/othervm RespondToRetransmit client 15 certificate_verify
+ * @run main/othervm RespondToRetransmit client 16 client_key_exchange
+ * @run main/othervm RespondToRetransmit client 20 finished
+ * @run main/othervm RespondToRetransmit client 21 certificate_url
+ * @run main/othervm RespondToRetransmit client 22 certificate_status
+ * @run main/othervm RespondToRetransmit client 23 supplemental_data
+ * @run main/othervm RespondToRetransmit client -1 change_cipher_spec
+ * @run main/othervm RespondToRetransmit server 0 hello_request
+ * @run main/othervm RespondToRetransmit server 1 client_hello
+ * @run main/othervm RespondToRetransmit server 2 server_hello
+ * @run main/othervm RespondToRetransmit server 3 hello_verify_request
+ * @run main/othervm RespondToRetransmit server 4 new_session_ticket
+ * @run main/othervm RespondToRetransmit server 11 certificate
+ * @run main/othervm RespondToRetransmit server 12 server_key_exchange
+ * @run main/othervm RespondToRetransmit server 13 certificate_request
+ * @run main/othervm RespondToRetransmit server 14 server_hello_done
+ * @run main/othervm RespondToRetransmit server 15 certificate_verify
+ * @run main/othervm RespondToRetransmit server 16 client_key_exchange
+ * @run main/othervm RespondToRetransmit server 20 finished
+ * @run main/othervm RespondToRetransmit server 21 certificate_url
+ * @run main/othervm RespondToRetransmit server 22 certificate_status
+ * @run main/othervm RespondToRetransmit server 23 supplemental_data
+ * @run main/othervm RespondToRetransmit server -1 change_cipher_spec
+ */
+
+import java.util.List;
+import java.util.ArrayList;
+import java.net.DatagramPacket;
+import java.net.SocketAddress;
+import javax.net.ssl.SSLEngine;
+
+/**
+ * Test that DTLS implementation is able to do retransmission internally
+ * automatically if packet get lost.
+ */
+public class RespondToRetransmit extends DTLSOverDatagram {
+    private static boolean isClient;
+    private static byte handshakeType;
+
+    private boolean needPacketDuplicate = true;
+
+    public static void main(String[] args) throws Exception {
+        isClient = args[0].equals("client");
+        handshakeType = Byte.valueOf(args[1]);
+
+        RespondToRetransmit testCase = new RespondToRetransmit();
+        testCase.runTest(testCase);
+    }
+
+    @Override
+    boolean produceHandshakePackets(SSLEngine engine, SocketAddress socketAddr,
+            String side, List<DatagramPacket> packets) throws Exception {
+
+        boolean finished = super.produceHandshakePackets(
+                engine, socketAddr, side, packets);
+
+        if (needPacketDuplicate && (!(isClient ^ engine.getUseClientMode()))) {
+            DatagramPacket packet = getPacket(packets, handshakeType);
+            if (packet != null) {
+                needPacketDuplicate = false;
+
+                System.out.println("Duplicate the flight.");
+                List<DatagramPacket> duplicates = new ArrayList<>();
+                finished = super.produceHandshakePackets(
+                        engine, socketAddr, side, duplicates);
+                packets.addAll(duplicates);
+            }
+        }
+
+        return finished;
+    }
+}
--- a/test/javax/net/ssl/TLSCommon/SSLEngineTestCase.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/test/javax/net/ssl/TLSCommon/SSLEngineTestCase.java	Mon Oct 31 10:39:14 2016 -0700
@@ -27,7 +27,9 @@
 import javax.net.ssl.SNIServerName;
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLSession;
 import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
 import javax.net.ssl.SSLException;
 import javax.net.ssl.SSLParameters;
 import javax.net.ssl.TrustManagerFactory;
@@ -57,19 +59,21 @@
     public enum Ciphers {
 
         /**
-         * Ciphers supported by the tested SSLEngine without those with kerberos
-         * authentication.
+         * Ciphers supported by the tested SSLEngine without those with
+         * kerberos authentication.
          */
         SUPPORTED_NON_KRB_CIPHERS(SSLEngineTestCase.SUPPORTED_NON_KRB_CIPHERS,
                 "Supported non kerberos"),
         /**
-         * Ciphers supported by the tested SSLEngine without those with kerberos
-         * authentication and without those with SHA256 ans SHA384.
+         * Ciphers supported by the tested SSLEngine without those with
+         * kerberos authentication and without those with SHA256 ans SHA384.
          */
-        SUPPORTED_NON_KRB_NON_SHA_CIPHERS(SSLEngineTestCase.SUPPORTED_NON_KRB_NON_SHA_CIPHERS,
+        SUPPORTED_NON_KRB_NON_SHA_CIPHERS(
+                SSLEngineTestCase.SUPPORTED_NON_KRB_NON_SHA_CIPHERS,
                 "Supported non kerberos non SHA256 and SHA384"),
         /**
-         * Ciphers supported by the tested SSLEngine with kerberos authentication.
+         * Ciphers supported by the tested SSLEngine with kerberos
+         * authentication.
          */
         SUPPORTED_KRB_CIPHERS(SSLEngineTestCase.SUPPORTED_KRB_CIPHERS,
                 "Supported kerberos"),
@@ -147,13 +151,13 @@
             = System.getProperty("test.src", ".") + FS + PATH_TO_STORES
             + FS + TRUST_STORE_FILE;
 
+    // Need an enhancement to use none-static mutable global variables.
     private static ByteBuffer net;
-    private static ByteBuffer netReplicatedClient;
-    private static ByteBuffer netReplicatedServer;
+    private static boolean doUnwrapForNotHandshakingStatus;
+    private static boolean endHandshakeLoop = false;
+
     private static final int MAX_HANDSHAKE_LOOPS = 100;
     private static final String EXCHANGE_MSG_SENT = "Hello, peer!";
-    private static boolean doUnwrapForNotHandshakingStatus;
-    private static boolean endHandshakeLoop = false;
     private static final String TEST_SRC = System.getProperty("test.src", ".");
     private static final String KTAB_FILENAME = "krb5.keytab.data";
     private static final String KRB_REALM = "TEST.REALM";
@@ -179,11 +183,13 @@
             List<String> supportedCiphersList = new LinkedList<>();
             for (String cipher : allSupportedCiphers) {
                 if (!cipher.contains("KRB5")
-                        && !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")) {
+                    && !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")) {
+
                     supportedCiphersList.add(cipher);
                 }
             }
-            SUPPORTED_NON_KRB_CIPHERS = supportedCiphersList.toArray(new String[0]);
+            SUPPORTED_NON_KRB_CIPHERS =
+                    supportedCiphersList.toArray(new String[0]);
         } catch (Exception ex) {
             throw new Error("Unexpected issue", ex);
         }
@@ -220,7 +226,7 @@
             List<String> supportedCiphersList = new LinkedList<>();
             for (String cipher : allSupportedCiphers) {
                 if (cipher.contains("KRB5")
-                        && !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")) {
+                    && !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")) {
                     supportedCiphersList.add(cipher);
                 }
             }
@@ -240,11 +246,12 @@
             List<String> enabledCiphersList = new LinkedList<>();
             for (String cipher : enabledCiphers) {
                 if (!cipher.contains("anon") && !cipher.contains("KRB5")
-                        && !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")) {
+                    && !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")) {
                     enabledCiphersList.add(cipher);
                 }
             }
-            ENABLED_NON_KRB_NOT_ANON_CIPHERS = enabledCiphersList.toArray(new String[0]);
+            ENABLED_NON_KRB_NOT_ANON_CIPHERS =
+                    enabledCiphersList.toArray(new String[0]);
         } catch (Exception ex) {
             throw new Error("Unexpected issue", ex);
         }
@@ -300,10 +307,10 @@
      * Wraps data with the specified engine.
      *
      * @param engine        - SSLEngine that wraps data.
-     * @param wrapper       - Set wrapper id, e.g. "server" of "client". Used for
-     *                      logging only.
-     * @param maxPacketSize - Max packet size to check that MFLN extension works
-     *                      or zero for no check.
+     * @param wrapper       - Set wrapper id, e.g. "server" of "client".
+     *                        Used for logging only.
+     * @param maxPacketSize - Max packet size to check that MFLN extension
+     *                        works or zero for no check.
      * @param app           - Buffer with data to wrap.
      * @return - Buffer with wrapped data.
      * @throws SSLException - thrown on engine errors.
@@ -319,13 +326,13 @@
      * Wraps data with the specified engine.
      *
      * @param engine        - SSLEngine that wraps data.
-     * @param wrapper       - Set wrapper id, e.g. "server" of "client". Used for
-     *                      logging only.
-     * @param maxPacketSize - Max packet size to check that MFLN extension works
-     *                      or zero for no check.
+     * @param wrapper       - Set wrapper id, e.g. "server" of "client".
+     *                        Used for logging only.
+     * @param maxPacketSize - Max packet size to check that MFLN extension
+     *                        works or zero for no check.
      * @param app           - Buffer with data to wrap.
-     * @param result        - Array which first element will be used to output wrap
-     *                      result object.
+     * @param result        - Array which first element will be used to
+     *                        output wrap result object.
      * @return - Buffer with wrapped data.
      * @throws SSLException - thrown on engine errors.
      */
@@ -341,10 +348,10 @@
      * Wraps data with the specified engine.
      *
      * @param engine        - SSLEngine that wraps data.
-     * @param wrapper       - Set wrapper id, e.g. "server" of "client". Used for
-     *                      logging only.
-     * @param maxPacketSize - Max packet size to check that MFLN extension works
-     *                      or zero for no check.
+     * @param wrapper       - Set wrapper id, e.g. "server" of "client".
+     *                        Used for logging only.
+     * @param maxPacketSize - Max packet size to check that MFLN extension
+     *                        works or zero for no check.
      * @param app           - Buffer with data to wrap.
      * @param wantedStatus  - Specifies expected result status of wrapping.
      * @return - Buffer with wrapped data.
@@ -362,14 +369,14 @@
      * Wraps data with the specified engine.
      *
      * @param engine        - SSLEngine that wraps data.
-     * @param wrapper       - Set wrapper id, e.g. "server" of "client". Used for
-     *                      logging only.
-     * @param maxPacketSize - Max packet size to check that MFLN extension works
-     *                      or zero for no check.
+     * @param wrapper       - Set wrapper id, e.g. "server" of "client".
+     *                        Used for logging only.
+     * @param maxPacketSize - Max packet size to check that MFLN extension
+     *                        works or zero for no check.
      * @param app           - Buffer with data to wrap.
      * @param wantedStatus  - Specifies expected result status of wrapping.
-     * @param result        - Array which first element will be used to output wrap
-     *                      result object.
+     * @param result        - Array which first element will be used to output
+     *                        wrap result object.
      * @return - Buffer with wrapped data.
      * @throws SSLException - thrown on engine errors.
      */
@@ -409,9 +416,9 @@
      * @throws SSLException - thrown on engine errors.
      */
     public static ByteBuffer doUnWrap(SSLEngine engine, String unwrapper,
-                                      ByteBuffer net)
-            throws SSLException {
-        return doUnWrap(engine, unwrapper, net, SSLEngineResult.Status.OK, null);
+            ByteBuffer net) throws SSLException {
+        return doUnWrap(engine, unwrapper,
+                net, SSLEngineResult.Status.OK, null);
     }
 
     /**
@@ -427,26 +434,25 @@
      * @throws SSLException - thrown on engine errors.
      */
     public static ByteBuffer doUnWrap(SSLEngine engine, String unwrapper,
-                                      ByteBuffer net, SSLEngineResult[] result)
-            throws SSLException {
-        return doUnWrap(engine, unwrapper, net, SSLEngineResult.Status.OK, result);
+            ByteBuffer net, SSLEngineResult[] result) throws SSLException {
+        return doUnWrap(engine, unwrapper,
+                net, SSLEngineResult.Status.OK, result);
     }
 
     /**
      * Unwraps data with the specified engine.
      *
      * @param engine       - SSLEngine that unwraps data.
-     * @param unwrapper    - Set unwrapper id, e.g. "server" of "client". Used for
-     *                     logging only.
+     * @param unwrapper    - Set unwrapper id, e.g. "server" of "client".
+     *                     Used for logging only.
      * @param net          - Buffer with data to unwrap.
      * @param wantedStatus - Specifies expected result status of wrapping.
      * @return - Buffer with unwrapped data.
      * @throws SSLException - thrown on engine errors.
      */
     public static ByteBuffer doUnWrap(SSLEngine engine, String unwrapper,
-                                      ByteBuffer net,
-                                      SSLEngineResult.Status wantedStatus)
-            throws SSLException {
+            ByteBuffer net,
+            SSLEngineResult.Status wantedStatus) throws SSLException {
         return doUnWrap(engine, unwrapper, net, wantedStatus, null);
     }
 
@@ -454,25 +460,23 @@
      * Unwraps data with the specified engine.
      *
      * @param engine       - SSLEngine that unwraps data.
-     * @param unwrapper    - Set unwrapper id, e.g. "server" of "client". Used for
-     *                     logging only.
+     * @param unwrapper    - Set unwrapper id, e.g. "server" of "client".
+     *                       Used for logging only.
      * @param net          - Buffer with data to unwrap.
      * @param wantedStatus - Specifies expected result status of wrapping.
-     * @param result       - Array which first element will be used to output wrap
-     *                     result object.
+     * @param result       - Array which first element will be used to output
+     *                       wrap result object.
      * @return - Buffer with unwrapped data.
      * @throws SSLException - thrown on engine errors.
      */
     public static ByteBuffer doUnWrap(SSLEngine engine, String unwrapper,
-                                      ByteBuffer net,
-                                      SSLEngineResult.Status wantedStatus,
-                                      SSLEngineResult[] result)
-            throws SSLException {
-        ByteBuffer app = ByteBuffer.allocate(engine.getSession()
-                .getApplicationBufferSize());
+            ByteBuffer net, SSLEngineResult.Status wantedStatus,
+            SSLEngineResult[] result) throws SSLException {
+
+        ByteBuffer app = ByteBuffer.allocate(
+                engine.getSession().getApplicationBufferSize());
         int length = net.remaining();
-        System.out.println(unwrapper + " unwrapping "
-                + length + " bytes...");
+        System.out.println(unwrapper + " unwrapping " + length + " bytes...");
         SSLEngineResult r = engine.unwrap(net, app);
         app.flip();
         System.out.println(unwrapper + " handshake status is "
@@ -491,13 +495,14 @@
      * @param clientEngine  - Client SSLEngine.
      * @param serverEngine  - Server SSLEngine.
      * @param maxPacketSize - Maximum packet size for MFLN of zero for no limit.
-     * @param mode          - Handshake mode according to {@link HandshakeMode} enum.
+     * @param mode          - Handshake mode according to
+     *                        {@link HandshakeMode} enum.
      * @throws SSLException - thrown on engine errors.
      */
     public static void doHandshake(SSLEngine clientEngine,
-                                   SSLEngine serverEngine,
-                                   int maxPacketSize, HandshakeMode mode)
-            throws SSLException {
+        SSLEngine serverEngine,
+        int maxPacketSize, HandshakeMode mode) throws SSLException {
+
         doHandshake(clientEngine, serverEngine, maxPacketSize, mode, false);
     }
 
@@ -507,19 +512,20 @@
      *
      * @param clientEngine          - Client SSLEngine.
      * @param serverEngine          - Server SSLEngine.
-     * @param maxPacketSize         - Maximum packet size for MFLN of zero for no limit.
-     * @param mode                  - Handshake mode according to {@link HandshakeMode} enum.
+     * @param maxPacketSize         - Maximum packet size for MFLN of zero
+     *                                for no limit.
+     * @param mode                  - Handshake mode according to
+     *                                {@link HandshakeMode} enum.
      * @param enableReplicatedPacks - Set {@code true} to enable replicated
-     *                              packet sending.
+     *                                packet sending.
      * @throws SSLException - thrown on engine errors.
      */
     public static void doHandshake(SSLEngine clientEngine,
-                                   SSLEngine serverEngine, int maxPacketSize,
-                                   HandshakeMode mode,
-                                   boolean enableReplicatedPacks)
-            throws SSLException {
-        System.out.println("================================================="
-                + "===========");
+            SSLEngine serverEngine, int maxPacketSize,
+            HandshakeMode mode,
+            boolean enableReplicatedPacks) throws SSLException {
+
+        System.out.println("=============================================");
         System.out.println("Starting handshake " + mode.name());
         int loop = 0;
         if (maxPacketSize < 0) {
@@ -561,18 +567,16 @@
             if (++loop > MAX_HANDSHAKE_LOOPS) {
                 throw new Error("Too much loops for handshaking");
             }
-            System.out.println("==============================================");
-            System.out.println("Handshake loop " + loop);
-            SSLEngineResult.HandshakeStatus clientHSStatus
-                    = clientEngine.getHandshakeStatus();
-            SSLEngineResult.HandshakeStatus serverHSStatus
-                    = serverEngine.getHandshakeStatus();
-            System.out.println("Client handshake status "
-                    + clientHSStatus.name());
-            System.out.println("Server handshake status "
-                    + serverHSStatus.name());
+            System.out.println("============================================");
+            System.out.println("Handshake loop " + loop + ": round 1");
+            System.out.println("==========================");
             handshakeProcess(firstEngine, secondEngine, maxPacketSize,
                     enableReplicatedPacks);
+            if (endHandshakeLoop) {
+                break;
+            }
+            System.out.println("Handshake loop " + loop + ": round 2");
+            System.out.println("==========================");
             handshakeProcess(secondEngine, firstEngine, maxPacketSize,
                     enableReplicatedPacks);
         }
@@ -596,15 +600,15 @@
             sender = "Client";
             reciever = "Server";
             excMsgSent += " Client.";
-        } else if (toEngine.getUseClientMode() && !fromEngine.getUseClientMode()) {
+        } else if (toEngine.getUseClientMode() &&
+                !fromEngine.getUseClientMode()) {
             sender = "Server";
             reciever = "Client";
             excMsgSent += " Server.";
         } else {
             throw new Error("Test issue: both engines are in the same mode");
         }
-        System.out.println("================================================="
-                + "===========");
+        System.out.println("=============================================");
         System.out.println("Trying to send application data from " + sender
                 + " to " + reciever);
         ByteBuffer clientAppSent
@@ -643,20 +647,24 @@
         if (fromEngine.getUseClientMode() && !toEngine.getUseClientMode()) {
             from = "Client";
             to = "Server";
-        } else if (toEngine.getUseClientMode() && !fromEngine.getUseClientMode()) {
+        } else if (toEngine.getUseClientMode() &&
+                !fromEngine.getUseClientMode()) {
             from = "Server";
             to = "Client";
         } else {
             throw new Error("Both engines are in the same mode");
         }
-        System.out.println("=========================================================");
-        System.out.println("Trying to close engines from " + from + " to " + to);
+        System.out.println("=============================================");
+        System.out.println(
+                "Trying to close engines from " + from + " to " + to);
         // Sending close outbound request to peer
         fromEngine.closeOutbound();
-        app = ByteBuffer.allocate(fromEngine.getSession().getApplicationBufferSize());
+        app = ByteBuffer.allocate(
+                fromEngine.getSession().getApplicationBufferSize());
         net = doWrap(fromEngine, from, 0, app, SSLEngineResult.Status.CLOSED);
         doUnWrap(toEngine, to, net, SSLEngineResult.Status.CLOSED);
-        app = ByteBuffer.allocate(fromEngine.getSession().getApplicationBufferSize());
+        app = ByteBuffer.allocate(
+                fromEngine.getSession().getApplicationBufferSize());
         net = doWrap(toEngine, to, 0, app, SSLEngineResult.Status.CLOSED);
         doUnWrap(fromEngine, from, net, SSLEngineResult.Status.CLOSED);
         if (!toEngine.isInboundDone()) {
@@ -665,7 +673,8 @@
         }
         // Executing close inbound
         fromEngine.closeInbound();
-        app = ByteBuffer.allocate(fromEngine.getSession().getApplicationBufferSize());
+        app = ByteBuffer.allocate(
+                fromEngine.getSession().getApplicationBufferSize());
         net = doWrap(fromEngine, from, 0, app, SSLEngineResult.Status.CLOSED);
         doUnWrap(toEngine, to, net, SSLEngineResult.Status.CLOSED);
         if (!toEngine.isOutboundDone()) {
@@ -712,7 +721,8 @@
                 runTests(Ciphers.SUPPORTED_KRB_CIPHERS);
                 break;
             default:
-                throw new Error("Test error: unexpected test mode: " + TEST_MODE);
+                throw new Error(
+                        "Test error: unexpected test mode: " + TEST_MODE);
         }
     }
 
@@ -743,28 +753,36 @@
     }
 
     /**
-     * Returns SSLContext with TESTED_SECURITY_PROTOCOL protocol and sets up keys.
+     * Returns SSLContext with TESTED_SECURITY_PROTOCOL protocol and
+     * sets up keys.
      *
-     * @return - SSLContext with a protocol specified by TESTED_SECURITY_PROTOCOL.
+     * @return - SSLContext with a protocol specified by
+     *           TESTED_SECURITY_PROTOCOL.
      */
     public static SSLContext getContext() {
         try {
-            java.security.Security.setProperty("jdk.tls.disabledAlgorithms", "");
-            java.security.Security.setProperty("jdk.certpath.disabledAlgorithms", "");
+            java.security.Security.setProperty(
+                    "jdk.tls.disabledAlgorithms", "");
+            java.security.Security.setProperty(
+                    "jdk.certpath.disabledAlgorithms", "");
             KeyStore ks = KeyStore.getInstance("JKS");
             KeyStore ts = KeyStore.getInstance("JKS");
             char[] passphrase = PASSWD.toCharArray();
-            try (FileInputStream keyFileStream = new FileInputStream(KEY_FILE_NAME)) {
+            try (FileInputStream keyFileStream =
+                    new FileInputStream(KEY_FILE_NAME)) {
                 ks.load(keyFileStream, passphrase);
             }
-            try (FileInputStream trustFileStream = new FileInputStream(TRUST_FILE_NAME)) {
+            try (FileInputStream trustFileStream =
+                    new FileInputStream(TRUST_FILE_NAME)) {
                 ts.load(trustFileStream, passphrase);
             }
             KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
             kmf.init(ks, passphrase);
-            TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
+            TrustManagerFactory tmf =
+                    TrustManagerFactory.getInstance("SunX509");
             tmf.init(ts);
-            SSLContext sslCtx = SSLContext.getInstance(TESTED_SECURITY_PROTOCOL);
+            SSLContext sslCtx =
+                    SSLContext.getInstance(TESTED_SECURITY_PROTOCOL);
             sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
             return sslCtx;
         } catch (KeyStoreException | IOException | NoSuchAlgorithmException |
@@ -791,7 +809,8 @@
     }
 
     /**
-     * Sets up and starts kerberos KDC server if SSLEngineTestCase.TEST_MODE is "krb".
+     * Sets up and starts kerberos KDC server if
+     * SSLEngineTestCase.TEST_MODE is "krb".
      */
     public static void setUpAndStartKDCIfNeeded() {
         if (TEST_MODE.equals("krb")) {
@@ -806,7 +825,9 @@
      * @param useSNI  - flag used to enable or disable using SNI extension.
      *                Needed for Kerberos.
      */
-    public static SSLEngine getClientSSLEngine(SSLContext context, boolean useSNI) {
+    public static SSLEngine getClientSSLEngine(
+            SSLContext context, boolean useSNI) {
+
         SSLEngine clientEngine = context.createSSLEngine(HOST, 80);
         clientEngine.setUseClientMode(true);
         if (useSNI) {
@@ -827,7 +848,9 @@
      * @param useSNI  - flag used to enable or disable using SNI extension.
      *                Needed for Kerberos.
      */
-    public static SSLEngine getServerSSLEngine(SSLContext context, boolean useSNI) {
+    public static SSLEngine getServerSSLEngine(
+            SSLContext context, boolean useSNI) {
+
         SSLEngine serverEngine = context.createSSLEngine();
         serverEngine.setUseClientMode(false);
         if (useSNI) {
@@ -860,18 +883,20 @@
     protected int testSomeCiphers(Ciphers ciphers) {
         int failedNum = 0;
         String description = ciphers.description;
-        System.out.println("==================================================="
-                + "=========");
+        System.out.println("===============================================");
         System.out.println(description + " ciphers testing");
-        System.out.println("==================================================="
-                + "=========");
+        System.out.println("===========================================");
         for (String cs : ciphers.ciphers) {
-            System.out.println("-----------------------------------------------"
-                    + "-------------");
+            System.out.println("---------------------------------------");
             System.out.println("Testing cipher suite " + cs);
-            System.out.println("-----------------------------------------------"
-                    + "-------------");
+            System.out.println("---------------------------------------");
             Throwable error = null;
+
+            // Reset global mutable static variables
+            net = null;
+            doUnwrapForNotHandshakingStatus = false;
+            endHandshakeLoop = false;
+
             try {
                 testOneCipher(cs);
             } catch (Throwable t) {
@@ -894,8 +919,9 @@
                 case UNSUPPORTED_CIPHERS:
                     if (error == null) {
                         System.out.println("Test Failed: " + cs);
-                        System.err.println("Test for " + cs + " should have thrown"
-                                + " IllegalArgumentException, but it has not!");
+                        System.err.println("Test for " + cs +
+                                " should have thrown " +
+                                "IllegalArgumentException, but it has not!");
                         failedNum++;
                     } else if (!(error instanceof IllegalArgumentException)) {
                         System.out.println("Test Failed: " + cs);
@@ -911,6 +937,7 @@
                             + ciphers.name());
             }
         }
+
         return failedNum;
     }
 
@@ -919,20 +946,20 @@
      *
      * @param wrapingEngine         - Engine that is expected to wrap data.
      * @param unwrapingEngine       - Engine that is expected to unwrap data.
-     * @param maxPacketSize         - Maximum packet size for MFLN of zero for no limit.
+     * @param maxPacketSize         - Maximum packet size for MFLN of zero
+     *                                for no limit.
      * @param enableReplicatedPacks - Set {@code true} to enable replicated
-     *                              packet sending.
+     *                                packet sending.
      * @throws SSLException - thrown on engine errors.
      */
     private static void handshakeProcess(SSLEngine wrapingEngine,
-                                         SSLEngine unwrapingEngine,
-                                         int maxPacketSize,
-                                         boolean enableReplicatedPacks)
-            throws SSLException {
-        SSLEngineResult.HandshakeStatus wrapingHSStatus = wrapingEngine
-                .getHandshakeStatus();
-        SSLEngineResult.HandshakeStatus unwrapingHSStatus = unwrapingEngine
-                .getHandshakeStatus();
+            SSLEngine unwrapingEngine,
+            int maxPacketSize,
+            boolean enableReplicatedPacks) throws SSLException {
+
+        HandshakeStatus wrapingHSStatus = wrapingEngine.getHandshakeStatus();
+        HandshakeStatus unwrapingHSStatus =
+                unwrapingEngine.getHandshakeStatus();
         SSLEngineResult r;
         String wrapper, unwrapper;
         if (wrapingEngine.getUseClientMode()
@@ -946,6 +973,13 @@
         } else {
             throw new Error("Both engines are in the same mode");
         }
+        System.out.println(
+                wrapper + " handshake (wrap) status " + wrapingHSStatus);
+        System.out.println(
+                unwrapper + " handshake (unwrap) status " + unwrapingHSStatus);
+
+        ByteBuffer netReplicatedClient = null;
+        ByteBuffer netReplicatedServer = null;
         switch (wrapingHSStatus) {
             case NEED_WRAP:
                 if (enableReplicatedPacks) {
@@ -960,9 +994,11 @@
                         }
                     }
                 }
-                ByteBuffer app = ByteBuffer.allocate(wrapingEngine.getSession()
-                        .getApplicationBufferSize());
+                ByteBuffer app = ByteBuffer.allocate(
+                        wrapingEngine.getSession().getApplicationBufferSize());
                 net = doWrap(wrapingEngine, wrapper, maxPacketSize, app);
+                wrapingHSStatus = wrapingEngine.getHandshakeStatus();
+                // No break, falling into unwrapping.
             case NOT_HANDSHAKING:
                 switch (unwrapingHSStatus) {
                     case NEED_TASK:
@@ -970,12 +1006,12 @@
                     case NEED_UNWRAP:
                         doUnWrap(unwrapingEngine, unwrapper, net);
                         if (enableReplicatedPacks) {
-                            System.out.println("Unwrapping replicated packet...");
+                            System.out.println(unwrapper +
+                                    " unwrapping replicated packet...");
                             if (unwrapingEngine.getHandshakeStatus()
-                                    .equals(SSLEngineResult.HandshakeStatus.NEED_TASK)) {
+                                    .equals(HandshakeStatus.NEED_TASK)) {
                                 runDelegatedTasks(unwrapingEngine);
                             }
-                            runDelegatedTasks(unwrapingEngine);
                             ByteBuffer netReplicated;
                             if (unwrapingEngine.getUseClientMode()) {
                                 netReplicated = netReplicatedClient;
@@ -983,7 +1019,8 @@
                                 netReplicated = netReplicatedServer;
                             }
                             if (netReplicated != null) {
-                                doUnWrap(unwrapingEngine, unwrapper, netReplicated);
+                                doUnWrap(unwrapingEngine,
+                                        unwrapper, netReplicated);
                             } else {
                                 net.flip();
                                 doUnWrap(unwrapingEngine, unwrapper, net);
@@ -994,15 +1031,39 @@
                         break;
                     case NOT_HANDSHAKING:
                         if (doUnwrapForNotHandshakingStatus) {
+                            System.out.println("Not handshake status unwrap");
                             doUnWrap(unwrapingEngine, unwrapper, net);
                             doUnwrapForNotHandshakingStatus = false;
                             break;
                         } else {
+                            if (wrapingHSStatus ==
+                                        HandshakeStatus.NOT_HANDSHAKING) {
+                                System.out.println("Handshake is completed");
+                                endHandshakeLoop = true;
+                            }
+                        }
+                        break;
+                    case NEED_WRAP:
+                        SSLSession session = unwrapingEngine.getSession();
+                        int bufferSize = session.getApplicationBufferSize();
+                        ByteBuffer b = ByteBuffer.allocate(bufferSize);
+                        net = doWrap(unwrapingEngine,
+                                        unwrapper, maxPacketSize, b);
+                        unwrapingHSStatus =
+                                unwrapingEngine.getHandshakeStatus();
+                        if ((wrapingHSStatus ==
+                                    HandshakeStatus.NOT_HANDSHAKING) &&
+                            (unwrapingHSStatus ==
+                                    HandshakeStatus.NOT_HANDSHAKING)) {
+
+                            System.out.println("Handshake is completed");
                             endHandshakeLoop = true;
                         }
+
                         break;
                     default:
-                        throw new Error("Unexpected unwraping engine handshake status "
+                        throw new Error(
+                                "Unexpected unwraping engine handshake status "
                                 + unwrapingHSStatus.name());
                 }
                 break;
@@ -1027,8 +1088,8 @@
         while ((runnable = engine.getDelegatedTask()) != null) {
             runnable.run();
         }
-        SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
-        if (hs == SSLEngineResult.HandshakeStatus.NEED_TASK) {
+        HandshakeStatus hs = engine.getHandshakeStatus();
+        if (hs == HandshakeStatus.NEED_TASK) {
             throw new Error("Handshake shouldn't need additional tasks.");
         }
     }
--- a/test/sun/security/smartcardio/TestChannel.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/test/sun/security/smartcardio/TestChannel.java	Mon Oct 31 10:39:14 2016 -0700
@@ -23,11 +23,12 @@
 
 /*
  * @test
- * @bug 6239117
+ * @bug 6239117 8168851
  * @summary test logical channels work
  * @author Andreas Sterbenz
  * @modules java.smartcardio/javax.smartcardio
  * @run main/manual TestChannel
+ * @run main/othervm/manual/java.security.policy==test.policy TestChannel
  */
 
 // This test requires special hardware.
--- a/test/sun/security/smartcardio/TestControl.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/test/sun/security/smartcardio/TestControl.java	Mon Oct 31 10:39:14 2016 -0700
@@ -23,11 +23,12 @@
 
 /*
  * @test
- * @bug 6239117 6470320
+ * @bug 6239117 6470320 8168851
  * @summary test if transmitControlCommand() works
  * @author Andreas Sterbenz
  * @modules java.smartcardio/javax.smartcardio
  * @run main/manual TestControl
+ * @run main/othervm/manual/java.security.policy==test.policy TestControl
  */
 
 // This test requires special hardware.
--- a/test/sun/security/smartcardio/TestDefault.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/test/sun/security/smartcardio/TestDefault.java	Mon Oct 31 10:39:14 2016 -0700
@@ -23,11 +23,12 @@
 
 /*
  * @test
- * @bug 6327047
+ * @bug 6327047 8168851
  * @summary verify that TerminalFactory.getDefault() works
  * @author Andreas Sterbenz
  * @modules java.smartcardio/javax.smartcardio
  * @run main/manual TestDefault
+ * @run main/othervm/manual/java.security.policy==test.policy TestDefault
  */
 
 // This test requires special hardware.
--- a/test/sun/security/smartcardio/TestDirect.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/test/sun/security/smartcardio/TestDirect.java	Mon Oct 31 10:39:14 2016 -0700
@@ -23,10 +23,11 @@
 
 /*
  * @test
- * @bug 8046343
+ * @bug 8046343 8168851
  * @summary Make sure that direct protocol is available
  * @modules java.smartcardio/javax.smartcardio
  * @run main/manual TestDirect
+ * @run main/othervm/manual/java.security.policy==test.policy TestDirect
  */
 
 // This test requires special hardware.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/smartcardio/test.policy	Mon Oct 31 10:39:14 2016 -0700
@@ -0,0 +1,3 @@
+grant codebase "file:${test.classes}/*" {
+    permission javax.smartcardio.CardPermission "*", "connect,getBasicChannel,reset,transmitControl";
+};
--- a/test/sun/util/calendar/zi/tzdata/VERSION	Mon Oct 31 14:50:09 2016 +0300
+++ b/test/sun/util/calendar/zi/tzdata/VERSION	Mon Oct 31 10:39:14 2016 -0700
@@ -21,4 +21,4 @@
 # or visit www.oracle.com if you need additional information or have any
 # questions.
 #
-tzdata2016g
+tzdata2016h
--- a/test/sun/util/calendar/zi/tzdata/asia	Mon Oct 31 14:50:09 2016 +0300
+++ b/test/sun/util/calendar/zi/tzdata/asia	Mon Oct 31 10:39:14 2016 -0700
@@ -2567,11 +2567,6 @@
 # From Paul Eggert (2015-03-03):
 # http://www.timeanddate.com/time/change/west-bank/ramallah?year=2014
 # says that the fall 2014 transition was Oct 23 at 24:00.
-# For future dates, guess the last Friday in March at 24:00 through
-# the first Friday on or after October 21 at 00:00.  This is consistent with
-# the predictions in today's editions of the following URLs:
-# http://www.timeanddate.com/time/change/gaza-strip/gaza
-# http://www.timeanddate.com/time/change/west-bank/hebron
 
 # From Hannah Kreitem (2016-03-09):
 # http://www.palestinecabinet.gov.ps/WebSite/ar/ViewDetails?ID=31728
@@ -2581,7 +2576,21 @@
 #
 # From Paul Eggert (2016-03-12):
 # Predict spring transitions on March's last Saturday at 01:00 from now on.
-# Leave fall predictions alone for now.
+
+# From Sharef Mustafa (2016-10-19):
+# [T]he Palestinian cabinet decision (Mar 8th 2016) published on
+# http://www.palestinecabinet.gov.ps/WebSite/Upload/Decree/GOV_17/16032016134830.pdf
+# states that summer time will end on Oct 29th at 01:00.
+#
+# From Tim Parenti (2016-10-19):
+# Predict fall transitions on October's last Saturday at 01:00 from now on.
+# This is consistent with the 2016 transition as well as our spring
+# predictions.
+#
+# From Paul Eggert (2016-10-19):
+# It's also consistent with predictions in the following URLs today:
+# http://www.timeanddate.com/time/change/gaza-strip/gaza
+# http://www.timeanddate.com/time/change/west-bank/hebron
 
 # Rule	NAME	FROM	TO	TYPE	IN	ON	AT	SAVE	LETTER/S
 Rule EgyptAsia	1957	only	-	May	10	0:00	1:00	S
@@ -2610,9 +2619,10 @@
 Rule Palestine	2012	2014	-	Mar	lastThu	24:00	1:00	S
 Rule Palestine	2012	only	-	Sep	21	1:00	0	-
 Rule Palestine	2013	only	-	Sep	Fri>=21	0:00	0	-
-Rule Palestine	2014	max	-	Oct	Fri>=21	0:00	0	-
+Rule Palestine	2014	2015	-	Oct	Fri>=21	0:00	0	-
 Rule Palestine	2015	only	-	Mar	lastFri	24:00	1:00	S
 Rule Palestine	2016	max	-	Mar	lastSat	1:00	1:00	S
+Rule Palestine	2016	max	-	Oct	lastSat	1:00	0	-
 
 # Zone	NAME		GMTOFF	RULES	FORMAT	[UNTIL]
 Zone	Asia/Gaza	2:17:52	-	LMT	1900 Oct
@@ -2762,45 +2772,31 @@
 # People who live in regions under Tamil control can use [TZ='Asia/Kolkata'],
 # as that zone has agreed with the Tamil areas since our cutoff date of 1970.
 
-# From K Sethu (2006-04-25):
-# I think the abbreviation LKT originated from the world of computers at
-# the time of or subsequent to the time zone changes by SL Government
-# twice in 1996 and probably SL Government or its standardization
-# agencies never declared an abbreviation as a national standard.
+# From Sadika Sumanapala (2016-10-19):
+# According to http://www.sltime.org (maintained by Measurement Units,
+# Standards & Services Department, Sri Lanka) abbreviation for Sri Lanka
+# standard time is SLST.
 #
-# I recollect before the recent change the government announcements
-# mentioning it as simply changing Sri Lanka Standard Time or Sri Lanka
-# Time and no mention was made about the abbreviation.
-#
-# If we look at Sri Lanka Department of Government's "Official News
-# Website of Sri Lanka" ... http://www.news.lk/ we can see that they
-# use SLT as abbreviation in time stamp at the beginning of each news
-# item....
-#
-# Within Sri Lanka I think LKT is well known among computer users and
-# administrators.  In my opinion SLT may not be a good choice because the
-# nation's largest telcom / internet operator Sri Lanka Telcom is well
-# known by that abbreviation - simply as SLT (there IP domains are
-# slt.lk and sltnet.lk).
-#
-# But if indeed our government has adopted SLT as standard abbreviation
-# (that we have not known so far) then  it is better that it be used for
-# all computers.
-
-# From Paul Eggert (2006-04-25):
-# One possibility is that we wait for a bit for the dust to settle down
-# and then see what people actually say in practice.
+# From Paul Eggert (2016-10-18):
+# "SLST" seems to be reasonably recent and rarely-used outside time
+# zone nerd sources.  I searched Google News and found three uses of
+# it in the International Business Times of India in February and
+# March of this year when discussing cricket match times, but nothing
+# since then (though there has been a lot of cricket) and nothing in
+# other English-language news sources.  Our old abbreviation "LKT" is
+# even worse.  For now, let's use a numeric abbreviation; we can
+# switch to "SLST" if it catches on.
 
 # Zone	NAME		GMTOFF	RULES	FORMAT	[UNTIL]
 Zone	Asia/Colombo	5:19:24 -	LMT	1880
 			5:19:32	-	MMT	1906        # Moratuwa Mean Time
-			5:30	-	IST	1942 Jan  5
-			5:30	0:30	IHST	1942 Sep
-			5:30	1:00	IST	1945 Oct 16  2:00
-			5:30	-	IST	1996 May 25  0:00
-			6:30	-	LKT	1996 Oct 26  0:30
-			6:00	-	LKT	2006 Apr 15  0:30
-			5:30	-	IST
+			5:30	-	+0530	1942 Jan  5
+			5:30	0:30	+0530/+06 1942 Sep
+			5:30	1:00	+0530/+0630 1945 Oct 16  2:00
+			5:30	-	+0530	1996 May 25  0:00
+			6:30	-	+0630	1996 Oct 26  0:30
+			6:00	-	+06	2006 Apr 15  0:30
+			5:30	-	+0530
 
 # Syria
 # Rule	NAME	FROM	TO	TYPE	IN	ON	AT	SAVE	LETTER/S
--- a/test/sun/util/calendar/zi/tzdata/australasia	Mon Oct 31 14:50:09 2016 +0300
+++ b/test/sun/util/calendar/zi/tzdata/australasia	Mon Oct 31 10:39:14 2016 -0700
@@ -373,7 +373,13 @@
 # commencing at 2.00 am on Sunday 1st November, 2015 and ending at
 # 3.00 am on Sunday 17th January, 2016.
 
-# From Paul Eggert (2015-09-01):
+# From Raymond Kumar (2016-10-04):
+# http://www.fiji.gov.fj/Media-Center/Press-Releases/DAYLIGHT-SAVING-STARTS-ON-6th-NOVEMBER,-2016.aspx
+# "Fiji's daylight savings will begin on Sunday, 6 November 2016, when
+# clocks go forward an hour at 2am to 3am....  Daylight Saving will
+# end at 3.00am on Sunday 15th January 2017."
+
+# From Paul Eggert (2016-10-03):
 # For now, guess DST from 02:00 the first Sunday in November to
 # 03:00 the third Sunday in January.  Although ad hoc, it matches
 # transitions since late 2014 and seems more likely to match future
--- a/test/sun/util/calendar/zi/tzdata/europe	Mon Oct 31 14:50:09 2016 +0300
+++ b/test/sun/util/calendar/zi/tzdata/europe	Mon Oct 31 10:39:14 2016 -0700
@@ -1931,7 +1931,7 @@
 # Amsterdam mean time.
 
 # The data entries before 1945 are taken from
-# http://www.phys.uu.nl/~vgent/wettijd/wettijd.htm
+# http://www.staff.science.uu.nl/~gent0113/idl/idl.htm
 
 # Rule	NAME	FROM	TO	TYPE	IN	ON	AT	SAVE	LETTER/S
 Rule	Neth	1916	only	-	May	 1	0:00	1:00	NST	# Netherlands Summer Time
@@ -3450,22 +3450,24 @@
 
 # Turkey
 
-# From Amar Devegowda (2007-01-03):
-# The time zone rules for Istanbul, Turkey have not been changed for years now.
-# ... The latest rules are available at:
-# http://www.timeanddate.com/worldclock/timezone.html?n=107
-# From Steffen Thorsen (2007-01-03):
-# I have been able to find press records back to 1996 which all say that
-# DST started 01:00 local time and end at 02:00 local time.  I am not sure
-# what happened before that.  One example for each year from 1996 to 2001:
-# http://newspot.byegm.gov.tr/arsiv/1996/21/N4.htm
-# http://www.byegm.gov.tr/YAYINLARIMIZ/CHR/ING97/03/97X03X25.TXT
-# http://www.byegm.gov.tr/YAYINLARIMIZ/CHR/ING98/03/98X03X02.HTM
-# http://www.byegm.gov.tr/YAYINLARIMIZ/CHR/ING99/10/99X10X26.HTM#%2016
-# http://www.byegm.gov.tr/YAYINLARIMIZ/CHR/ING2000/03/00X03X06.HTM#%2021
-# http://www.byegm.gov.tr/YAYINLARIMIZ/CHR/ING2001/03/23x03x01.HTM#%2027
-# From Paul Eggert (2007-01-03):
-# Prefer the above source to Shanks & Pottenger for time stamps after 1990.
+# From Kıvanç Yazan (2016-09-25):
+# 1) For 1986-2006, DST started at 01:00 local and ended at 02:00 local, with
+#    no exceptions.
+# 2) 1994's lastSun was overridden with Mar 20 ...
+# Here are official papers:
+# http://www.resmigazete.gov.tr/arsiv/19032.pdf  - page 2 for 1986
+# http://www.resmigazete.gov.tr/arsiv/19400.pdf  - page 4 for 1987
+# http://www.resmigazete.gov.tr/arsiv/19752.pdf  - page 15 for 1988
+# http://www.resmigazete.gov.tr/arsiv/20102.pdf  - page 6 for 1989
+# http://www.resmigazete.gov.tr/arsiv/20464.pdf  - page 1 for 1990 - 1992
+# http://www.resmigazete.gov.tr/arsiv/21531.pdf  - page 15 for 1993 - 1995
+# http://www.resmigazete.gov.tr/arsiv/21879.pdf  - page 1 for overriding 1994
+# http://www.resmigazete.gov.tr/arsiv/22588.pdf  - page 1 for 1996, 1997
+# http://www.resmigazete.gov.tr/arsiv/23286.pdf  - page 10 for 1998 - 2000
+# http://www.resmigazete.gov.tr/eskiler/2001/03/20010324.htm#2  - for 2001
+# http://www.resmigazete.gov.tr/eskiler/2002/03/20020316.htm#2  - for 2002-2006
+# From Paul Eggert (2016-09-25):
+# Prefer the above sources to Shanks & Pottenger for time stamps after 1985.
 
 # From Steffen Thorsen (2007-03-09):
 # Starting 2007 though, it seems that they are adopting EU's 1:00 UTC
@@ -3574,10 +3576,10 @@
 Rule	Turkey	1983	only	-	Oct	 2	0:00	0	-
 Rule	Turkey	1985	only	-	Apr	20	0:00	1:00	S
 Rule	Turkey	1985	only	-	Sep	28	0:00	0	-
-Rule	Turkey	1986	1990	-	Mar	lastSun	2:00s	1:00	S
-Rule	Turkey	1986	1990	-	Sep	lastSun	2:00s	0	-
-Rule	Turkey	1991	2006	-	Mar	lastSun	1:00s	1:00	S
-Rule	Turkey	1991	1995	-	Sep	lastSun	1:00s	0	-
+Rule	Turkey	1986	1993	-	Mar	lastSun	1:00s	1:00	S
+Rule	Turkey	1986	1995	-	Sep	lastSun	1:00s	0	-
+Rule	Turkey	1994	only	-	Mar	20	1:00s	1:00	S
+Rule	Turkey	1995	2006	-	Mar	lastSun	1:00s	1:00	S
 Rule	Turkey	1996	2006	-	Oct	lastSun	1:00s	0	-
 # Zone	NAME		GMTOFF	RULES	FORMAT	[UNTIL]
 Zone	Europe/Istanbul	1:55:52 -	LMT	1880
--- a/test/sun/util/calendar/zi/tzdata/northamerica	Mon Oct 31 14:50:09 2016 +0300
+++ b/test/sun/util/calendar/zi/tzdata/northamerica	Mon Oct 31 10:39:14 2016 -0700
@@ -47,8 +47,32 @@
 # was the result of his proposals at the Convention of Railroad Trunk Lines
 # in New York City (1869-10).  His 1870 proposal was based on Washington, DC,
 # but in 1872-05 he moved the proposed origin to Greenwich.
-# His proposal was adopted by the railroads on 1883-11-18 at 12:00,
-# and the most of the country soon followed suit.
+
+# From Paul Eggert (2016-09-21):
+# Dowd's proposal left many details unresolved, such as where to draw
+# lines between time zones.  The key individual who made time zones
+# work in the US was William Frederick Allen - railway engineer,
+# managing editor of the Travelers' Guide, and secretary of the
+# General Time Convention, a railway standardization group.  Allen
+# spent months in dialogs with scientific and railway leaders,
+# developed a workable plan to institute time zones, and presented it
+# to the General Time Convention on 1883-04-11, saying that his plan
+# meant "local time would be practically abolished" - a plus for
+# railway scheduling.  By the next convention on 1883-10-11 nearly all
+# railroads had agreed and it took effect on 1883-11-18 at 12:00.
+# That Sunday was called the "day of two noons", as the eastern parts
+# of the new zones observed noon twice.  Allen witnessed the
+# transition in New York City, writing:
+#
+#   I heard the bells of St. Paul's strike on the old time.  Four
+#   minutes later, obedient to the electrical signal from the Naval
+#   Observatory ... the time-ball made its rapid descent, the chimes
+#   of old Trinity rang twelve measured strokes, and local time was
+#   abandoned, probably forever.
+#
+# Most of the US soon followed suit.  See:
+# Bartky IR. The adoption of standard time. Technol Cult 1989 Jan;30(1):25-56.
+# http://dx.doi.org/10.2307/3105430
 
 # From Paul Eggert (2005-04-16):
 # That 1883 transition occurred at 12:00 new time, not at 12:00 old time.
--- a/test/tools/jmod/hashes/HashesTest.java	Mon Oct 31 14:50:09 2016 +0300
+++ b/test/tools/jmod/hashes/HashesTest.java	Mon Oct 31 10:39:14 2016 -0700
@@ -26,7 +26,8 @@
  * @summary Test the recording and checking of module hashes
  * @author Andrei Eremeev
  * @library /lib/testlibrary
- * @modules java.base/jdk.internal.module
+ * @modules java.base/jdk.internal.misc
+ *          java.base/jdk.internal.module
  *          jdk.jlink
  *          jdk.compiler
  * @build CompilerUtils
@@ -39,7 +40,6 @@
 import java.lang.module.ModuleFinder;
 import java.lang.module.ModuleReader;
 import java.lang.module.ModuleReference;
-import java.lang.reflect.Method;
 import java.nio.file.FileVisitResult;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -55,8 +55,10 @@
 import java.util.spi.ToolProvider;
 import java.util.stream.Collectors;
 
-import jdk.internal.module.ConfigurableModuleFinder;
+import jdk.internal.misc.SharedSecrets;
+import jdk.internal.misc.JavaLangModuleAccess;
 import jdk.internal.module.ModuleHashes;
+
 import org.testng.annotations.BeforeTest;
 import org.testng.annotations.Test;
 
@@ -74,7 +76,6 @@
     private final Path jmods = Paths.get("jmods");
     private final String[] modules = new String[] { "m1", "m2", "m3"};
 
-    private static Method hashesMethod;
     @BeforeTest
     private void setup() throws Exception {
         if (Files.exists(jmods)) {
@@ -97,13 +98,6 @@
         // compile org.bar and org.foo
         compileModule("org.bar", modSrc);
         compileModule("org.foo", modSrc);
-
-        try {
-            hashesMethod = ModuleDescriptor.class.getDeclaredMethod("hashes");
-            hashesMethod.setAccessible(true);
-        } catch (ReflectiveOperationException x) {
-            throw new InternalError(x);
-        }
     }
 
     @Test
@@ -143,17 +137,14 @@
     }
 
     private Optional<ModuleHashes> hashes(String name) throws Exception {
-        ModuleFinder finder = ModuleFinder.of(jmods.resolve(name + ".jmod"));
-        if (finder instanceof ConfigurableModuleFinder) {
-            ((ConfigurableModuleFinder) finder)
-                .configurePhase(ConfigurableModuleFinder.Phase.LINK_TIME);
-        }
+        ModuleFinder finder = SharedSecrets.getJavaLangModuleAccess()
+            .newModulePath(Runtime.version(), true, jmods.resolve(name + ".jmod"));
         ModuleReference mref = finder.find(name).orElseThrow(RuntimeException::new);
         ModuleReader reader = mref.open();
         try (InputStream in = reader.open("module-info.class").get()) {
             ModuleDescriptor md = ModuleDescriptor.read(in);
-            Optional<ModuleHashes> hashes =
-                (Optional<ModuleHashes>) hashesMethod.invoke(md);
+            JavaLangModuleAccess jmla = SharedSecrets.getJavaLangModuleAccess();
+            Optional<ModuleHashes> hashes = jmla.hashes(md);
             System.out.format("hashes in module %s %s%n", name,
                               hashes.isPresent() ? "present" : "absent");
             if (hashes.isPresent()) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/launcher/modules/classpath/JavaClassPathTest.java	Mon Oct 31 10:39:14 2016 -0700
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2016, 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.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import jdk.testlibrary.OutputAnalyzer;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertTrue;
+import static jdk.testlibrary.ProcessTools.*;
+
+/**
+ * @test
+ * @bug 8168205
+ * @summary Test the default class path if -Djava.class.path is set
+ * @library /lib/testlibrary
+ * @modules jdk.compiler
+ * @build CompilerUtils jdk.testlibrary.*
+ * @run testng JavaClassPathTest
+ */
+
+public class JavaClassPathTest {
+    private static final Path SRC_DIR = Paths.get(System.getProperty("test.src"),
+                                                  "src");
+    private static final Path MODS_DIR = Paths.get("mods");
+    private static final String TEST_MODULE = "m";
+    private static final String TEST_MAIN = "jdk.test.Main";
+
+    @BeforeTest
+    public void setup() throws Exception {
+        boolean compiled = CompilerUtils.compile(SRC_DIR.resolve(TEST_MODULE),
+                                                 MODS_DIR.resolve(TEST_MODULE));
+        assertTrue(compiled, "module " + TEST_MODULE + " did not compile");
+
+        // add the class and a resource to the current working directory
+        Path file = Paths.get("jdk/test/Main.class");
+        Files.createDirectories(file.getParent());
+        Files.copy(MODS_DIR.resolve(TEST_MODULE).resolve(file), file);
+
+        Path res = Paths.get("jdk/test/res.properties");
+        Files.createFile(res);
+    }
+
+    @DataProvider(name = "classpath")
+    public Object[][] classpath() {
+        return new Object[][]{
+            // true indicates that class path default to current working directory
+            { "",                              true  },
+            { "-Djava.class.path",             true  },
+            { "-Djava.class.path=",            true  },
+            { "-Djava.class.path=.",           true  },
+        };
+    }
+
+    @Test(dataProvider = "classpath")
+    public void testUnnamedModule(String option, boolean expected) throws Throwable {
+        List<String> args = new ArrayList<>();
+        if (!option.isEmpty()) {
+            args.add(option);
+        }
+        args.add(TEST_MAIN);
+        args.add(Boolean.toString(expected));
+
+        assertTrue(execute(args).getExitValue() == 0);
+    }
+
+    @DataProvider(name = "moduleAndClassPath")
+    public Object[][] moduleAndClassPath() {
+        return new Object[][]{
+            // true indicates that class path default to current working directory
+            { "",                              false  },
+            { "-Djava.class.path",             false  },
+            { "-Djava.class.path=",            false  },
+            { "-Djava.class.path=.",           true   },
+        };
+    }
+
+    @Test(dataProvider = "moduleAndClassPath")
+    public void testNamedModule(String option, boolean expected) throws Throwable {
+        List<String> args = new ArrayList<>();
+        if (!option.isEmpty()) {
+            args.add(option);
+        }
+        args.add("--module-path");
+        args.add(MODS_DIR.toString());
+        args.add("-m");
+        args.add(TEST_MODULE + "/" + TEST_MAIN);
+        args.add(Boolean.toString(expected));
+
+        assertTrue(execute(args).getExitValue() == 0);
+    }
+
+    private OutputAnalyzer execute(List<String> options) throws Throwable {
+        ProcessBuilder pb =
+            createJavaProcessBuilder(options.toArray(new String[0]));
+        Map<String,String> env = pb.environment();
+        // remove CLASSPATH environment variable
+        String value = env.remove("CLASSPATH");
+        return executeCommand(pb)
+                    .outputTo(System.out)
+                    .errorTo(System.out);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/launcher/modules/classpath/src/m/jdk/test/Main.java	Mon Oct 31 10:39:14 2016 -0700
@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2016, 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 jdk.test;
+
+import java.net.URL;
+
+public class Main {
+    static final String JAVA_CLASS_PATH = "java.class.path";
+
+    public static void main(String[] args) throws Exception {
+        String value = System.getProperty(JAVA_CLASS_PATH);
+        if (value == null) {
+            throw new RuntimeException(JAVA_CLASS_PATH + " is expected non-null" +
+                " for compatibility");
+        }
+
+        boolean expected = args[0].equals("true");
+        ClassLoader loader = ClassLoader.getSystemClassLoader();
+        URL url = loader.getResource("jdk/test/res.properties");
+        if ((expected && url == null) || (!expected && url != null)) {
+            throw new RuntimeException("URL: " + url + " expected non-null: " + expected);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/launcher/modules/classpath/src/m/module-info.java	Mon Oct 31 10:39:14 2016 -0700
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+module m {
+}
+