--- a/jdk/.hgtags Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/.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/jdk/make/data/tzdata/VERSION Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/make/data/tzdata/asia Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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.
-#
-# 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.
+# 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.
#
-# 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/jdk/make/data/tzdata/australasia Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/make/data/tzdata/europe Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/make/data/tzdata/northamerica Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/src/java.base/share/classes/java/io/FilePermission.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/src/java.base/share/classes/java/lang/ClassLoader.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/src/java.base/share/classes/java/lang/module/ModuleFinder.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/src/java.base/share/classes/java/lang/module/ModulePath.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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("/");
- // 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('/', '.');
+ 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();
+ }
+
+ 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/jdk/src/java.base/share/classes/java/lang/module/ModuleReader.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/src/java.base/share/classes/java/lang/module/ModuleReferences.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/src/java.base/share/classes/java/lang/module/SystemModuleFinder.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/src/java.base/share/classes/java/util/Scanner.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/src/java.base/share/classes/jdk/internal/jmod/JmodFile.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/src/java.base/share/classes/jdk/internal/loader/ClassLoaders.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/src/java.base/share/classes/jdk/internal/loader/Loader.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/src/java.base/share/classes/jdk/internal/logger/DefaultLoggerFinder.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangModuleAccess.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/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/jdk/src/java.base/share/classes/jdk/internal/module/ModuleInfoWriter.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/src/java.base/share/classes/jdk/internal/module/ModulePatcher.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/src/java.base/share/classes/sun/launcher/resources/launcher.properties Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/src/java.base/share/classes/sun/security/ssl/DTLSInputRecord.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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);
+ }
+
+ 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) {
+
+ HandshakeFragment hsFrag = parseHandshakeMessage(
+ contentType, majorVersion, minorVersion,
+ recordEnS, recordEpoch, recordSeq, plaintextFragment);
- 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 (hsFrag == null) {
- // invalid, discard this record
+ 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;
-
- // The handshke fragment with the biggest record sequence number
- // in a flight, not counting the Finished message.
- HandshakeFragment lastHandshakeFragment = null;
+ DTLSReassembler(int handshakeEpoch) {
+ this.handshakeEpoch = handshakeEpoch;
+ this.nextRecordEpoch = handshakeEpoch;
- // 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;
}
- // Is it the first message of next flight?
- if ((flightTopMessageSeq == hsf.messageSeq) &&
- (hsf.fragmentOffset == 0) && (flightTopRecordSeq == -1)) {
+ // Clean up the retransmission messages if necessary.
+ cleanUpRetransmit(hsf);
- flightType = hsf.handshakeType;
- flightTopEpoch = hsf.recordEpoch;
- flightTopRecordSeq = hsf.recordSeq;
+ // Is it the first message of next flight?
+ //
+ // 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;
+ }
+
+ 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;
+ }
- if (!isAbbreviatedHandshake) {
- prevSessionID = getSessionID(hsf.fragment);
+ 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;
}
- }
+ } // 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;
+ }
+ }
+
+ 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;
}
- boolean isEmpty() {
+ // 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))) {
- return false;
- }
+ if (!hasCompleted(flightType)) {
+ if (debug != null && Debug.isOn("verbose")) {
+ Debug.log(
+ "The ClientKeyExchange or client Certificate " +
+ "message is not completed yet.");
+ }
- if (!hasFinisedMessage(bufferedFragments)) {
- // not yet got the ChangeCipherSpec/Finished messages
return false;
}
- if (flightType == HandshakeMessage.ht_client_key_exchange) {
- // single handshake message flight
- return true;
+ // 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;
+ }
}
- //
- // 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.
+ 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");
+ }
+
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/jdk/src/java.base/share/classes/sun/security/ssl/DTLSOutputRecord.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/src/java.base/share/classes/sun/security/ssl/DTLSRecord.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/src/java.base/share/classes/sun/security/ssl/Debug.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/src/java.base/share/classes/sun/security/ssl/OutputRecord.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/src/java.base/share/classes/sun/security/ssl/Plaintext.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/src/java.base/share/classes/sun/security/ssl/SSLEngineImpl.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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()) {
+
+ if (handshaker == null) {
+ hsStatus = HandshakeStatus.FINISHED;
+ } else if (handshaker.isDone()) {
+ hsStatus = finishHandshake();
+ connectionState = cs_DATA;
- 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/jdk/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/src/java.base/share/conf/security/java.security Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/src/java.base/share/lib/security/default.policy Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/src/java.base/share/native/launcher/defines.h Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/src/java.base/share/native/launcher/main.c Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/src/java.base/share/native/libjli/java.c Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/TaskHelper.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/src/jdk.jsobject/share/classes/netscape/javascript/JSObject.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/test/ProblemList.txt Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/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/jdk/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/jdk/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/jdk/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/jdk/test/java/lang/module/AutomaticModulesTest.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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 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 JAR file where the entry does not correspond to a
- * legal package name.
+ * 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/jdk/test/java/lang/module/ModuleFinderTest.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/test/java/lang/module/ModuleReader/ModuleReaderTest.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/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/jdk/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/jdk/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/jdk/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/jdk/test/java/util/ResourceBundle/modules/basic/src/mainbundles/jdk/test/resources/MyResourcesProvider.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/test/java/util/Scanner/ScanTest.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/test/javax/net/ssl/DTLS/DTLSOverDatagram.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/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/jdk/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/jdk/test/javax/net/ssl/TLSCommon/SSLEngineTestCase.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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 {
- endHandshakeLoop = true;
+ 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/jdk/test/sun/security/smartcardio/TestChannel.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/test/sun/security/smartcardio/TestControl.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/test/sun/security/smartcardio/TestDefault.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/test/sun/security/smartcardio/TestDirect.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/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/jdk/test/sun/util/calendar/zi/tzdata/VERSION Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/test/sun/util/calendar/zi/tzdata/asia Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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.
-#
-# 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.
+# 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.
#
-# 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/jdk/test/sun/util/calendar/zi/tzdata/australasia Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/test/sun/util/calendar/zi/tzdata/europe Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/test/sun/util/calendar/zi/tzdata/northamerica Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/test/tools/jmod/hashes/HashesTest.java Mon Oct 31 14:50:09 2016 +0300
+++ b/jdk/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/jdk/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/jdk/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/jdk/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 {
+}
+