Merge
This commit is contained in:
commit
a4b6e56bbf
@ -360,3 +360,4 @@ f900d5afd9c83a0df8f36161c27c5e4c86a66f4c jdk-9+111
|
||||
09617ce980b99d49abfd54dacfed353c47e2a115 jdk-9+115
|
||||
6743a8e0cab7b5f6f4a0575f6664892f0ab740af jdk-9+116
|
||||
e882bcdbdac436523f3d5681611d3118a3804ea7 jdk-9+117
|
||||
047f95de8f918d8ff5e8cd2636a2abb5c3c8adb8 jdk-9+118
|
||||
|
@ -768,7 +768,7 @@ AC_DEFUN([FLAGS_SETUP_COMPILER_FLAGS_FOR_JDK_HELPER],
|
||||
$2CFLAGS_JDK="${$2CFLAGS_JDK} -fno-strict-aliasing"
|
||||
;;
|
||||
esac
|
||||
TOOLCHAIN_CHECK_COMPILER_VERSION(VERSION: 6, IF_AT_LEAST: FLAGS_SETUP_GCC6_COMPILER_FLAGS)
|
||||
TOOLCHAIN_CHECK_COMPILER_VERSION(VERSION: 6, PREFIX: $2, IF_AT_LEAST: FLAGS_SETUP_GCC6_COMPILER_FLAGS)
|
||||
elif test "x$TOOLCHAIN_TYPE" = xclang; then
|
||||
$2JVM_CFLAGS="[$]$2JVM_CFLAGS -D_GNU_SOURCE"
|
||||
|
||||
@ -964,7 +964,7 @@ AC_DEFUN([FLAGS_SETUP_COMPILER_FLAGS_FOR_JDK_HELPER],
|
||||
-Wunused-value -Woverloaded-virtual"
|
||||
|
||||
if test "x$TOOLCHAIN_TYPE" = xgcc; then
|
||||
TOOLCHAIN_CHECK_COMPILER_VERSION(VERSION: [4.8],
|
||||
TOOLCHAIN_CHECK_COMPILER_VERSION(VERSION: [4.8], PREFIX: $2,
|
||||
IF_AT_LEAST: [
|
||||
# These flags either do not work or give spurious warnings prior to gcc 4.8.
|
||||
$2JVM_CFLAGS="[$]$2JVM_CFLAGS -Wno-format-zero-length -Wtype-limits -Wuninitialized"
|
||||
@ -1411,9 +1411,15 @@ AC_DEFUN_ONCE([FLAGS_SETUP_COMPILER_FLAGS_MISC],
|
||||
DISABLE_WARNING_PREFIX=
|
||||
fi
|
||||
CFLAGS_WARNINGS_ARE_ERRORS="-Werror"
|
||||
# Repeate the check for the BUILD_CC
|
||||
# Repeate the check for the BUILD_CC and BUILD_CXX. Need to also reset
|
||||
# CFLAGS since any target specific flags will likely not work with the
|
||||
# build compiler
|
||||
CC_OLD="$CC"
|
||||
CXX_OLD="$CXX"
|
||||
CC="$BUILD_CC"
|
||||
CXX="$BUILD_CXX"
|
||||
CFLAGS_OLD="$CFLAGS"
|
||||
CFLAGS=""
|
||||
FLAGS_COMPILER_CHECK_ARGUMENTS(ARGUMENT: [-Wno-this-is-a-warning-that-do-not-exist],
|
||||
IF_TRUE: [BUILD_CC_CAN_DISABLE_WARNINGS=true],
|
||||
IF_FALSE: [BUILD_CC_CAN_DISABLE_WARNINGS=false]
|
||||
@ -1424,6 +1430,8 @@ AC_DEFUN_ONCE([FLAGS_SETUP_COMPILER_FLAGS_MISC],
|
||||
BUILD_CC_DISABLE_WARNING_PREFIX=
|
||||
fi
|
||||
CC="$CC_OLD"
|
||||
CXX="$CXX_OLD"
|
||||
CFLAGS="$CFLAGS_OLD"
|
||||
;;
|
||||
clang)
|
||||
DISABLE_WARNING_PREFIX="-Wno-"
|
||||
|
@ -4900,6 +4900,8 @@ TOOLCHAIN_MINIMUM_VERSION_xlc=""
|
||||
|
||||
# Prepare the system so that TOOLCHAIN_CHECK_COMPILER_VERSION can be called.
|
||||
# Must have CC_VERSION_NUMBER and CXX_VERSION_NUMBER.
|
||||
# $1 - optional variable prefix for compiler and version variables (BUILD_)
|
||||
# $2 - optional variable prefix for comparable variable (OPENJDK_BUILD_)
|
||||
|
||||
|
||||
# Check if the configured compiler (C and C++) is of a specific version or
|
||||
@ -4909,6 +4911,7 @@ TOOLCHAIN_MINIMUM_VERSION_xlc=""
|
||||
# VERSION: The version string to check against the found version
|
||||
# IF_AT_LEAST: block to run if the compiler is at least this version (>=)
|
||||
# IF_OLDER_THAN: block to run if the compiler is older than this version (<)
|
||||
# PREFIX: Optional variable prefix for compiler to compare version for (OPENJDK_BUILD_)
|
||||
|
||||
|
||||
|
||||
@ -5073,7 +5076,7 @@ VS_SDK_PLATFORM_NAME_2013=
|
||||
#CUSTOM_AUTOCONF_INCLUDE
|
||||
|
||||
# Do not change or remove the following line, it is needed for consistency checks:
|
||||
DATE_WHEN_GENERATED=1462806878
|
||||
DATE_WHEN_GENERATED=1462970869
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
@ -34795,19 +34798,19 @@ ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
|
||||
|
||||
|
||||
if test "x$CC_VERSION_NUMBER" != "x$CXX_VERSION_NUMBER"; then
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: C and C++ compiler has different version numbers, $CC_VERSION_NUMBER vs $CXX_VERSION_NUMBER." >&5
|
||||
$as_echo "$as_me: WARNING: C and C++ compiler has different version numbers, $CC_VERSION_NUMBER vs $CXX_VERSION_NUMBER." >&2;}
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: C and C++ compiler have different version numbers, $CC_VERSION_NUMBER vs $CXX_VERSION_NUMBER." >&5
|
||||
$as_echo "$as_me: WARNING: C and C++ compiler have different version numbers, $CC_VERSION_NUMBER vs $CXX_VERSION_NUMBER." >&2;}
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: This typically indicates a broken setup, and is not supported" >&5
|
||||
$as_echo "$as_me: WARNING: This typically indicates a broken setup, and is not supported" >&2;}
|
||||
fi
|
||||
|
||||
# We only check CC_VERSION_NUMBER since we assume CXX_VERSION_NUMBER is equal.
|
||||
if [[ "$CC_VERSION_NUMBER" =~ (.*\.){3} ]] ; then
|
||||
if [[ "[$]CC_VERSION_NUMBER" =~ (.*\.){3} ]] ; then
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: C compiler version number has more than three parts (X.Y.Z): $CC_VERSION_NUMBER. Comparisons might be wrong." >&5
|
||||
$as_echo "$as_me: WARNING: C compiler version number has more than three parts (X.Y.Z): $CC_VERSION_NUMBER. Comparisons might be wrong." >&2;}
|
||||
fi
|
||||
|
||||
if [[ "$CC_VERSION_NUMBER" =~ [0-9]{6} ]] ; then
|
||||
if [[ "[$]CC_VERSION_NUMBER" =~ [0-9]{6} ]] ; then
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: C compiler version number has a part larger than 99999: $CC_VERSION_NUMBER. Comparisons might be wrong." >&5
|
||||
$as_echo "$as_me: WARNING: C compiler version number has a part larger than 99999: $CC_VERSION_NUMBER. Comparisons might be wrong." >&2;}
|
||||
fi
|
||||
@ -34850,6 +34853,13 @@ $as_echo "$as_me: WARNING: C compiler version number has a part larger than 9999
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -34897,6 +34907,8 @@ $as_echo "$as_me: WARNING: You are using $TOOLCHAIN_TYPE older than $TOOLCHAIN_M
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
fi
|
||||
|
||||
#
|
||||
@ -46475,6 +46487,268 @@ $as_echo "$as_me: Rewriting BUILD_STRIP to \"$new_complete\"" >&6;}
|
||||
BUILD_LDCXX="$BUILD_CXX"
|
||||
|
||||
PATH="$OLDPATH"
|
||||
|
||||
|
||||
COMPILER=$BUILD_CC
|
||||
COMPILER_NAME=BuildC
|
||||
|
||||
if test "x$TOOLCHAIN_TYPE" = xsolstudio; then
|
||||
# cc -V output typically looks like
|
||||
# cc: Sun C 5.12 Linux_i386 2011/11/16
|
||||
COMPILER_VERSION_OUTPUT=`$COMPILER -V 2>&1`
|
||||
# Check that this is likely to be the Solaris Studio cc.
|
||||
$ECHO "$COMPILER_VERSION_OUTPUT" | $GREP "^.*: Sun $COMPILER_NAME" > /dev/null
|
||||
if test $? -ne 0; then
|
||||
ALT_VERSION_OUTPUT=`$COMPILER --version 2>&1`
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: The $COMPILER_NAME compiler (located as $COMPILER) does not seem to be the required $TOOLCHAIN_TYPE compiler." >&5
|
||||
$as_echo "$as_me: The $COMPILER_NAME compiler (located as $COMPILER) does not seem to be the required $TOOLCHAIN_TYPE compiler." >&6;}
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: The result from running with -V was: \"$COMPILER_VERSION_OUTPUT\"" >&5
|
||||
$as_echo "$as_me: The result from running with -V was: \"$COMPILER_VERSION_OUTPUT\"" >&6;}
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: The result from running with --version was: \"$ALT_VERSION_OUTPUT\"" >&5
|
||||
$as_echo "$as_me: The result from running with --version was: \"$ALT_VERSION_OUTPUT\"" >&6;}
|
||||
as_fn_error $? "A $TOOLCHAIN_TYPE compiler is required. Try setting --with-tools-dir." "$LINENO" 5
|
||||
fi
|
||||
# Remove usage instructions (if present), and
|
||||
# collapse compiler output into a single line
|
||||
COMPILER_VERSION_STRING=`$ECHO $COMPILER_VERSION_OUTPUT | \
|
||||
$SED -e 's/ *[Uu]sage:.*//'`
|
||||
COMPILER_VERSION_NUMBER=`$ECHO $COMPILER_VERSION_OUTPUT | \
|
||||
$SED -e "s/^.*[ ,\t]$COMPILER_NAME[ ,\t]\([1-9]\.[0-9][0-9]*\).*/\1/"`
|
||||
elif test "x$TOOLCHAIN_TYPE" = xxlc; then
|
||||
# xlc -qversion output typically looks like
|
||||
# IBM XL C/C++ for AIX, V11.1 (5724-X13)
|
||||
# Version: 11.01.0000.0015
|
||||
COMPILER_VERSION_OUTPUT=`$COMPILER -qversion 2>&1`
|
||||
# Check that this is likely to be the IBM XL C compiler.
|
||||
$ECHO "$COMPILER_VERSION_OUTPUT" | $GREP "IBM XL C" > /dev/null
|
||||
if test $? -ne 0; then
|
||||
ALT_VERSION_OUTPUT=`$COMPILER --version 2>&1`
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: The $COMPILER_NAME compiler (located as $COMPILER) does not seem to be the required $TOOLCHAIN_TYPE compiler." >&5
|
||||
$as_echo "$as_me: The $COMPILER_NAME compiler (located as $COMPILER) does not seem to be the required $TOOLCHAIN_TYPE compiler." >&6;}
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: The result from running with -qversion was: \"$COMPILER_VERSION_OUTPUT\"" >&5
|
||||
$as_echo "$as_me: The result from running with -qversion was: \"$COMPILER_VERSION_OUTPUT\"" >&6;}
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: The result from running with --version was: \"$ALT_VERSION_OUTPUT\"" >&5
|
||||
$as_echo "$as_me: The result from running with --version was: \"$ALT_VERSION_OUTPUT\"" >&6;}
|
||||
as_fn_error $? "A $TOOLCHAIN_TYPE compiler is required. Try setting --with-tools-dir." "$LINENO" 5
|
||||
fi
|
||||
# Collapse compiler output into a single line
|
||||
COMPILER_VERSION_STRING=`$ECHO $COMPILER_VERSION_OUTPUT`
|
||||
COMPILER_VERSION_NUMBER=`$ECHO $COMPILER_VERSION_OUTPUT | \
|
||||
$SED -e 's/^.*, V\([1-9][0-9.]*\).*$/\1/'`
|
||||
elif test "x$TOOLCHAIN_TYPE" = xmicrosoft; then
|
||||
# There is no specific version flag, but all output starts with a version string.
|
||||
# First line typically looks something like:
|
||||
# Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
|
||||
COMPILER_VERSION_OUTPUT=`$COMPILER 2>&1 | $HEAD -n 1 | $TR -d '\r'`
|
||||
# Check that this is likely to be Microsoft CL.EXE.
|
||||
$ECHO "$COMPILER_VERSION_OUTPUT" | $GREP "Microsoft.*Compiler" > /dev/null
|
||||
if test $? -ne 0; then
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: The $COMPILER_NAME compiler (located as $COMPILER) does not seem to be the required $TOOLCHAIN_TYPE compiler." >&5
|
||||
$as_echo "$as_me: The $COMPILER_NAME compiler (located as $COMPILER) does not seem to be the required $TOOLCHAIN_TYPE compiler." >&6;}
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: The result from running it was: \"$COMPILER_VERSION_OUTPUT\"" >&5
|
||||
$as_echo "$as_me: The result from running it was: \"$COMPILER_VERSION_OUTPUT\"" >&6;}
|
||||
as_fn_error $? "A $TOOLCHAIN_TYPE compiler is required. Try setting --with-tools-dir." "$LINENO" 5
|
||||
fi
|
||||
# Collapse compiler output into a single line
|
||||
COMPILER_VERSION_STRING=`$ECHO $COMPILER_VERSION_OUTPUT`
|
||||
COMPILER_VERSION_NUMBER=`$ECHO $COMPILER_VERSION_OUTPUT | \
|
||||
$SED -e 's/^.*ersion.\([1-9][0-9.]*\) .*$/\1/'`
|
||||
elif test "x$TOOLCHAIN_TYPE" = xgcc; then
|
||||
# gcc --version output typically looks like
|
||||
# gcc (Ubuntu/Linaro 4.8.1-10ubuntu9) 4.8.1
|
||||
# Copyright (C) 2013 Free Software Foundation, Inc.
|
||||
# This is free software; see the source for copying conditions. There is NO
|
||||
# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
COMPILER_VERSION_OUTPUT=`$COMPILER --version 2>&1`
|
||||
# Check that this is likely to be GCC.
|
||||
$ECHO "$COMPILER_VERSION_OUTPUT" | $GREP "Free Software Foundation" > /dev/null
|
||||
if test $? -ne 0; then
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: The $COMPILER_NAME compiler (located as $COMPILER) does not seem to be the required $TOOLCHAIN_TYPE compiler." >&5
|
||||
$as_echo "$as_me: The $COMPILER_NAME compiler (located as $COMPILER) does not seem to be the required $TOOLCHAIN_TYPE compiler." >&6;}
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: The result from running with --version was: \"$COMPILER_VERSION\"" >&5
|
||||
$as_echo "$as_me: The result from running with --version was: \"$COMPILER_VERSION\"" >&6;}
|
||||
as_fn_error $? "A $TOOLCHAIN_TYPE compiler is required. Try setting --with-tools-dir." "$LINENO" 5
|
||||
fi
|
||||
# Remove Copyright and legalese from version string, and
|
||||
# collapse into a single line
|
||||
COMPILER_VERSION_STRING=`$ECHO $COMPILER_VERSION_OUTPUT | \
|
||||
$SED -e 's/ *Copyright .*//'`
|
||||
COMPILER_VERSION_NUMBER=`$ECHO $COMPILER_VERSION_OUTPUT | \
|
||||
$SED -e 's/^.* \([1-9]\.[0-9.]*\)[^0-9.].*$/\1/'`
|
||||
elif test "x$TOOLCHAIN_TYPE" = xclang; then
|
||||
# clang --version output typically looks like
|
||||
# Apple LLVM version 5.0 (clang-500.2.79) (based on LLVM 3.3svn)
|
||||
# clang version 3.3 (tags/RELEASE_33/final)
|
||||
# or
|
||||
# Debian clang version 3.2-7ubuntu1 (tags/RELEASE_32/final) (based on LLVM 3.2)
|
||||
# Target: x86_64-pc-linux-gnu
|
||||
# Thread model: posix
|
||||
COMPILER_VERSION_OUTPUT=`$COMPILER --version 2>&1`
|
||||
# Check that this is likely to be clang
|
||||
$ECHO "$COMPILER_VERSION_OUTPUT" | $GREP "clang" > /dev/null
|
||||
if test $? -ne 0; then
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: The $COMPILER_NAME compiler (located as $COMPILER) does not seem to be the required $TOOLCHAIN_TYPE compiler." >&5
|
||||
$as_echo "$as_me: The $COMPILER_NAME compiler (located as $COMPILER) does not seem to be the required $TOOLCHAIN_TYPE compiler." >&6;}
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: The result from running with --version was: \"$COMPILER_VERSION_OUTPUT\"" >&5
|
||||
$as_echo "$as_me: The result from running with --version was: \"$COMPILER_VERSION_OUTPUT\"" >&6;}
|
||||
as_fn_error $? "A $TOOLCHAIN_TYPE compiler is required. Try setting --with-tools-dir." "$LINENO" 5
|
||||
fi
|
||||
# Collapse compiler output into a single line
|
||||
COMPILER_VERSION_STRING=`$ECHO $COMPILER_VERSION_OUTPUT`
|
||||
COMPILER_VERSION_NUMBER=`$ECHO $COMPILER_VERSION_OUTPUT | \
|
||||
$SED -e 's/^.* version \([1-9][0-9.]*\).*$/\1/'`
|
||||
else
|
||||
as_fn_error $? "Unknown toolchain type $TOOLCHAIN_TYPE." "$LINENO" 5
|
||||
fi
|
||||
# This sets CC_VERSION_NUMBER or CXX_VERSION_NUMBER. (This comment is a grep marker)
|
||||
BUILD_CC_VERSION_NUMBER="$COMPILER_VERSION_NUMBER"
|
||||
# This sets CC_VERSION_STRING or CXX_VERSION_STRING. (This comment is a grep marker)
|
||||
BUILD_CC_VERSION_STRING="$COMPILER_VERSION_STRING"
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: Using $TOOLCHAIN_TYPE $COMPILER_NAME compiler version $COMPILER_VERSION_NUMBER [$COMPILER_VERSION_STRING]" >&5
|
||||
$as_echo "$as_me: Using $TOOLCHAIN_TYPE $COMPILER_NAME compiler version $COMPILER_VERSION_NUMBER [$COMPILER_VERSION_STRING]" >&6;}
|
||||
|
||||
|
||||
COMPILER=$BUILD_CXX
|
||||
COMPILER_NAME=BuildC++
|
||||
|
||||
if test "x$TOOLCHAIN_TYPE" = xsolstudio; then
|
||||
# cc -V output typically looks like
|
||||
# cc: Sun C 5.12 Linux_i386 2011/11/16
|
||||
COMPILER_VERSION_OUTPUT=`$COMPILER -V 2>&1`
|
||||
# Check that this is likely to be the Solaris Studio cc.
|
||||
$ECHO "$COMPILER_VERSION_OUTPUT" | $GREP "^.*: Sun $COMPILER_NAME" > /dev/null
|
||||
if test $? -ne 0; then
|
||||
ALT_VERSION_OUTPUT=`$COMPILER --version 2>&1`
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: The $COMPILER_NAME compiler (located as $COMPILER) does not seem to be the required $TOOLCHAIN_TYPE compiler." >&5
|
||||
$as_echo "$as_me: The $COMPILER_NAME compiler (located as $COMPILER) does not seem to be the required $TOOLCHAIN_TYPE compiler." >&6;}
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: The result from running with -V was: \"$COMPILER_VERSION_OUTPUT\"" >&5
|
||||
$as_echo "$as_me: The result from running with -V was: \"$COMPILER_VERSION_OUTPUT\"" >&6;}
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: The result from running with --version was: \"$ALT_VERSION_OUTPUT\"" >&5
|
||||
$as_echo "$as_me: The result from running with --version was: \"$ALT_VERSION_OUTPUT\"" >&6;}
|
||||
as_fn_error $? "A $TOOLCHAIN_TYPE compiler is required. Try setting --with-tools-dir." "$LINENO" 5
|
||||
fi
|
||||
# Remove usage instructions (if present), and
|
||||
# collapse compiler output into a single line
|
||||
COMPILER_VERSION_STRING=`$ECHO $COMPILER_VERSION_OUTPUT | \
|
||||
$SED -e 's/ *[Uu]sage:.*//'`
|
||||
COMPILER_VERSION_NUMBER=`$ECHO $COMPILER_VERSION_OUTPUT | \
|
||||
$SED -e "s/^.*[ ,\t]$COMPILER_NAME[ ,\t]\([1-9]\.[0-9][0-9]*\).*/\1/"`
|
||||
elif test "x$TOOLCHAIN_TYPE" = xxlc; then
|
||||
# xlc -qversion output typically looks like
|
||||
# IBM XL C/C++ for AIX, V11.1 (5724-X13)
|
||||
# Version: 11.01.0000.0015
|
||||
COMPILER_VERSION_OUTPUT=`$COMPILER -qversion 2>&1`
|
||||
# Check that this is likely to be the IBM XL C compiler.
|
||||
$ECHO "$COMPILER_VERSION_OUTPUT" | $GREP "IBM XL C" > /dev/null
|
||||
if test $? -ne 0; then
|
||||
ALT_VERSION_OUTPUT=`$COMPILER --version 2>&1`
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: The $COMPILER_NAME compiler (located as $COMPILER) does not seem to be the required $TOOLCHAIN_TYPE compiler." >&5
|
||||
$as_echo "$as_me: The $COMPILER_NAME compiler (located as $COMPILER) does not seem to be the required $TOOLCHAIN_TYPE compiler." >&6;}
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: The result from running with -qversion was: \"$COMPILER_VERSION_OUTPUT\"" >&5
|
||||
$as_echo "$as_me: The result from running with -qversion was: \"$COMPILER_VERSION_OUTPUT\"" >&6;}
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: The result from running with --version was: \"$ALT_VERSION_OUTPUT\"" >&5
|
||||
$as_echo "$as_me: The result from running with --version was: \"$ALT_VERSION_OUTPUT\"" >&6;}
|
||||
as_fn_error $? "A $TOOLCHAIN_TYPE compiler is required. Try setting --with-tools-dir." "$LINENO" 5
|
||||
fi
|
||||
# Collapse compiler output into a single line
|
||||
COMPILER_VERSION_STRING=`$ECHO $COMPILER_VERSION_OUTPUT`
|
||||
COMPILER_VERSION_NUMBER=`$ECHO $COMPILER_VERSION_OUTPUT | \
|
||||
$SED -e 's/^.*, V\([1-9][0-9.]*\).*$/\1/'`
|
||||
elif test "x$TOOLCHAIN_TYPE" = xmicrosoft; then
|
||||
# There is no specific version flag, but all output starts with a version string.
|
||||
# First line typically looks something like:
|
||||
# Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
|
||||
COMPILER_VERSION_OUTPUT=`$COMPILER 2>&1 | $HEAD -n 1 | $TR -d '\r'`
|
||||
# Check that this is likely to be Microsoft CL.EXE.
|
||||
$ECHO "$COMPILER_VERSION_OUTPUT" | $GREP "Microsoft.*Compiler" > /dev/null
|
||||
if test $? -ne 0; then
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: The $COMPILER_NAME compiler (located as $COMPILER) does not seem to be the required $TOOLCHAIN_TYPE compiler." >&5
|
||||
$as_echo "$as_me: The $COMPILER_NAME compiler (located as $COMPILER) does not seem to be the required $TOOLCHAIN_TYPE compiler." >&6;}
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: The result from running it was: \"$COMPILER_VERSION_OUTPUT\"" >&5
|
||||
$as_echo "$as_me: The result from running it was: \"$COMPILER_VERSION_OUTPUT\"" >&6;}
|
||||
as_fn_error $? "A $TOOLCHAIN_TYPE compiler is required. Try setting --with-tools-dir." "$LINENO" 5
|
||||
fi
|
||||
# Collapse compiler output into a single line
|
||||
COMPILER_VERSION_STRING=`$ECHO $COMPILER_VERSION_OUTPUT`
|
||||
COMPILER_VERSION_NUMBER=`$ECHO $COMPILER_VERSION_OUTPUT | \
|
||||
$SED -e 's/^.*ersion.\([1-9][0-9.]*\) .*$/\1/'`
|
||||
elif test "x$TOOLCHAIN_TYPE" = xgcc; then
|
||||
# gcc --version output typically looks like
|
||||
# gcc (Ubuntu/Linaro 4.8.1-10ubuntu9) 4.8.1
|
||||
# Copyright (C) 2013 Free Software Foundation, Inc.
|
||||
# This is free software; see the source for copying conditions. There is NO
|
||||
# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
COMPILER_VERSION_OUTPUT=`$COMPILER --version 2>&1`
|
||||
# Check that this is likely to be GCC.
|
||||
$ECHO "$COMPILER_VERSION_OUTPUT" | $GREP "Free Software Foundation" > /dev/null
|
||||
if test $? -ne 0; then
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: The $COMPILER_NAME compiler (located as $COMPILER) does not seem to be the required $TOOLCHAIN_TYPE compiler." >&5
|
||||
$as_echo "$as_me: The $COMPILER_NAME compiler (located as $COMPILER) does not seem to be the required $TOOLCHAIN_TYPE compiler." >&6;}
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: The result from running with --version was: \"$COMPILER_VERSION\"" >&5
|
||||
$as_echo "$as_me: The result from running with --version was: \"$COMPILER_VERSION\"" >&6;}
|
||||
as_fn_error $? "A $TOOLCHAIN_TYPE compiler is required. Try setting --with-tools-dir." "$LINENO" 5
|
||||
fi
|
||||
# Remove Copyright and legalese from version string, and
|
||||
# collapse into a single line
|
||||
COMPILER_VERSION_STRING=`$ECHO $COMPILER_VERSION_OUTPUT | \
|
||||
$SED -e 's/ *Copyright .*//'`
|
||||
COMPILER_VERSION_NUMBER=`$ECHO $COMPILER_VERSION_OUTPUT | \
|
||||
$SED -e 's/^.* \([1-9]\.[0-9.]*\)[^0-9.].*$/\1/'`
|
||||
elif test "x$TOOLCHAIN_TYPE" = xclang; then
|
||||
# clang --version output typically looks like
|
||||
# Apple LLVM version 5.0 (clang-500.2.79) (based on LLVM 3.3svn)
|
||||
# clang version 3.3 (tags/RELEASE_33/final)
|
||||
# or
|
||||
# Debian clang version 3.2-7ubuntu1 (tags/RELEASE_32/final) (based on LLVM 3.2)
|
||||
# Target: x86_64-pc-linux-gnu
|
||||
# Thread model: posix
|
||||
COMPILER_VERSION_OUTPUT=`$COMPILER --version 2>&1`
|
||||
# Check that this is likely to be clang
|
||||
$ECHO "$COMPILER_VERSION_OUTPUT" | $GREP "clang" > /dev/null
|
||||
if test $? -ne 0; then
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: The $COMPILER_NAME compiler (located as $COMPILER) does not seem to be the required $TOOLCHAIN_TYPE compiler." >&5
|
||||
$as_echo "$as_me: The $COMPILER_NAME compiler (located as $COMPILER) does not seem to be the required $TOOLCHAIN_TYPE compiler." >&6;}
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: The result from running with --version was: \"$COMPILER_VERSION_OUTPUT\"" >&5
|
||||
$as_echo "$as_me: The result from running with --version was: \"$COMPILER_VERSION_OUTPUT\"" >&6;}
|
||||
as_fn_error $? "A $TOOLCHAIN_TYPE compiler is required. Try setting --with-tools-dir." "$LINENO" 5
|
||||
fi
|
||||
# Collapse compiler output into a single line
|
||||
COMPILER_VERSION_STRING=`$ECHO $COMPILER_VERSION_OUTPUT`
|
||||
COMPILER_VERSION_NUMBER=`$ECHO $COMPILER_VERSION_OUTPUT | \
|
||||
$SED -e 's/^.* version \([1-9][0-9.]*\).*$/\1/'`
|
||||
else
|
||||
as_fn_error $? "Unknown toolchain type $TOOLCHAIN_TYPE." "$LINENO" 5
|
||||
fi
|
||||
# This sets CC_VERSION_NUMBER or CXX_VERSION_NUMBER. (This comment is a grep marker)
|
||||
BUILD_CXX_VERSION_NUMBER="$COMPILER_VERSION_NUMBER"
|
||||
# This sets CC_VERSION_STRING or CXX_VERSION_STRING. (This comment is a grep marker)
|
||||
BUILD_CXX_VERSION_STRING="$COMPILER_VERSION_STRING"
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: Using $TOOLCHAIN_TYPE $COMPILER_NAME compiler version $COMPILER_VERSION_NUMBER [$COMPILER_VERSION_STRING]" >&5
|
||||
$as_echo "$as_me: Using $TOOLCHAIN_TYPE $COMPILER_NAME compiler version $COMPILER_VERSION_NUMBER [$COMPILER_VERSION_STRING]" >&6;}
|
||||
|
||||
|
||||
if test "x$BUILD_CC_VERSION_NUMBER" != "x$BUILD_CXX_VERSION_NUMBER"; then
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: C and C++ compiler have different version numbers, $BUILD_CC_VERSION_NUMBER vs $BUILD_CXX_VERSION_NUMBER." >&5
|
||||
$as_echo "$as_me: WARNING: C and C++ compiler have different version numbers, $BUILD_CC_VERSION_NUMBER vs $BUILD_CXX_VERSION_NUMBER." >&2;}
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: This typically indicates a broken setup, and is not supported" >&5
|
||||
$as_echo "$as_me: WARNING: This typically indicates a broken setup, and is not supported" >&2;}
|
||||
fi
|
||||
|
||||
# We only check CC_VERSION_NUMBER since we assume CXX_VERSION_NUMBER is equal.
|
||||
if [[ "[$]BUILD_CC_VERSION_NUMBER" =~ (.*\.){3} ]] ; then
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: C compiler version number has more than three parts (X.Y.Z): $BUILD_CC_VERSION_NUMBER. Comparisons might be wrong." >&5
|
||||
$as_echo "$as_me: WARNING: C compiler version number has more than three parts (X.Y.Z): $BUILD_CC_VERSION_NUMBER. Comparisons might be wrong." >&2;}
|
||||
fi
|
||||
|
||||
if [[ "[$]BUILD_CC_VERSION_NUMBER" =~ [0-9]{6} ]] ; then
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: C compiler version number has a part larger than 99999: $BUILD_CC_VERSION_NUMBER. Comparisons might be wrong." >&5
|
||||
$as_echo "$as_me: WARNING: C compiler version number has a part larger than 99999: $BUILD_CC_VERSION_NUMBER. Comparisons might be wrong." >&2;}
|
||||
fi
|
||||
|
||||
OPENJDK_BUILD_COMPARABLE_ACTUAL_VERSION=`$AWK -F. '{ printf("%05d%05d%05d\n", $1, $2, $3) }' <<< "$BUILD_CC_VERSION_NUMBER"`
|
||||
|
||||
else
|
||||
# If we are not cross compiling, use the normal target compilers for
|
||||
# building the build platform executables.
|
||||
@ -49122,6 +49396,18 @@ $as_echo "$supports" >&6; }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -49711,6 +49997,8 @@ $as_echo "$supports" >&6; }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
elif test "x$TOOLCHAIN_TYPE" = xclang; then
|
||||
JVM_CFLAGS="$JVM_CFLAGS -D_GNU_SOURCE"
|
||||
|
||||
@ -49935,6 +50223,18 @@ $as_echo "$supports" >&6; }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -49987,6 +50287,8 @@ $as_echo "$supports" >&6; }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
fi
|
||||
if ! [[ " $JVM_VARIANTS " =~ " zero " ]] && ! [[ " $JVM_VARIANTS " =~ " zeroshark " ]] ; then
|
||||
# Non-zero builds have stricter warnings
|
||||
@ -50440,6 +50742,18 @@ $as_echo "$supports" >&6; }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -50469,7 +50783,7 @@ $as_echo "$supports" >&6; }
|
||||
# Version comparison method inspired by http://stackoverflow.com/a/24067243
|
||||
COMPARABLE_REFERENCE_VERSION=`$AWK -F. '{ printf("%05d%05d%05d\n", $1, $2, $3) }' <<< "$REFERENCE_VERSION"`
|
||||
|
||||
if test $COMPARABLE_ACTUAL_VERSION -ge $COMPARABLE_REFERENCE_VERSION ; then
|
||||
if test $OPENJDK_BUILD_COMPARABLE_ACTUAL_VERSION -ge $COMPARABLE_REFERENCE_VERSION ; then
|
||||
:
|
||||
|
||||
else
|
||||
@ -50488,6 +50802,8 @@ $as_echo "$supports" >&6; }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
elif test "x$TOOLCHAIN_TYPE" = xclang; then
|
||||
OPENJDK_BUILD_JVM_CFLAGS="$OPENJDK_BUILD_JVM_CFLAGS -D_GNU_SOURCE"
|
||||
|
||||
@ -50712,6 +51028,18 @@ $as_echo "$supports" >&6; }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -50741,7 +51069,7 @@ $as_echo "$supports" >&6; }
|
||||
# Version comparison method inspired by http://stackoverflow.com/a/24067243
|
||||
COMPARABLE_REFERENCE_VERSION=`$AWK -F. '{ printf("%05d%05d%05d\n", $1, $2, $3) }' <<< "$REFERENCE_VERSION"`
|
||||
|
||||
if test $COMPARABLE_ACTUAL_VERSION -ge $COMPARABLE_REFERENCE_VERSION ; then
|
||||
if test $OPENJDK_BUILD_COMPARABLE_ACTUAL_VERSION -ge $COMPARABLE_REFERENCE_VERSION ; then
|
||||
:
|
||||
|
||||
# These flags either do not work or give spurious warnings prior to gcc 4.8.
|
||||
@ -50764,6 +51092,8 @@ $as_echo "$supports" >&6; }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
fi
|
||||
if ! [[ " $JVM_VARIANTS " =~ " zero " ]] && ! [[ " $JVM_VARIANTS " =~ " zeroshark " ]] ; then
|
||||
# Non-zero builds have stricter warnings
|
||||
@ -51918,9 +52248,15 @@ $as_echo "$supports" >&6; }
|
||||
DISABLE_WARNING_PREFIX=
|
||||
fi
|
||||
CFLAGS_WARNINGS_ARE_ERRORS="-Werror"
|
||||
# Repeate the check for the BUILD_CC
|
||||
# Repeate the check for the BUILD_CC and BUILD_CXX. Need to also reset
|
||||
# CFLAGS since any target specific flags will likely not work with the
|
||||
# build compiler
|
||||
CC_OLD="$CC"
|
||||
CXX_OLD="$CXX"
|
||||
CC="$BUILD_CC"
|
||||
CXX="$BUILD_CXX"
|
||||
CFLAGS_OLD="$CFLAGS"
|
||||
CFLAGS=""
|
||||
|
||||
|
||||
|
||||
@ -52198,6 +52534,8 @@ $as_echo "$supports" >&6; }
|
||||
BUILD_CC_DISABLE_WARNING_PREFIX=
|
||||
fi
|
||||
CC="$CC_OLD"
|
||||
CXX="$CXX_OLD"
|
||||
CFLAGS="$CFLAGS_OLD"
|
||||
;;
|
||||
clang)
|
||||
DISABLE_WARNING_PREFIX="-Wno-"
|
||||
|
@ -59,23 +59,25 @@ TOOLCHAIN_MINIMUM_VERSION_xlc=""
|
||||
|
||||
# Prepare the system so that TOOLCHAIN_CHECK_COMPILER_VERSION can be called.
|
||||
# Must have CC_VERSION_NUMBER and CXX_VERSION_NUMBER.
|
||||
# $1 - optional variable prefix for compiler and version variables (BUILD_)
|
||||
# $2 - optional variable prefix for comparable variable (OPENJDK_BUILD_)
|
||||
AC_DEFUN([TOOLCHAIN_PREPARE_FOR_VERSION_COMPARISONS],
|
||||
[
|
||||
if test "x$CC_VERSION_NUMBER" != "x$CXX_VERSION_NUMBER"; then
|
||||
AC_MSG_WARN([C and C++ compiler has different version numbers, $CC_VERSION_NUMBER vs $CXX_VERSION_NUMBER.])
|
||||
if test "x[$]$1CC_VERSION_NUMBER" != "x[$]$1CXX_VERSION_NUMBER"; then
|
||||
AC_MSG_WARN([C and C++ compiler have different version numbers, [$]$1CC_VERSION_NUMBER vs [$]$1CXX_VERSION_NUMBER.])
|
||||
AC_MSG_WARN([This typically indicates a broken setup, and is not supported])
|
||||
fi
|
||||
|
||||
# We only check CC_VERSION_NUMBER since we assume CXX_VERSION_NUMBER is equal.
|
||||
if [ [[ "$CC_VERSION_NUMBER" =~ (.*\.){3} ]] ]; then
|
||||
AC_MSG_WARN([C compiler version number has more than three parts (X.Y.Z): $CC_VERSION_NUMBER. Comparisons might be wrong.])
|
||||
if [ [[ "[$]$1CC_VERSION_NUMBER" =~ (.*\.){3} ]] ]; then
|
||||
AC_MSG_WARN([C compiler version number has more than three parts (X.Y.Z): [$]$1CC_VERSION_NUMBER. Comparisons might be wrong.])
|
||||
fi
|
||||
|
||||
if [ [[ "$CC_VERSION_NUMBER" =~ [0-9]{6} ]] ]; then
|
||||
AC_MSG_WARN([C compiler version number has a part larger than 99999: $CC_VERSION_NUMBER. Comparisons might be wrong.])
|
||||
if [ [[ "[$]$1CC_VERSION_NUMBER" =~ [0-9]{6} ]] ]; then
|
||||
AC_MSG_WARN([C compiler version number has a part larger than 99999: [$]$1CC_VERSION_NUMBER. Comparisons might be wrong.])
|
||||
fi
|
||||
|
||||
COMPARABLE_ACTUAL_VERSION=`$AWK -F. '{ printf("%05d%05d%05d\n", [$]1, [$]2, [$]3) }' <<< "$CC_VERSION_NUMBER"`
|
||||
$2COMPARABLE_ACTUAL_VERSION=`$AWK -F. '{ printf("%05d%05d%05d\n", [$]1, [$]2, [$]3) }' <<< "[$]$1CC_VERSION_NUMBER"`
|
||||
])
|
||||
|
||||
# Check if the configured compiler (C and C++) is of a specific version or
|
||||
@ -85,8 +87,9 @@ AC_DEFUN([TOOLCHAIN_PREPARE_FOR_VERSION_COMPARISONS],
|
||||
# VERSION: The version string to check against the found version
|
||||
# IF_AT_LEAST: block to run if the compiler is at least this version (>=)
|
||||
# IF_OLDER_THAN: block to run if the compiler is older than this version (<)
|
||||
# PREFIX: Optional variable prefix for compiler to compare version for (OPENJDK_BUILD_)
|
||||
BASIC_DEFUN_NAMED([TOOLCHAIN_CHECK_COMPILER_VERSION],
|
||||
[*VERSION IF_AT_LEAST IF_OLDER_THAN], [$@],
|
||||
[*VERSION PREFIX IF_AT_LEAST IF_OLDER_THAN], [$@],
|
||||
[
|
||||
# Need to assign to a variable since m4 is blocked from modifying parts in [].
|
||||
REFERENCE_VERSION=ARG_VERSION
|
||||
@ -102,7 +105,7 @@ BASIC_DEFUN_NAMED([TOOLCHAIN_CHECK_COMPILER_VERSION],
|
||||
# Version comparison method inspired by http://stackoverflow.com/a/24067243
|
||||
COMPARABLE_REFERENCE_VERSION=`$AWK -F. '{ printf("%05d%05d%05d\n", [$]1, [$]2, [$]3) }' <<< "$REFERENCE_VERSION"`
|
||||
|
||||
if test $COMPARABLE_ACTUAL_VERSION -ge $COMPARABLE_REFERENCE_VERSION ; then
|
||||
if test [$]ARG_PREFIX[COMPARABLE_ACTUAL_VERSION] -ge $COMPARABLE_REFERENCE_VERSION ; then
|
||||
:
|
||||
ARG_IF_AT_LEAST
|
||||
else
|
||||
@ -808,6 +811,10 @@ AC_DEFUN_ONCE([TOOLCHAIN_SETUP_BUILD_COMPILERS],
|
||||
BUILD_LDCXX="$BUILD_CXX"
|
||||
|
||||
PATH="$OLDPATH"
|
||||
|
||||
TOOLCHAIN_EXTRACT_COMPILER_VERSION(BUILD_CC, [BuildC])
|
||||
TOOLCHAIN_EXTRACT_COMPILER_VERSION(BUILD_CXX, [BuildC++])
|
||||
TOOLCHAIN_PREPARE_FOR_VERSION_COMPARISONS([BUILD_], [OPENJDK_BUILD_])
|
||||
else
|
||||
# If we are not cross compiling, use the normal target compilers for
|
||||
# building the build platform executables.
|
||||
|
199
common/bin/idea.sh
Normal file
199
common/bin/idea.sh
Normal file
@ -0,0 +1,199 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2009, 2014, 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.
|
||||
#
|
||||
|
||||
# Shell script for generating an IDEA project from a given list of modules
|
||||
|
||||
usage() {
|
||||
echo "usage: $0 [-h|--help] [-v|--verbose] [-o|--output <path>] [modules]+"
|
||||
exit 1
|
||||
}
|
||||
|
||||
SCRIPT_DIR=`dirname $0`
|
||||
PWD=`pwd`
|
||||
cd $SCRIPT_DIR; SCRIPT_DIR=`pwd`
|
||||
cd ../../; TOP=`pwd`; cd $PWD
|
||||
|
||||
IDEA_OUTPUT=$TOP/.idea
|
||||
VERBOSE="false"
|
||||
while [ $# -gt 0 ]
|
||||
do
|
||||
case $1 in
|
||||
-h | --help )
|
||||
usage
|
||||
;;
|
||||
|
||||
-v | --vebose )
|
||||
VERBOSE="true"
|
||||
;;
|
||||
|
||||
-o | --output )
|
||||
IDEA_OUTPUT=$2
|
||||
shift
|
||||
;;
|
||||
|
||||
-*) # bad option
|
||||
usage
|
||||
;;
|
||||
|
||||
* ) # non option
|
||||
break
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
mkdir $IDEA_OUTPUT || exit 1
|
||||
cd $IDEA_OUTPUT; IDEA_OUTPUT=`pwd`
|
||||
|
||||
IDEA_MAKE="$TOP/make/idea"
|
||||
IDEA_TEMPLATE="$IDEA_MAKE/template"
|
||||
IML_TEMPLATE="$IDEA_TEMPLATE/jdk.iml"
|
||||
ANT_TEMPLATE="$IDEA_TEMPLATE/ant.xml"
|
||||
IDEA_IML="$IDEA_OUTPUT/jdk.iml"
|
||||
IDEA_ANT="$IDEA_OUTPUT/ant.xml"
|
||||
|
||||
if [ "$VERBOSE" = "true" ] ; then
|
||||
echo "output dir: $IDEA_OUTPUT"
|
||||
echo "idea template dir: $IDEA_TEMPLATE"
|
||||
fi
|
||||
|
||||
if [ ! -f "$IML_TEMPLATE" ] ; then
|
||||
echo "FATAL: cannot find $IML_TEMPLATE" >&2; exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$ANT_TEMPLATE" ] ; then
|
||||
echo "FATAL: cannot find $ANT_TEMPLATE" >&2; exit 1
|
||||
fi
|
||||
|
||||
cp -r "$IDEA_TEMPLATE"/* "$IDEA_OUTPUT"
|
||||
cd $TOP ; make -f "$IDEA_MAKE/idea.gmk" -I make/common idea MAKEOVERRIDES= OUT=$IDEA_OUTPUT/env.cfg MODULES="$*" || exit 1
|
||||
cd $SCRIPT_DIR
|
||||
|
||||
. $IDEA_OUTPUT/env.cfg
|
||||
|
||||
# Expect MODULE_ROOTS, MODULE_NAMES, BOOT_JDK & SPEC to be set
|
||||
if [ "x$MODULE_ROOTS" = "x" ] ; then
|
||||
echo "FATAL: MODULE_ROOTS is empty" >&2; exit 1
|
||||
fi
|
||||
|
||||
if [ "x$MODULE_NAMES" = "x" ] ; then
|
||||
echo "FATAL: MODULE_NAMES is empty" >&2; exit 1
|
||||
fi
|
||||
|
||||
if [ "x$BOOT_JDK" = "x" ] ; then
|
||||
echo "FATAL: BOOT_JDK is empty" >&2; exit 1
|
||||
fi
|
||||
|
||||
if [ "x$SPEC" = "x" ] ; then
|
||||
echo "FATAL: SPEC is empty" >&2; exit 1
|
||||
fi
|
||||
|
||||
SOURCE_FOLDER=" <sourceFolder url=\"file://\$MODULE_DIR\$/####\" isTestSource=\"false\" />"
|
||||
SOURCE_FOLDERS_DONE="false"
|
||||
|
||||
addSourceFolder() {
|
||||
root=$@
|
||||
relativePath="`echo "$root" | sed -e s@"$TOP/\(.*$\)"@"\1"@`"
|
||||
folder="`echo "$SOURCE_FOLDER" | sed -e s@"\(.*/\)####\(.*\)"@"\1$relativePath\2"@`"
|
||||
printf "%s\n" "$folder" >> $IDEA_IML
|
||||
}
|
||||
|
||||
### Generate project iml
|
||||
RELATIVE_BUILD_DIR="`dirname $SPEC | sed -e s@"$TOP/\(.*$\)"@"\1"@`"
|
||||
rm -f $IDEA_IML
|
||||
while IFS= read -r line
|
||||
do
|
||||
if echo "$line" | egrep "^ .* <sourceFolder.*####" > /dev/null ; then
|
||||
if [ "$SOURCE_FOLDERS_DONE" = "false" ] ; then
|
||||
SOURCE_FOLDERS_DONE="true"
|
||||
for root in $MODULE_ROOTS; do
|
||||
addSourceFolder $root
|
||||
done
|
||||
fi
|
||||
elif echo "$line" | egrep "^ .* <excludeFolder.*####" > /dev/null ; then
|
||||
ul="`echo "$line" | sed -e s@"\(.*/\)####\(.*\)"@"\1$RELATIVE_BUILD_DIR\2"@`"
|
||||
printf "%s\n" "$ul" >> $IDEA_IML
|
||||
else
|
||||
printf "%s\n" "$line" >> $IDEA_IML
|
||||
fi
|
||||
done < "$IML_TEMPLATE"
|
||||
|
||||
|
||||
MODULE_NAME=" <property name=\"module.name\" value=\"####\" />"
|
||||
|
||||
addModuleName() {
|
||||
mn="`echo "$MODULE_NAME" | sed -e s@"\(.*\)####\(.*\)"@"\1$MODULE_NAMES\2"@`"
|
||||
printf "%s\n" "$mn" >> $IDEA_ANT
|
||||
}
|
||||
|
||||
BUILD_DIR=" <property name=\"build.target.dir\" value=\"####\" />"
|
||||
|
||||
addBuildDir() {
|
||||
DIR=`dirname $SPEC`
|
||||
mn="`echo "$BUILD_DIR" | sed -e s@"\(.*\)####\(.*\)"@"\1$DIR\2"@`"
|
||||
printf "%s\n" "$mn" >> $IDEA_ANT
|
||||
}
|
||||
|
||||
### Generate ant.xml
|
||||
|
||||
rm -f $IDEA_ANT
|
||||
while IFS= read -r line
|
||||
do
|
||||
if echo "$line" | egrep "^ .* <property name=\"module.name\"" > /dev/null ; then
|
||||
addModuleName
|
||||
elif echo "$line" | egrep "^ .* <property name=\"build.target.dir\"" > /dev/null ; then
|
||||
addBuildDir
|
||||
else
|
||||
printf "%s\n" "$line" >> $IDEA_ANT
|
||||
fi
|
||||
done < "$ANT_TEMPLATE"
|
||||
|
||||
### Compile the custom Logger
|
||||
|
||||
CLASSES=$IDEA_OUTPUT/classes
|
||||
|
||||
if [ "x$ANT_HOME" = "x" ] ; then
|
||||
# try some common locations, before giving up
|
||||
if [ -f "/usr/share/ant/lib/ant.jar" ] ; then
|
||||
ANT_HOME="/usr/share/ant"
|
||||
elif [ -f "/usr/local/Cellar/ant/1.9.4/libexec/lib/ant.jar" ] ; then
|
||||
ANT_HOME="/usr/local/Cellar/ant/1.9.4/libexec"
|
||||
else
|
||||
echo "FATAL: cannot find ant. Try setting ANT_HOME." >&2; exit 1
|
||||
fi
|
||||
fi
|
||||
CP=$ANT_HOME/lib/ant.jar
|
||||
rm -rf $CLASSES; mkdir $CLASSES
|
||||
|
||||
if [ "x$CYGPATH" = "x" ] ; then ## CYGPATH may be set in env.cfg
|
||||
JAVAC_SOURCE_FILE=$IDEA_OUTPUT/src/idea/JdkIdeaAntLogger.java
|
||||
JAVAC_CLASSES=$CLASSES
|
||||
JAVAC_CP=$CP
|
||||
else
|
||||
JAVAC_SOURCE_FILE=`cygpath -am $IDEA_OUTPUT/src/idea/JdkIdeaAntLogger.java`
|
||||
JAVAC_CLASSES=`cygpath -am $CLASSES`
|
||||
JAVAC_CP=`cygpath -am $CP`
|
||||
fi
|
||||
|
||||
$BOOT_JDK/bin/javac -d $JAVAC_CLASSES -cp $JAVAC_CP $JAVAC_SOURCE_FILE
|
@ -89,7 +89,7 @@ install_jib() {
|
||||
fi
|
||||
|
||||
if command -v curl > /dev/null; then
|
||||
getcmd="curl -s"
|
||||
getcmd="curl -s -L --retry 3 --retry-delay 5"
|
||||
elif command -v wget > /dev/null; then
|
||||
getcmd="wget --quiet -O -"
|
||||
else
|
||||
|
@ -360,3 +360,4 @@ cc30faa2da498c478e89ab062ff160653ca1b170 jdk-9+113
|
||||
7bab1b1b36824924b1c657a8419369ba93d198d3 jdk-9+115
|
||||
7dfa7377a5e601b8f740741a9a80e04c72dd04d6 jdk-9+116
|
||||
7a1b36bf2fe55a9a7732489ccdd326c910329a7e jdk-9+117
|
||||
8c2c2d17f7ce92a31c9ccb44a122ec62f5a85ace jdk-9+118
|
||||
|
@ -520,3 +520,4 @@ b64432bae5271735fd53300b2005b713e98ef411 jdk-9+114
|
||||
88dd08d7be0fe7fb9f1914b1628f0aae9bf56e25 jdk-9+115
|
||||
61a214186dae6811dd989e9165e42f7dbf02acde jdk-9+116
|
||||
88170d3642905b9e6cac03e8efcc976885a7e6da jdk-9+117
|
||||
9b1075cac08dc836ec32e7b368415cbe3aceaf8c jdk-9+118
|
||||
|
@ -3387,14 +3387,14 @@ bool force_verify_field_access(Klass* current_class, Klass* field_class, AccessF
|
||||
return (!access.is_private() && InstanceKlass::cast(current_class)->is_same_class_package(field_class));
|
||||
}
|
||||
|
||||
// Return the first non-null class loader up the execution stack, or null
|
||||
// if only code from the null class loader is on the stack.
|
||||
// Return the first user-defined class loader up the execution stack, or null
|
||||
// if only code from the bootstrap or platform class loader is on the stack.
|
||||
|
||||
JVM_ENTRY(jobject, JVM_LatestUserDefinedLoader(JNIEnv *env))
|
||||
for (vframeStream vfst(thread); !vfst.at_end(); vfst.next()) {
|
||||
vfst.skip_reflection_related_frames(); // Only needed for 1.4 reflection
|
||||
oop loader = vfst.method()->method_holder()->class_loader();
|
||||
if (loader != NULL) {
|
||||
if (loader != NULL && !SystemDictionary::is_platform_class_loader(loader)) {
|
||||
return JNIHandles::make_local(env, loader);
|
||||
}
|
||||
}
|
||||
|
@ -120,24 +120,33 @@ endif
|
||||
TEST_ROOT := $(shell pwd)
|
||||
|
||||
# Root of all test results
|
||||
ifdef ALT_OUTPUTDIR
|
||||
ABS_BUILD_ROOT = $(ALT_OUTPUTDIR)
|
||||
ifdef TEST_OUTPUT_DIR
|
||||
$(shell $(MKDIR) -p $(TEST_OUTPUT_DIR)/jtreg)
|
||||
ABS_TEST_OUTPUT_DIR := \
|
||||
$(shell $(CD) $(TEST_OUTPUT_DIR)/jtreg && $(PWD))
|
||||
else
|
||||
ABS_BUILD_ROOT = $(TEST_ROOT)/../build/$(PLATFORM)-$(ARCH)
|
||||
ifdef ALT_OUTPUTDIR
|
||||
ABS_OUTPUTDIR = $(shell $(CD) $(ALT_OUTPUTDIR) && $(PWD))
|
||||
else
|
||||
ABS_OUTPUTDIR = $(shell $(CD) $(TEST_ROOT)/.. && $(PWD))
|
||||
endif
|
||||
|
||||
ABS_PLATFORM_BUILD_ROOT = $(ABS_OUTPUTDIR)
|
||||
ABS_TEST_OUTPUT_DIR := $(ABS_PLATFORM_BUILD_ROOT)/testoutput/$(UNIQUE_DIR)
|
||||
endif
|
||||
ABS_TEST_OUTPUT_DIR = $(ABS_BUILD_ROOT)/testoutput/$(UNIQUE_DIR)
|
||||
|
||||
# Expect JPRT to set PRODUCT_HOME (the product or jdk in this case to test)
|
||||
ifndef PRODUCT_HOME
|
||||
# Try to use j2sdk-image if it exists
|
||||
ABS_JDK_IMAGE = $(ABS_BUILD_ROOT)/j2sdk-image
|
||||
PRODUCT_HOME := \
|
||||
$(shell \
|
||||
if [ -d $(ABS_JDK_IMAGE) ] ; then \
|
||||
$(ECHO) "$(ABS_JDK_IMAGE)"; \
|
||||
else \
|
||||
$(ECHO) "$(ABS_BUILD_ROOT)" ; \
|
||||
# Try to use images/jdk if it exists
|
||||
ABS_JDK_IMAGE = $(ABS_PLATFORM_BUILD_ROOT)/images/jdk
|
||||
PRODUCT_HOME := \
|
||||
$(shell \
|
||||
if [ -d $(ABS_JDK_IMAGE) ] ; then \
|
||||
$(ECHO) "$(ABS_JDK_IMAGE)"; \
|
||||
else \
|
||||
$(ECHO) "$(ABS_PLATFORM_BUILD_ROOT)"; \
|
||||
fi)
|
||||
PRODUCT_HOME := $(PRODUCT_HOME)
|
||||
endif
|
||||
|
||||
# Expect JPRT to set JAVA_ARGS (e.g. -server etc.)
|
||||
|
@ -360,3 +360,4 @@ bb8379287f3736f38c52b2d1418784e2592461d1 jdk-9+114
|
||||
35225b837d66582037eeadeb471c13235dfd793d jdk-9+115
|
||||
baeb5edb38939cdb78ae0ac6f4fd368465cbf188 jdk-9+116
|
||||
4da0f73ce03aaf245b92cc040cc0ab0e3fa54dc2 jdk-9+117
|
||||
e1eba5cfa5cc8c66d524396a05323dc93568730a jdk-9+118
|
||||
|
@ -37,10 +37,6 @@ BUILD_TOOLS_JDK := $(call SetupJavaCompilationCompileTarget, \
|
||||
|
||||
################################################################################
|
||||
|
||||
# Add a checksum ("jsum") to the end of a text file. Prevents trivial tampering with class lists.
|
||||
TOOL_ADDJSUM = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes \
|
||||
build.tools.addjsum.AddJsum
|
||||
|
||||
ifeq ($(BOOT_JDK_MODULAR), true)
|
||||
COMPILEFONTCONFIG_ADD_EXPORTS := -XaddExports:java.desktop/sun.awt=ALL-UNNAMED
|
||||
endif
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
# Copyright (c) 2011, 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
|
||||
@ -25,11 +25,11 @@
|
||||
|
||||
include LauncherCommon.gmk
|
||||
|
||||
JAVA_RC_FLAGS += -i $(JDK_TOPDIR)/src/java.base/windows/native/common
|
||||
JAVA_RC_FLAGS += -I $(JDK_TOPDIR)/src/java.base/windows/native/common
|
||||
ifdef OPENJDK
|
||||
JAVA_RC_FLAGS += -i "$(JDK_TOPDIR)/src/java.base/windows/native/launcher/icons"
|
||||
JAVA_RC_FLAGS += -I $(JDK_TOPDIR)/src/java.base/windows/native/launcher/icons
|
||||
else
|
||||
JAVA_RC_FLAGS += -i "$(JDK_TOPDIR)/src/closed/java.base/windows/native/launcher/icons"
|
||||
JAVA_RC_FLAGS += -I $(JDK_TOPDIR)/src/closed/java.base/windows/native/launcher/icons
|
||||
endif
|
||||
|
||||
################################################################################
|
||||
|
@ -224,9 +224,9 @@ ifeq ($(OPENJDK_TARGET_OS), windows)
|
||||
endif
|
||||
|
||||
ifdef OPENJDK
|
||||
LIBAWT_RC_FLAGS := -i "$(JDK_TOPDIR)/src/java.base/windows/native/launcher/icons"
|
||||
LIBAWT_RC_FLAGS := -I $(JDK_TOPDIR)/src/java.base/windows/native/launcher/icons
|
||||
else
|
||||
LIBAWT_RC_FLAGS := -i "$(JDK_TOPDIR)/src/closed/java.base/windows/native/launcher/icons"
|
||||
LIBAWT_RC_FLAGS := -I $(JDK_TOPDIR)/src/closed/java.base/windows/native/launcher/icons
|
||||
endif
|
||||
LIBAWT_VERSIONINFO_RESOURCE := $(JDK_TOPDIR)/src/java.desktop/windows/native/libawt/windows/awt.rc
|
||||
endif
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
# Copyright (c) 2012, 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
|
||||
@ -30,28 +30,35 @@ SUNWprivate_1.1 {
|
||||
JNI_OnLoad;
|
||||
Java_com_oracle_security_ucrypto_UcryptoProvider_loadLibraries;
|
||||
Java_com_oracle_security_ucrypto_UcryptoProvider_getMechList;
|
||||
Java_com_oracle_security_ucrypto_NativeDigest_nativeInit;
|
||||
Java_com_oracle_security_ucrypto_NativeDigestMD_nativeInit;
|
||||
Java_com_oracle_security_ucrypto_NativeDigestMD_nativeUpdate;
|
||||
Java_com_oracle_security_ucrypto_NativeDigestMD_nativeDigest;
|
||||
Java_com_oracle_security_ucrypto_NativeDigestMD_nativeClone;
|
||||
Java_com_oracle_security_ucrypto_NativeDigestMD_nativeFree;
|
||||
Java_com_oracle_security_ucrypto_NativeDigest_nativeInit;
|
||||
Java_com_oracle_security_ucrypto_NativeDigest_nativeUpdate;
|
||||
Java_com_oracle_security_ucrypto_NativeDigest_nativeDigest;
|
||||
Java_com_oracle_security_ucrypto_NativeDigest_nativeClone;
|
||||
Java_com_oracle_security_ucrypto_NativeDigest_nativeFree;
|
||||
Java_com_oracle_security_ucrypto_NativeCipher_nativeInit;
|
||||
Java_com_oracle_security_ucrypto_NativeCipher_nativeUpdate;
|
||||
Java_com_oracle_security_ucrypto_NativeCipher_nativeFinal;
|
||||
Java_com_oracle_security_ucrypto_NativeCipher_nativeInit;
|
||||
Java_com_oracle_security_ucrypto_NativeCipher_nativeUpdate;
|
||||
Java_com_oracle_security_ucrypto_NativeCipher_nativeFinal;
|
||||
Java_com_oracle_security_ucrypto_NativeKey_nativeFree;
|
||||
Java_com_oracle_security_ucrypto_NativeKey_00024RSAPrivate_nativeInit;
|
||||
Java_com_oracle_security_ucrypto_NativeKey_00024RSAPrivateCrt_nativeInit;
|
||||
Java_com_oracle_security_ucrypto_NativeKey_00024RSAPublic_nativeInit;
|
||||
Java_com_oracle_security_ucrypto_NativeRSASignature_nativeInit;
|
||||
Java_com_oracle_security_ucrypto_NativeRSASignature_nativeUpdate__JZ_3BII;
|
||||
Java_com_oracle_security_ucrypto_NativeRSASignature_nativeUpdate__JZJI;
|
||||
Java_com_oracle_security_ucrypto_NativeRSASignature_nativeFinal;
|
||||
Java_com_oracle_security_ucrypto_NativeRSACipher_nativeAtomic;
|
||||
|
||||
Java_com_oracle_security_ucrypto_NativeRSASignature_nativeInit;
|
||||
Java_com_oracle_security_ucrypto_NativeRSASignature_nativeUpdate__JZ_3BII;
|
||||
Java_com_oracle_security_ucrypto_NativeRSASignature_nativeUpdate__JZJI;
|
||||
Java_com_oracle_security_ucrypto_NativeRSASignature_nativeFinal;
|
||||
Java_com_oracle_security_ucrypto_NativeRSACipher_nativeAtomic;
|
||||
JavaCritical_com_oracle_security_ucrypto_NativeDigestMD_nativeInit;
|
||||
JavaCritical_com_oracle_security_ucrypto_NativeDigestMD_nativeUpdate;
|
||||
JavaCritical_com_oracle_security_ucrypto_NativeDigestMD_nativeDigest;
|
||||
JavaCritical_com_oracle_security_ucrypto_NativeDigestMD_nativeClone;
|
||||
JavaCritical_com_oracle_security_ucrypto_NativeDigestMD_nativeFree;
|
||||
JavaCritical_com_oracle_security_ucrypto_NativeDigest_nativeInit;
|
||||
JavaCritical_com_oracle_security_ucrypto_NativeDigest_nativeUpdate;
|
||||
JavaCritical_com_oracle_security_ucrypto_NativeDigest_nativeDigest;
|
||||
JavaCritical_com_oracle_security_ucrypto_NativeDigest_nativeClone;
|
||||
JavaCritical_com_oracle_security_ucrypto_NativeDigest_nativeFree;
|
||||
JavaCritical_com_oracle_security_ucrypto_NativeCipher_nativeInit;
|
||||
JavaCritical_com_oracle_security_ucrypto_NativeCipher_nativeUpdate;
|
||||
@ -60,10 +67,10 @@ SUNWprivate_1.1 {
|
||||
JavaCritical_com_oracle_security_ucrypto_NativeKey_00024RSAPrivate_nativeInit;
|
||||
JavaCritical_com_oracle_security_ucrypto_NativeKey_00024RSAPrivateCrt_nativeInit;
|
||||
JavaCritical_com_oracle_security_ucrypto_NativeKey_00024RSAPublic_nativeInit;
|
||||
JavaCritical_com_oracle_security_ucrypto_NativeRSASignature_nativeInit;
|
||||
JavaCritical_com_oracle_security_ucrypto_NativeRSASignature_nativeUpdate__JZ_3BII;
|
||||
JavaCritical_com_oracle_security_ucrypto_NativeRSASignature_nativeUpdate__JZJI;
|
||||
JavaCritical_com_oracle_security_ucrypto_NativeRSASignature_nativeFinal;
|
||||
JavaCritical_com_oracle_security_ucrypto_NativeRSASignature_nativeInit;
|
||||
JavaCritical_com_oracle_security_ucrypto_NativeRSASignature_nativeUpdate__JZ_3BII;
|
||||
JavaCritical_com_oracle_security_ucrypto_NativeRSASignature_nativeUpdate__JZJI;
|
||||
JavaCritical_com_oracle_security_ucrypto_NativeRSASignature_nativeFinal;
|
||||
JavaCritical_com_oracle_security_ucrypto_NativeRSACipher_nativeAtomic;
|
||||
|
||||
local:
|
||||
|
@ -262,7 +262,7 @@ SUNWprivate_1.1 {
|
||||
Java_jdk_internal_reflect_Reflection_getCallerClass__;
|
||||
Java_jdk_internal_reflect_Reflection_getCallerClass__I;
|
||||
Java_jdk_internal_reflect_Reflection_getClassAccessFlags;
|
||||
Java_jdk_internal_misc_VM_latestUserDefinedLoader;
|
||||
Java_jdk_internal_misc_VM_latestUserDefinedLoader0;
|
||||
Java_jdk_internal_misc_VM_getuid;
|
||||
Java_jdk_internal_misc_VM_geteuid;
|
||||
Java_jdk_internal_misc_VM_getgid;
|
||||
|
@ -1,62 +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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This tool is used to help create the class list for class data sharing.
|
||||
*
|
||||
* The classlist is produced internally by first running a select number of
|
||||
* startup benchmarks with the -XX:DumpLoadedClassList=<file> option, then
|
||||
* running this tool in the following fashion to produce a complete classlist:
|
||||
*
|
||||
* jjs -scripting makeClasslist.js -- list1 list2 list3 > classlist.platform
|
||||
*
|
||||
* The lists should be listed in roughly smallest to largest order based on
|
||||
* application size.
|
||||
*
|
||||
* After generating the classlist it's necessary to add a checksum (using
|
||||
* AddJsum.java) before checking it into the workspace as the corresponding
|
||||
* platform-specific classlist, such as make/data/classlist/classlist.linux
|
||||
*/
|
||||
"use strict";
|
||||
var classlist = [];
|
||||
var seenClasses = {};
|
||||
|
||||
for (var a in $ARG) {
|
||||
var arg = $ARG[a];
|
||||
|
||||
var classes = readFully(arg).replace(/[\r\n]+/g, "\n").split("\n");
|
||||
|
||||
for (var c in classes) {
|
||||
var clazz = classes[c];
|
||||
if (clazz !== "" && seenClasses[clazz] === undefined) {
|
||||
seenClasses[clazz] = clazz;
|
||||
classlist.push(clazz);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (c in classlist) {
|
||||
print(classlist[c]);
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2013, 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 build.tools.addjsum;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.regex.*;
|
||||
|
||||
/** Adds a checksum ("jsum") to the end of a text file. The algorithm
|
||||
used is known to the JVM and prevents trivial tampering with the
|
||||
class list used for class data sharing.
|
||||
*/
|
||||
|
||||
public class AddJsum {
|
||||
private static final long JSUM_SEED = 0xCAFEBABEBABECAFEL;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (args.length != 2) {
|
||||
System.err.println("Usage: java AddJsum [input file name] [output file name]");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
File inFile = new File(args[0]);
|
||||
File outFile = new File(args[1]);
|
||||
BufferedReader reader = new BufferedReader(new FileReader(inFile));
|
||||
BufferedWriter writer = new BufferedWriter(new FileWriter(outFile));
|
||||
Pattern p = Pattern.compile("# [0-9A-Fa-f]*");
|
||||
long computedJsum = JSUM_SEED;
|
||||
|
||||
String line = null;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (line.length() > 0 && line.charAt(0) == '#') {
|
||||
Matcher m = p.matcher(line);
|
||||
if (!m.matches()) {
|
||||
writer.write(line);
|
||||
writer.newLine();
|
||||
}
|
||||
} else {
|
||||
computedJsum = jsum(computedJsum, line);
|
||||
writer.write(line);
|
||||
writer.newLine();
|
||||
}
|
||||
}
|
||||
String hex = Long.toHexString(computedJsum);
|
||||
int diff = 16 - hex.length();
|
||||
for (int i = 0; i < diff; i++) {
|
||||
hex = "0" + hex;
|
||||
}
|
||||
writer.write("# " + hex);
|
||||
writer.newLine();
|
||||
reader.close();
|
||||
writer.close();
|
||||
} catch (IOException e) {
|
||||
System.err.println("Error reading or writing file");
|
||||
throw(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static long jsum(long start, String str) {
|
||||
long h = start;
|
||||
int len = str.length();
|
||||
for (int i = 0; i < len; i++) {
|
||||
char c = str.charAt(i);
|
||||
if (c <= ' ') {
|
||||
/* Skip spaces and control characters */
|
||||
continue;
|
||||
}
|
||||
h = 31 * h + c;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
}
|
@ -603,12 +603,12 @@ public class ObjectInputStream
|
||||
* Class.forName(desc.getName(), false, loader)
|
||||
* </pre>
|
||||
* where <code>loader</code> is determined as follows: if there is a
|
||||
* method on the current thread's stack whose declaring class was
|
||||
* defined by a user-defined class loader (and was not a generated to
|
||||
* implement reflective invocations), then <code>loader</code> is class
|
||||
* loader corresponding to the closest such method to the currently
|
||||
* executing frame; otherwise, <code>loader</code> is
|
||||
* <code>null</code>. If this call results in a
|
||||
* method on the current thread's stack whose declaring class is not a
|
||||
* <a href="../lang/ClassLoader.html#builtinLoaders">
|
||||
* <em>platform class</em></a>, then <code>loader</code> is
|
||||
* the class loader of such class; otherwise, <code>loader</code>
|
||||
* is the {@linkplain ClassLoader#getPlatformClassLoader()
|
||||
* platform class loader}. If this call results in a
|
||||
* <code>ClassNotFoundException</code> and the name of the passed
|
||||
* <code>ObjectStreamClass</code> instance is the Java language keyword
|
||||
* for a primitive type or void, then the <code>Class</code> object
|
||||
@ -666,12 +666,15 @@ public class ObjectInputStream
|
||||
* <pre>
|
||||
* Class.forName(i, false, loader)
|
||||
* </pre>
|
||||
* where <code>loader</code> is that of the first non-<code>null</code>
|
||||
* class loader up the execution stack, or <code>null</code> if no
|
||||
* non-<code>null</code> class loaders are on the stack (the same class
|
||||
* loader choice used by the <code>resolveClass</code> method). Unless any
|
||||
* of the resolved interfaces are non-public, this same value of
|
||||
* <code>loader</code> is also the class loader passed to
|
||||
* where <code>loader</code> is determined as follows: if there is a
|
||||
* method on the current thread's stack whose declaring class is not a
|
||||
* <a href="../lang/ClassLoader.html#builtinLoaders">
|
||||
* <em>platform class</em></a>, then <code>loader</code> is
|
||||
* the class loader of such class; otherwise, <code>loader</code>
|
||||
* is the {@linkplain ClassLoader#getPlatformClassLoader()
|
||||
* platform class loader}.
|
||||
* Unless any of the resolved interfaces are non-public, this same value
|
||||
* of <code>loader</code> is also the class loader passed to
|
||||
* <code>Proxy.getProxyClass</code>; if non-public interfaces are present,
|
||||
* their class loader is passed instead (if more than one non-public
|
||||
* interface class loader is encountered, an
|
||||
@ -2154,10 +2157,11 @@ public class ObjectInputStream
|
||||
int ndoubles);
|
||||
|
||||
/**
|
||||
* Returns the first non-null class loader (not counting class loaders of
|
||||
* generated reflection implementation classes) up the execution stack, or
|
||||
* null if only code from the null class loader is on the stack. This
|
||||
* method is also called via reflection by the following RMI-IIOP class:
|
||||
* Returns the first non-null and non-platform class loader
|
||||
* (not counting class loaders of generated reflection implementation classes)
|
||||
* up the execution stack, or null if only code from the bootstrap and
|
||||
* platform class loader is on the stack.
|
||||
* This method is also called via reflection by the following RMI-IIOP class:
|
||||
*
|
||||
* com.sun.corba.se.internal.util.JDKClassLoader
|
||||
*
|
||||
|
@ -1221,13 +1221,13 @@ public final class Integer extends Number implements Comparable<Integer> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a hash code for a {@code int} value; compatible with
|
||||
* Returns a hash code for an {@code int} value; compatible with
|
||||
* {@code Integer.hashCode()}.
|
||||
*
|
||||
* @param value the value to hash
|
||||
* @since 1.8
|
||||
*
|
||||
* @return a hash code value for a {@code int} value.
|
||||
* @return a hash code value for an {@code int} value.
|
||||
*/
|
||||
public static int hashCode(int value) {
|
||||
return value;
|
||||
@ -1596,7 +1596,7 @@ public final class Integer extends Number implements Comparable<Integer> {
|
||||
@Native public static final int SIZE = 32;
|
||||
|
||||
/**
|
||||
* The number of bytes used to represent a {@code int} value in two's
|
||||
* The number of bytes used to represent an {@code int} value in two's
|
||||
* complement binary form.
|
||||
*
|
||||
* @since 1.8
|
||||
@ -1790,9 +1790,8 @@ public final class Integer extends Number implements Comparable<Integer> {
|
||||
i = (i & 0x55555555) << 1 | (i >>> 1) & 0x55555555;
|
||||
i = (i & 0x33333333) << 2 | (i >>> 2) & 0x33333333;
|
||||
i = (i & 0x0f0f0f0f) << 4 | (i >>> 4) & 0x0f0f0f0f;
|
||||
i = (i << 24) | ((i & 0xff00) << 8) |
|
||||
((i >>> 8) & 0xff00) | (i >>> 24);
|
||||
return i;
|
||||
|
||||
return reverseBytes(i);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1820,10 +1819,10 @@ public final class Integer extends Number implements Comparable<Integer> {
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public static int reverseBytes(int i) {
|
||||
return ((i >>> 24) ) |
|
||||
((i >> 8) & 0xFF00) |
|
||||
((i << 8) & 0xFF0000) |
|
||||
((i << 24));
|
||||
return (i << 24) |
|
||||
((i & 0xff00) << 8) |
|
||||
((i >>> 8) & 0xff00) |
|
||||
(i >>> 24);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1952,10 +1952,8 @@ public final class Long extends Number implements Comparable<Long> {
|
||||
i = (i & 0x5555555555555555L) << 1 | (i >>> 1) & 0x5555555555555555L;
|
||||
i = (i & 0x3333333333333333L) << 2 | (i >>> 2) & 0x3333333333333333L;
|
||||
i = (i & 0x0f0f0f0f0f0f0f0fL) << 4 | (i >>> 4) & 0x0f0f0f0f0f0f0f0fL;
|
||||
i = (i & 0x00ff00ff00ff00ffL) << 8 | (i >>> 8) & 0x00ff00ff00ff00ffL;
|
||||
i = (i << 48) | ((i & 0xffff0000L) << 16) |
|
||||
((i >>> 16) & 0xffff0000L) | (i >>> 48);
|
||||
return i;
|
||||
|
||||
return reverseBytes(i);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1627,8 +1627,7 @@ class SecurityManager {
|
||||
* @deprecated This method relies on the caller being at a stack depth
|
||||
* of 4 which is error-prone and cannot be enforced by the runtime.
|
||||
* Users of this method should instead invoke {@link #checkPermission}
|
||||
* directly. This method will be changed in a future release
|
||||
* to check the permission {@code java.security.AllPermission}.
|
||||
* directly.
|
||||
* This method is subject to removal in a future version of Java SE.
|
||||
*
|
||||
* @see java.lang.reflect.Member
|
||||
|
@ -318,7 +318,7 @@ public class ModuleDescriptor
|
||||
/**
|
||||
* Tests this module export for equality with the given object.
|
||||
*
|
||||
* <p> If the given object is not a {@code Exports} then this method
|
||||
* <p> If the given object is not an {@code Exports} then this method
|
||||
* returns {@code false}. Two module exports objects are equal if the
|
||||
* package names are equal and the set of target module names is equal.
|
||||
* </p>
|
||||
|
@ -903,7 +903,7 @@ class Field extends AccessibleObject implements Member {
|
||||
* Sets the value of a field as an {@code int} on the specified object.
|
||||
* This method is equivalent to
|
||||
* {@code set(obj, iObj)},
|
||||
* where {@code iObj} is a {@code Integer} object and
|
||||
* where {@code iObj} is an {@code Integer} object and
|
||||
* {@code iObj.intValue() == i}.
|
||||
*
|
||||
* @param obj the object whose field should be modified
|
||||
|
@ -36,13 +36,13 @@ public class InaccessibleObjectException extends RuntimeException {
|
||||
private static final long serialVersionUID = 4158786093378140901L;
|
||||
|
||||
/**
|
||||
* Constructs a {@code InaccessibleObjectException} with no detail message.
|
||||
* Constructs an {@code InaccessibleObjectException} with no detail message.
|
||||
*/
|
||||
public InaccessibleObjectException() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@code InaccessibleObjectException} with the given detail
|
||||
* Constructs an {@code InaccessibleObjectException} with the given detail
|
||||
* message.
|
||||
*
|
||||
* @param msg
|
||||
|
@ -4676,7 +4676,7 @@ public class BigInteger extends Number implements Comparable<BigInteger> {
|
||||
*
|
||||
* @return this {@code BigInteger} converted to an {@code int}.
|
||||
* @throws ArithmeticException if the value of {@code this} will
|
||||
* not exactly fit in a {@code int}.
|
||||
* not exactly fit in an {@code int}.
|
||||
* @see BigInteger#intValue
|
||||
* @since 1.8
|
||||
*/
|
||||
|
@ -246,7 +246,7 @@ public class InetSocketAddress
|
||||
* the range of valid port values, or if the hostname
|
||||
* parameter is {@code null}.
|
||||
* @see #isUnresolved()
|
||||
* @return a {@code InetSocketAddress} representing the unresolved
|
||||
* @return an {@code InetSocketAddress} representing the unresolved
|
||||
* socket address
|
||||
* @since 1.5
|
||||
*/
|
||||
|
@ -106,8 +106,8 @@ public class URLDecoder {
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a {@code application/x-www-form-urlencoded} string using a specific
|
||||
* encoding scheme.
|
||||
* Decodes an {@code application/x-www-form-urlencoded} string using
|
||||
* a specific encoding scheme.
|
||||
* The supplied encoding is used to determine
|
||||
* what characters are represented by any consecutive sequences of the
|
||||
* form "<i>{@code %xy}</i>".
|
||||
|
@ -225,7 +225,7 @@ public class URLEncoder {
|
||||
/*
|
||||
* If this character represents the start of a Unicode
|
||||
* surrogate pair, then pass in two characters. It's not
|
||||
* clear what should be done if a bytes reserved in the
|
||||
* clear what should be done if a byte reserved in the
|
||||
* surrogate pairs range occurs outside of a legal
|
||||
* surrogate pair. For now, just treat it as if it were
|
||||
* any other character.
|
||||
|
@ -196,10 +196,9 @@ import java.util.Objects;
|
||||
* of the JDK reference implementation.
|
||||
* <p>
|
||||
* This implementation supports the Hash_DRBG and HMAC_DRBG mechanisms with
|
||||
* DRBG algorithm SHA-1, SHA-224, SHA-512/224, SHA-256, SHA-512/256,
|
||||
* SHA-384 and SHA-512, and CTR_DRBG (both using derivation function and
|
||||
* not using derivation function) with DRBG algorithm 3KeyTDEA
|
||||
* (also known as DESede in JCE), AES-128, AES-192 and AES-256.
|
||||
* DRBG algorithm SHA-224, SHA-512/224, SHA-256, SHA-512/256, SHA-384 and
|
||||
* SHA-512, and CTR_DRBG (both using derivation function and not using
|
||||
* derivation function) with DRBG algorithm AES-128, AES-192 and AES-256.
|
||||
* <p>
|
||||
* The mechanism name and DRBG algorithm name are determined by the
|
||||
* {@linkplain Security#getProperty(String) security property}
|
||||
|
@ -65,7 +65,7 @@ extends GeneralSecurityException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code InvalidAlgorithmParameterException} with the
|
||||
* Creates an {@code InvalidAlgorithmParameterException} with the
|
||||
* specified detail message and cause.
|
||||
*
|
||||
* @param message the detail message (which is saved for later retrieval
|
||||
@ -80,7 +80,7 @@ extends GeneralSecurityException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code InvalidAlgorithmParameterException} with the
|
||||
* Creates an {@code InvalidAlgorithmParameterException} with the
|
||||
* specified cause and a detail message of
|
||||
* {@code (cause==null ? null : cause.toString())}
|
||||
* (which typically contains the class and detail message of
|
||||
|
@ -58,7 +58,7 @@ public class InvalidKeyException extends KeyException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code InvalidKeyException} with the specified
|
||||
* Creates an {@code InvalidKeyException} with the specified
|
||||
* detail message and cause.
|
||||
*
|
||||
* @param message the detail message (which is saved for later retrieval
|
||||
@ -73,7 +73,7 @@ public class InvalidKeyException extends KeyException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code InvalidKeyException} with the specified cause
|
||||
* Creates an {@code InvalidKeyException} with the specified cause
|
||||
* and a detail message of {@code (cause==null ? null : cause.toString())}
|
||||
* (which typically contains the class and detail message of
|
||||
* {@code cause}).
|
||||
|
@ -139,12 +139,10 @@ public class ProtectionDomain {
|
||||
*/
|
||||
final Key key = new Key();
|
||||
|
||||
private static final Debug debug = Debug.getInstance("domain");
|
||||
|
||||
/**
|
||||
* Creates a new ProtectionDomain with the given CodeSource and
|
||||
* Permissions. If the permissions object is not null, then
|
||||
* {@code setReadOnly())} will be called on the passed in
|
||||
* {@code setReadOnly()} will be called on the passed in
|
||||
* Permissions object. The only permissions granted to this domain
|
||||
* are the ones specified; the current Policy will not be consulted.
|
||||
*
|
||||
@ -338,6 +336,13 @@ public class ProtectionDomain {
|
||||
" "+pc+"\n";
|
||||
}
|
||||
|
||||
/*
|
||||
* holder class for the static field "debug" to delay its initialization
|
||||
*/
|
||||
private static class DebugHolder {
|
||||
private static final Debug debug = Debug.getInstance("domain");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true (merge policy permissions) in the following cases:
|
||||
*
|
||||
@ -359,7 +364,7 @@ public class ProtectionDomain {
|
||||
if (sm == null) {
|
||||
return true;
|
||||
} else {
|
||||
if (debug != null) {
|
||||
if (DebugHolder.debug != null) {
|
||||
if (sm.getClass().getClassLoader() == null &&
|
||||
Policy.getPolicyNoCheck().getClass().getClassLoader()
|
||||
== null) {
|
||||
|
@ -1809,7 +1809,7 @@ public abstract class Provider extends Properties {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this service has its Supported* properties for
|
||||
* Return whether this service has its supported properties for
|
||||
* keys defined. Parses the attributes if not yet initialized.
|
||||
*/
|
||||
private boolean hasKeyAttributes() {
|
||||
|
@ -62,8 +62,6 @@ public class SecureClassLoader extends ClassLoader {
|
||||
private final Map<CodeSourceKey, ProtectionDomain> pdcache
|
||||
= new ConcurrentHashMap<>(11);
|
||||
|
||||
private static final Debug debug = Debug.getInstance("scl");
|
||||
|
||||
static {
|
||||
ClassLoader.registerAsParallelCapable();
|
||||
}
|
||||
@ -202,6 +200,13 @@ public class SecureClassLoader extends ClassLoader {
|
||||
return new Permissions(); // ProtectionDomain defers the binding
|
||||
}
|
||||
|
||||
/*
|
||||
* holder class for the static field "debug" to delay its initialization
|
||||
*/
|
||||
private static class DebugHolder {
|
||||
private static final Debug debug = Debug.getInstance("scl");
|
||||
}
|
||||
|
||||
/*
|
||||
* Returned cached ProtectionDomain for the specified CodeSource.
|
||||
*/
|
||||
@ -222,9 +227,9 @@ public class SecureClassLoader extends ClassLoader {
|
||||
= SecureClassLoader.this.getPermissions(cs);
|
||||
ProtectionDomain pd = new ProtectionDomain(
|
||||
cs, perms, SecureClassLoader.this, null);
|
||||
if (debug != null) {
|
||||
debug.println(" getPermissions " + pd);
|
||||
debug.println("");
|
||||
if (DebugHolder.debug != null) {
|
||||
DebugHolder.debug.println(" getPermissions " + pd);
|
||||
DebugHolder.debug.println("");
|
||||
}
|
||||
return pd;
|
||||
}
|
||||
|
@ -549,7 +549,7 @@ public final class Security {
|
||||
|
||||
/**
|
||||
* Returns an array containing all installed providers that satisfy the
|
||||
* specified* selection criteria, or null if no such providers have been
|
||||
* specified selection criteria, or null if no such providers have been
|
||||
* installed. The returned providers are ordered
|
||||
* according to their
|
||||
* {@linkplain #insertProviderAt(java.security.Provider, int)
|
||||
|
@ -63,7 +63,7 @@ public class InvalidKeySpecException extends GeneralSecurityException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code InvalidKeySpecException} with the specified
|
||||
* Creates an {@code InvalidKeySpecException} with the specified
|
||||
* detail message and cause.
|
||||
*
|
||||
* @param message the detail message (which is saved for later retrieval
|
||||
@ -78,7 +78,7 @@ public class InvalidKeySpecException extends GeneralSecurityException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code InvalidKeySpecException} with the specified cause
|
||||
* Creates an {@code InvalidKeySpecException} with the specified cause
|
||||
* and a detail message of {@code (cause==null ? null : cause.toString())}
|
||||
* (which typically contains the class and detail message of
|
||||
* {@code cause}).
|
||||
|
@ -1068,7 +1068,7 @@ public class MessageFormat extends Format {
|
||||
* index information as described above.
|
||||
* @return An <code>Object</code> array parsed from the string. In case of
|
||||
* error, returns null.
|
||||
* @throws NullPointerException if {@code source} or {@code pos} is null.
|
||||
* @throws NullPointerException if {@code pos} is null.
|
||||
*/
|
||||
public Object parseObject(String source, ParsePosition pos) {
|
||||
return parse(source, pos);
|
||||
|
@ -799,33 +799,33 @@ public final class Instant
|
||||
* The supported fields behave as follows:
|
||||
* <ul>
|
||||
* <li>{@code NANOS} -
|
||||
* Returns a {@code Instant} with the specified number of nanoseconds added.
|
||||
* Returns an {@code Instant} with the specified number of nanoseconds added.
|
||||
* This is equivalent to {@link #plusNanos(long)}.
|
||||
* <li>{@code MICROS} -
|
||||
* Returns a {@code Instant} with the specified number of microseconds added.
|
||||
* Returns an {@code Instant} with the specified number of microseconds added.
|
||||
* This is equivalent to {@link #plusNanos(long)} with the amount
|
||||
* multiplied by 1,000.
|
||||
* <li>{@code MILLIS} -
|
||||
* Returns a {@code Instant} with the specified number of milliseconds added.
|
||||
* Returns an {@code Instant} with the specified number of milliseconds added.
|
||||
* This is equivalent to {@link #plusNanos(long)} with the amount
|
||||
* multiplied by 1,000,000.
|
||||
* <li>{@code SECONDS} -
|
||||
* Returns a {@code Instant} with the specified number of seconds added.
|
||||
* Returns an {@code Instant} with the specified number of seconds added.
|
||||
* This is equivalent to {@link #plusSeconds(long)}.
|
||||
* <li>{@code MINUTES} -
|
||||
* Returns a {@code Instant} with the specified number of minutes added.
|
||||
* Returns an {@code Instant} with the specified number of minutes added.
|
||||
* This is equivalent to {@link #plusSeconds(long)} with the amount
|
||||
* multiplied by 60.
|
||||
* <li>{@code HOURS} -
|
||||
* Returns a {@code Instant} with the specified number of hours added.
|
||||
* Returns an {@code Instant} with the specified number of hours added.
|
||||
* This is equivalent to {@link #plusSeconds(long)} with the amount
|
||||
* multiplied by 3,600.
|
||||
* <li>{@code HALF_DAYS} -
|
||||
* Returns a {@code Instant} with the specified number of half-days added.
|
||||
* Returns an {@code Instant} with the specified number of half-days added.
|
||||
* This is equivalent to {@link #plusSeconds(long)} with the amount
|
||||
* multiplied by 43,200 (12 hours).
|
||||
* <li>{@code DAYS} -
|
||||
* Returns a {@code Instant} with the specified number of days added.
|
||||
* Returns an {@code Instant} with the specified number of days added.
|
||||
* This is equivalent to {@link #plusSeconds(long)} with the amount
|
||||
* multiplied by 86,400 (24 hours).
|
||||
* </ul>
|
||||
@ -958,7 +958,7 @@ public final class Instant
|
||||
/**
|
||||
* Returns a copy of this instant with the specified amount subtracted.
|
||||
* <p>
|
||||
* This returns a {@code Instant}, based on this one, with the amount
|
||||
* This returns an {@code Instant}, based on this one, with the amount
|
||||
* in terms of the unit subtracted. If it is not possible to subtract the amount,
|
||||
* because the unit is not supported or for some other reason, an exception is thrown.
|
||||
* <p>
|
||||
|
@ -665,7 +665,7 @@ public final class LocalDateTime
|
||||
* The {@link #isSupported(TemporalField) supported fields} will return valid
|
||||
* values based on this date-time, except {@code NANO_OF_DAY}, {@code MICRO_OF_DAY},
|
||||
* {@code EPOCH_DAY} and {@code PROLEPTIC_MONTH} which are too large to fit in
|
||||
* an {@code int} and throw a {@code UnsupportedTemporalTypeException}.
|
||||
* an {@code int} and throw an {@code UnsupportedTemporalTypeException}.
|
||||
* All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
|
||||
* <p>
|
||||
* If the field is not a {@code ChronoField}, then the result of this method
|
||||
|
@ -619,7 +619,7 @@ public final class LocalTime
|
||||
* If the field is a {@link ChronoField} then the query is implemented here.
|
||||
* The {@link #isSupported(TemporalField) supported fields} will return valid
|
||||
* values based on this time, except {@code NANO_OF_DAY} and {@code MICRO_OF_DAY}
|
||||
* which are too large to fit in an {@code int} and throw a {@code UnsupportedTemporalTypeException}.
|
||||
* which are too large to fit in an {@code int} and throw an {@code UnsupportedTemporalTypeException}.
|
||||
* All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
|
||||
* <p>
|
||||
* If the field is not a {@code ChronoField}, then the result of this method
|
||||
|
@ -576,7 +576,7 @@ public final class OffsetDateTime
|
||||
* The {@link #isSupported(TemporalField) supported fields} will return valid
|
||||
* values based on this date-time, except {@code NANO_OF_DAY}, {@code MICRO_OF_DAY},
|
||||
* {@code EPOCH_DAY}, {@code PROLEPTIC_MONTH} and {@code INSTANT_SECONDS} which are too
|
||||
* large to fit in an {@code int} and throw a {@code UnsupportedTemporalTypeException}.
|
||||
* large to fit in an {@code int} and throw an {@code UnsupportedTemporalTypeException}.
|
||||
* All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
|
||||
* <p>
|
||||
* If the field is not a {@code ChronoField}, then the result of this method
|
||||
|
@ -481,7 +481,7 @@ public final class OffsetTime
|
||||
* If the field is a {@link ChronoField} then the query is implemented here.
|
||||
* The {@link #isSupported(TemporalField) supported fields} will return valid
|
||||
* values based on this time, except {@code NANO_OF_DAY} and {@code MICRO_OF_DAY}
|
||||
* which are too large to fit in an {@code int} and throw a {@code UnsupportedTemporalTypeException}.
|
||||
* which are too large to fit in an {@code int} and throw an {@code UnsupportedTemporalTypeException}.
|
||||
* All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
|
||||
* <p>
|
||||
* If the field is not a {@code ChronoField}, then the result of this method
|
||||
|
@ -793,7 +793,7 @@ public final class ZonedDateTime
|
||||
* The {@link #isSupported(TemporalField) supported fields} will return valid
|
||||
* values based on this date-time, except {@code NANO_OF_DAY}, {@code MICRO_OF_DAY},
|
||||
* {@code EPOCH_DAY}, {@code PROLEPTIC_MONTH} and {@code INSTANT_SECONDS} which are too
|
||||
* large to fit in an {@code int} and throw a {@code UnsupportedTemporalTypeException}.
|
||||
* large to fit in an {@code int} and throw an {@code UnsupportedTemporalTypeException}.
|
||||
* All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
|
||||
* <p>
|
||||
* If the field is not a {@code ChronoField}, then the result of this method
|
||||
|
@ -308,6 +308,7 @@ import java.util.Set;
|
||||
* N nano-of-day number 1234000000
|
||||
*
|
||||
* V time-zone ID zone-id America/Los_Angeles; Z; -08:30
|
||||
* v generic time-zone name zone-name Pacific Time; PT
|
||||
* z time-zone name zone-name Pacific Standard Time; PST
|
||||
* O localized zone-offset offset-O GMT+8; GMT+08:00; UTC-08:00
|
||||
* X zone-offset 'Z' for zero offset-X Z; -08; -0830; -08:30; -083015; -08:30:15
|
||||
@ -365,9 +366,17 @@ import java.util.Set;
|
||||
* letters throws {@code IllegalArgumentException}.
|
||||
* <p>
|
||||
* <b>Zone names</b>: This outputs the display name of the time-zone ID. If the
|
||||
* count of letters is one, two or three, then the short name is output. If the
|
||||
* count of letters is four, then the full name is output. Five or more letters
|
||||
* throws {@code IllegalArgumentException}.
|
||||
* pattern letter is 'z' the output is the daylight savings aware zone name.
|
||||
* If there is insufficient information to determine whether DST applies,
|
||||
* the name ignoring daylight savings time will be used.
|
||||
* If the count of letters is one, two or three, then the short name is output.
|
||||
* If the count of letters is four, then the full name is output.
|
||||
* Five or more letters throws {@code IllegalArgumentException}.
|
||||
* <p>
|
||||
* If the pattern letter is 'v' the output provides the zone name ignoring
|
||||
* daylight savings time. If the count of letters is one, then the short name is output.
|
||||
* If the count of letters is four, then the full name is output.
|
||||
* Two, three and five or more letters throw {@code IllegalArgumentException}.
|
||||
* <p>
|
||||
* <b>Offset X and x</b>: This formats the offset based on the number of pattern
|
||||
* letters. One letter outputs just the hour, such as '+01', unless the minute
|
||||
|
@ -81,9 +81,11 @@ import java.time.DateTimeException;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.chrono.ChronoLocalDate;
|
||||
import java.time.chrono.ChronoLocalDateTime;
|
||||
import java.time.chrono.Chronology;
|
||||
import java.time.chrono.Era;
|
||||
import java.time.chrono.IsoChronology;
|
||||
@ -1157,10 +1159,11 @@ public final class DateTimeFormatterBuilder {
|
||||
* result of {@link ZoneOffset#getId()}.
|
||||
* If the zone is not an offset, the textual name will be looked up
|
||||
* for the locale set in the {@link DateTimeFormatter}.
|
||||
* If the temporal object being printed represents an instant, then the text
|
||||
* will be the summer or winter time text as appropriate.
|
||||
* If the temporal object being printed represents an instant, or if it is a
|
||||
* local date-time that is not in a daylight saving gap or overlap then
|
||||
* the text will be the summer or winter time text as appropriate.
|
||||
* If the lookup for text does not find any suitable result, then the
|
||||
* {@link ZoneId#getId() ID} will be printed instead.
|
||||
* {@link ZoneId#getId() ID} will be printed.
|
||||
* If the zone cannot be obtained then an exception is thrown unless the
|
||||
* section of the formatter is optional.
|
||||
* <p>
|
||||
@ -1177,7 +1180,7 @@ public final class DateTimeFormatterBuilder {
|
||||
* @return this, for chaining, not null
|
||||
*/
|
||||
public DateTimeFormatterBuilder appendZoneText(TextStyle textStyle) {
|
||||
appendInternal(new ZoneTextPrinterParser(textStyle, null));
|
||||
appendInternal(new ZoneTextPrinterParser(textStyle, null, false));
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -1193,10 +1196,11 @@ public final class DateTimeFormatterBuilder {
|
||||
* result of {@link ZoneOffset#getId()}.
|
||||
* If the zone is not an offset, the textual name will be looked up
|
||||
* for the locale set in the {@link DateTimeFormatter}.
|
||||
* If the temporal object being printed represents an instant, then the text
|
||||
* If the temporal object being printed represents an instant, or if it is a
|
||||
* local date-time that is not in a daylight saving gap or overlap, then the text
|
||||
* will be the summer or winter time text as appropriate.
|
||||
* If the lookup for text does not find any suitable result, then the
|
||||
* {@link ZoneId#getId() ID} will be printed instead.
|
||||
* {@link ZoneId#getId() ID} will be printed.
|
||||
* If the zone cannot be obtained then an exception is thrown unless the
|
||||
* section of the formatter is optional.
|
||||
* <p>
|
||||
@ -1220,7 +1224,70 @@ public final class DateTimeFormatterBuilder {
|
||||
public DateTimeFormatterBuilder appendZoneText(TextStyle textStyle,
|
||||
Set<ZoneId> preferredZones) {
|
||||
Objects.requireNonNull(preferredZones, "preferredZones");
|
||||
appendInternal(new ZoneTextPrinterParser(textStyle, preferredZones));
|
||||
appendInternal(new ZoneTextPrinterParser(textStyle, preferredZones, false));
|
||||
return this;
|
||||
}
|
||||
//----------------------------------------------------------------------
|
||||
/**
|
||||
* Appends the generic time-zone name, such as 'Pacific Time', to the formatter.
|
||||
* <p>
|
||||
* This appends an instruction to format/parse the generic textual
|
||||
* name of the zone to the builder. The generic name is the same throughout the whole
|
||||
* year, ignoring any daylight saving changes. For example, 'Pacific Time' is the
|
||||
* generic name, whereas 'Pacific Standard Time' and 'Pacific Daylight Time' are the
|
||||
* specific names, see {@link #appendZoneText(TextStyle)}.
|
||||
* <p>
|
||||
* During formatting, the zone is obtained using a mechanism equivalent
|
||||
* to querying the temporal with {@link TemporalQueries#zoneId()}.
|
||||
* If the zone is a {@code ZoneOffset} it will be printed using the
|
||||
* result of {@link ZoneOffset#getId()}.
|
||||
* If the zone is not an offset, the textual name will be looked up
|
||||
* for the locale set in the {@link DateTimeFormatter}.
|
||||
* If the lookup for text does not find any suitable result, then the
|
||||
* {@link ZoneId#getId() ID} will be printed.
|
||||
* If the zone cannot be obtained then an exception is thrown unless the
|
||||
* section of the formatter is optional.
|
||||
* <p>
|
||||
* During parsing, either the textual zone name, the zone ID or the offset
|
||||
* is accepted. Many textual zone names are not unique, such as CST can be
|
||||
* for both "Central Standard Time" and "China Standard Time". In this
|
||||
* situation, the zone id will be determined by the region information from
|
||||
* formatter's {@link DateTimeFormatter#getLocale() locale} and the standard
|
||||
* zone id for that area, for example, America/New_York for the America Eastern zone.
|
||||
* The {@link #appendGenericZoneText(TextStyle, Set)} may be used
|
||||
* to specify a set of preferred {@link ZoneId} in this situation.
|
||||
*
|
||||
* @param textStyle the text style to use, not null
|
||||
* @return this, for chaining, not null
|
||||
*/
|
||||
public DateTimeFormatterBuilder appendGenericZoneText(TextStyle textStyle) {
|
||||
appendInternal(new ZoneTextPrinterParser(textStyle, null, true));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the generic time-zone name, such as 'Pacific Time', to the formatter.
|
||||
* <p>
|
||||
* This appends an instruction to format/parse the generic textual
|
||||
* name of the zone to the builder. The generic name is the same throughout the whole
|
||||
* year, ignoring any daylight saving changes. For example, 'Pacific Time' is the
|
||||
* generic name, whereas 'Pacific Standard Time' and 'Pacific Daylight Time' are the
|
||||
* specific names, see {@link #appendZoneText(TextStyle)}.
|
||||
* <p>
|
||||
* This method also allows a set of preferred {@link ZoneId} to be
|
||||
* specified for parsing. The matched preferred zone id will be used if the
|
||||
* textural zone name being parsed is not unique.
|
||||
* <p>
|
||||
* See {@link #appendGenericZoneText(TextStyle)} for details about
|
||||
* formatting and parsing.
|
||||
*
|
||||
* @param textStyle the text style to use, not null
|
||||
* @param preferredZones the set of preferred zone ids, not null
|
||||
* @return this, for chaining, not null
|
||||
*/
|
||||
public DateTimeFormatterBuilder appendGenericZoneText(TextStyle textStyle,
|
||||
Set<ZoneId> preferredZones) {
|
||||
appendInternal(new ZoneTextPrinterParser(textStyle, preferredZones, true));
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -1416,6 +1483,7 @@ public final class DateTimeFormatterBuilder {
|
||||
* N nano-of-day number 1234000000
|
||||
*
|
||||
* V time-zone ID zone-id America/Los_Angeles; Z; -08:30
|
||||
* v generic time-zone name zone-name PT, Pacific Time
|
||||
* z time-zone name zone-name Pacific Standard Time; PST
|
||||
* O localized zone-offset offset-O GMT+8; GMT+08:00; UTC-08:00;
|
||||
* X zone-offset 'Z' for zero offset-X Z; -08; -0830; -08:30; -083015; -08:30:15
|
||||
@ -1537,6 +1605,8 @@ public final class DateTimeFormatterBuilder {
|
||||
* Pattern Count Equivalent builder methods
|
||||
* ------- ----- --------------------------
|
||||
* VV 2 appendZoneId()
|
||||
* v 1 appendGenericZoneText(TextStyle.SHORT)
|
||||
* vvvv 4 appendGenericZoneText(TextStyle.FULL)
|
||||
* z 1 appendZoneText(TextStyle.SHORT)
|
||||
* zz 2 appendZoneText(TextStyle.SHORT)
|
||||
* zzz 3 appendZoneText(TextStyle.SHORT)
|
||||
@ -1643,6 +1713,14 @@ public final class DateTimeFormatterBuilder {
|
||||
throw new IllegalArgumentException("Pattern letter count must be 2: " + cur);
|
||||
}
|
||||
appendZoneId();
|
||||
} else if (cur == 'v') {
|
||||
if (count == 1) {
|
||||
appendGenericZoneText(TextStyle.SHORT);
|
||||
} else if (count == 4) {
|
||||
appendGenericZoneText(TextStyle.FULL);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Wrong number of pattern letters: " + cur);
|
||||
}
|
||||
} else if (cur == 'Z') {
|
||||
if (count < 4) {
|
||||
appendOffset("+HHMM", "+0000");
|
||||
@ -1894,6 +1972,8 @@ public final class DateTimeFormatterBuilder {
|
||||
// 310 - z - time-zone names, matches LDML and SimpleDateFormat 1 to 4
|
||||
// 310 - Z - matches SimpleDateFormat and LDML
|
||||
// 310 - V - time-zone id, matches LDML
|
||||
// 310 - v - general timezone names, not matching exactly with LDML because LDML specify to fall back
|
||||
// to 'VVVV' if general-nonlocation unavailable but here it's not falling back because of lack of data
|
||||
// 310 - p - prefix for padding
|
||||
// 310 - X - matches LDML, almost matches SDF for 1, exact match 2&3, extended 4&5
|
||||
// 310 - x - matches LDML
|
||||
@ -1901,7 +1981,6 @@ public final class DateTimeFormatterBuilder {
|
||||
// LDML - U - cycle year name, not supported by 310 yet
|
||||
// LDML - l - deprecated
|
||||
// LDML - j - not relevant
|
||||
// LDML - v,V - extended time-zone names
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
@ -3723,9 +3802,12 @@ public final class DateTimeFormatterBuilder {
|
||||
/** The preferred zoneid map */
|
||||
private Set<String> preferredZones;
|
||||
|
||||
ZoneTextPrinterParser(TextStyle textStyle, Set<ZoneId> preferredZones) {
|
||||
/** Display in generic time-zone format. True in case of pattern letter 'v' */
|
||||
private final boolean isGeneric;
|
||||
ZoneTextPrinterParser(TextStyle textStyle, Set<ZoneId> preferredZones, boolean isGeneric) {
|
||||
super(TemporalQueries.zone(), "ZoneText(" + textStyle + ")");
|
||||
this.textStyle = Objects.requireNonNull(textStyle, "textStyle");
|
||||
this.isGeneric = isGeneric;
|
||||
if (preferredZones != null && preferredZones.size() != 0) {
|
||||
this.preferredZones = new HashSet<>();
|
||||
for (ZoneId id : preferredZones) {
|
||||
@ -3788,11 +3870,21 @@ public final class DateTimeFormatterBuilder {
|
||||
String zname = zone.getId();
|
||||
if (!(zone instanceof ZoneOffset)) {
|
||||
TemporalAccessor dt = context.getTemporal();
|
||||
String name = getDisplayName(zname,
|
||||
dt.isSupported(ChronoField.INSTANT_SECONDS)
|
||||
? (zone.getRules().isDaylightSavings(Instant.from(dt)) ? DST : STD)
|
||||
: GENERIC,
|
||||
context.getLocale());
|
||||
int type = GENERIC;
|
||||
if (!isGeneric) {
|
||||
if (dt.isSupported(ChronoField.INSTANT_SECONDS)) {
|
||||
type = zone.getRules().isDaylightSavings(Instant.from(dt)) ? DST : STD;
|
||||
} else if (dt.isSupported(ChronoField.EPOCH_DAY) &&
|
||||
dt.isSupported(ChronoField.NANO_OF_DAY)) {
|
||||
LocalDate date = LocalDate.ofEpochDay(dt.getLong(ChronoField.EPOCH_DAY));
|
||||
LocalTime time = LocalTime.ofNanoOfDay(dt.getLong(ChronoField.NANO_OF_DAY));
|
||||
LocalDateTime ldt = date.atTime(time);
|
||||
if (zone.getRules().getTransition(ldt) == null) {
|
||||
type = zone.getRules().isDaylightSavings(ldt.atZone(zone).toInstant()) ? DST : STD;
|
||||
}
|
||||
}
|
||||
}
|
||||
String name = getDisplayName(zname, type, context.getLocale());
|
||||
if (name != null) {
|
||||
zname = name;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 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
|
||||
@ -402,6 +402,12 @@ public final class IsoFields {
|
||||
long moy = temporal.getLong(MONTH_OF_YEAR);
|
||||
return ((moy + 2) / 3);
|
||||
}
|
||||
public ValueRange rangeRefinedBy(TemporalAccessor temporal) {
|
||||
if (isSupportedBy(temporal) == false) {
|
||||
throw new UnsupportedTemporalTypeException("Unsupported field: QuarterOfYear");
|
||||
}
|
||||
return super.rangeRefinedBy(temporal);
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <R extends Temporal> R adjustInto(R temporal, long newValue) {
|
||||
@ -529,6 +535,12 @@ public final class IsoFields {
|
||||
}
|
||||
return getWeekBasedYear(LocalDate.from(temporal));
|
||||
}
|
||||
public ValueRange rangeRefinedBy(TemporalAccessor temporal) {
|
||||
if (isSupportedBy(temporal) == false) {
|
||||
throw new UnsupportedTemporalTypeException("Unsupported field: WeekBasedYear");
|
||||
}
|
||||
return super.rangeRefinedBy(temporal);
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <R extends Temporal> R adjustInto(R temporal, long newValue) {
|
||||
|
@ -614,7 +614,7 @@ public final class ZoneRules implements Serializable {
|
||||
* One technique, using this method, would be:
|
||||
* <pre>
|
||||
* ZoneOffsetTransition trans = rules.getTransition(localDT);
|
||||
* if (trans == null) {
|
||||
* if (trans != null) {
|
||||
* // Gap or Overlap: determine what to do from transition
|
||||
* } else {
|
||||
* // Normal case: only one valid offset
|
||||
|
@ -267,7 +267,7 @@ public interface Comparator<T> {
|
||||
|
||||
/**
|
||||
* Returns a lexicographic-order comparator with a function that
|
||||
* extracts a {@code int} sort key.
|
||||
* extracts an {@code int} sort key.
|
||||
*
|
||||
* @implSpec This default implementation behaves as if {@code
|
||||
* thenComparing(comparingInt(keyExtractor))}.
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2009, 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
|
||||
@ -146,12 +146,26 @@ final class DualPivotQuicksort {
|
||||
}
|
||||
}
|
||||
|
||||
// Check special cases
|
||||
// Implementation note: variable "right" is increased by 1.
|
||||
if (run[count] == right++) { // The last run contains one element
|
||||
run[++count] = right;
|
||||
} else if (count <= 1) { // The array is already sorted
|
||||
// These invariants should hold true:
|
||||
// run[0] = 0
|
||||
// run[<last>] = right + 1; (terminator)
|
||||
|
||||
if (count == 0) {
|
||||
// A single equal run
|
||||
return;
|
||||
} else if (count == 1 && run[count] > right) {
|
||||
// Either a single ascending or a transformed descending run.
|
||||
// Always check that a final run is a proper terminator, otherwise
|
||||
// we have an unterminated trailing run, to handle downstream.
|
||||
return;
|
||||
}
|
||||
right++;
|
||||
if (run[count] < right) {
|
||||
// Corner case: the final run is not a terminator. This may happen
|
||||
// if a final run is an equals run, or there is a single-element run
|
||||
// at the end. Fix up by adding a proper terminator at the end.
|
||||
// Note that we terminate with (right + 1), incremented earlier.
|
||||
run[++count] = right;
|
||||
}
|
||||
|
||||
// Determine alternation base for merge
|
||||
@ -598,12 +612,26 @@ final class DualPivotQuicksort {
|
||||
}
|
||||
}
|
||||
|
||||
// Check special cases
|
||||
// Implementation note: variable "right" is increased by 1.
|
||||
if (run[count] == right++) { // The last run contains one element
|
||||
run[++count] = right;
|
||||
} else if (count <= 1) { // The array is already sorted
|
||||
// These invariants should hold true:
|
||||
// run[0] = 0
|
||||
// run[<last>] = right + 1; (terminator)
|
||||
|
||||
if (count == 0) {
|
||||
// A single equal run
|
||||
return;
|
||||
} else if (count == 1 && run[count] > right) {
|
||||
// Either a single ascending or a transformed descending run.
|
||||
// Always check that a final run is a proper terminator, otherwise
|
||||
// we have an unterminated trailing run, to handle downstream.
|
||||
return;
|
||||
}
|
||||
right++;
|
||||
if (run[count] < right) {
|
||||
// Corner case: the final run is not a terminator. This may happen
|
||||
// if a final run is an equals run, or there is a single-element run
|
||||
// at the end. Fix up by adding a proper terminator at the end.
|
||||
// Note that we terminate with (right + 1), incremented earlier.
|
||||
run[++count] = right;
|
||||
}
|
||||
|
||||
// Determine alternation base for merge
|
||||
@ -1086,12 +1114,26 @@ final class DualPivotQuicksort {
|
||||
}
|
||||
}
|
||||
|
||||
// Check special cases
|
||||
// Implementation note: variable "right" is increased by 1.
|
||||
if (run[count] == right++) { // The last run contains one element
|
||||
run[++count] = right;
|
||||
} else if (count <= 1) { // The array is already sorted
|
||||
// These invariants should hold true:
|
||||
// run[0] = 0
|
||||
// run[<last>] = right + 1; (terminator)
|
||||
|
||||
if (count == 0) {
|
||||
// A single equal run
|
||||
return;
|
||||
} else if (count == 1 && run[count] > right) {
|
||||
// Either a single ascending or a transformed descending run.
|
||||
// Always check that a final run is a proper terminator, otherwise
|
||||
// we have an unterminated trailing run, to handle downstream.
|
||||
return;
|
||||
}
|
||||
right++;
|
||||
if (run[count] < right) {
|
||||
// Corner case: the final run is not a terminator. This may happen
|
||||
// if a final run is an equals run, or there is a single-element run
|
||||
// at the end. Fix up by adding a proper terminator at the end.
|
||||
// Note that we terminate with (right + 1), incremented earlier.
|
||||
run[++count] = right;
|
||||
}
|
||||
|
||||
// Determine alternation base for merge
|
||||
@ -1574,12 +1616,26 @@ final class DualPivotQuicksort {
|
||||
}
|
||||
}
|
||||
|
||||
// Check special cases
|
||||
// Implementation note: variable "right" is increased by 1.
|
||||
if (run[count] == right++) { // The last run contains one element
|
||||
run[++count] = right;
|
||||
} else if (count <= 1) { // The array is already sorted
|
||||
// These invariants should hold true:
|
||||
// run[0] = 0
|
||||
// run[<last>] = right + 1; (terminator)
|
||||
|
||||
if (count == 0) {
|
||||
// A single equal run
|
||||
return;
|
||||
} else if (count == 1 && run[count] > right) {
|
||||
// Either a single ascending or a transformed descending run.
|
||||
// Always check that a final run is a proper terminator, otherwise
|
||||
// we have an unterminated trailing run, to handle downstream.
|
||||
return;
|
||||
}
|
||||
right++;
|
||||
if (run[count] < right) {
|
||||
// Corner case: the final run is not a terminator. This may happen
|
||||
// if a final run is an equals run, or there is a single-element run
|
||||
// at the end. Fix up by adding a proper terminator at the end.
|
||||
// Note that we terminate with (right + 1), incremented earlier.
|
||||
run[++count] = right;
|
||||
}
|
||||
|
||||
// Determine alternation base for merge
|
||||
@ -2158,12 +2214,26 @@ final class DualPivotQuicksort {
|
||||
}
|
||||
}
|
||||
|
||||
// Check special cases
|
||||
// Implementation note: variable "right" is increased by 1.
|
||||
if (run[count] == right++) { // The last run contains one element
|
||||
run[++count] = right;
|
||||
} else if (count <= 1) { // The array is already sorted
|
||||
// These invariants should hold true:
|
||||
// run[0] = 0
|
||||
// run[<last>] = right + 1; (terminator)
|
||||
|
||||
if (count == 0) {
|
||||
// A single equal run
|
||||
return;
|
||||
} else if (count == 1 && run[count] > right) {
|
||||
// Either a single ascending or a transformed descending run.
|
||||
// Always check that a final run is a proper terminator, otherwise
|
||||
// we have an unterminated trailing run, to handle downstream.
|
||||
return;
|
||||
}
|
||||
right++;
|
||||
if (run[count] < right) {
|
||||
// Corner case: the final run is not a terminator. This may happen
|
||||
// if a final run is an equals run, or there is a single-element run
|
||||
// at the end. Fix up by adding a proper terminator at the end.
|
||||
// Note that we terminate with (right + 1), incremented earlier.
|
||||
run[++count] = right;
|
||||
}
|
||||
|
||||
// Determine alternation base for merge
|
||||
@ -2701,12 +2771,26 @@ final class DualPivotQuicksort {
|
||||
}
|
||||
}
|
||||
|
||||
// Check special cases
|
||||
// Implementation note: variable "right" is increased by 1.
|
||||
if (run[count] == right++) { // The last run contains one element
|
||||
run[++count] = right;
|
||||
} else if (count <= 1) { // The array is already sorted
|
||||
// These invariants should hold true:
|
||||
// run[0] = 0
|
||||
// run[<last>] = right + 1; (terminator)
|
||||
|
||||
if (count == 0) {
|
||||
// A single equal run
|
||||
return;
|
||||
} else if (count == 1 && run[count] > right) {
|
||||
// Either a single ascending or a transformed descending run.
|
||||
// Always check that a final run is a proper terminator, otherwise
|
||||
// we have an unterminated trailing run, to handle downstream.
|
||||
return;
|
||||
}
|
||||
right++;
|
||||
if (run[count] < right) {
|
||||
// Corner case: the final run is not a terminator. This may happen
|
||||
// if a final run is an equals run, or there is a single-element run
|
||||
// at the end. Fix up by adding a proper terminator at the end.
|
||||
// Note that we terminate with (right + 1), incremented earlier.
|
||||
run[++count] = right;
|
||||
}
|
||||
|
||||
// Determine alternation base for merge
|
||||
|
@ -0,0 +1,375 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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 java.util.regex;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Pattern.CharPredicate;
|
||||
import java.util.regex.Pattern.BmpCharPredicate;
|
||||
|
||||
class CharPredicates {
|
||||
|
||||
static final CharPredicate ALPHABETIC = Character::isAlphabetic;
|
||||
|
||||
// \p{gc=Decimal_Number}
|
||||
static final CharPredicate DIGIT = Character::isDigit;
|
||||
|
||||
static final CharPredicate LETTER = Character::isLetter;
|
||||
|
||||
static final CharPredicate IDEOGRAPHIC = Character::isIdeographic;
|
||||
|
||||
static final CharPredicate LOWERCASE = Character::isLowerCase;
|
||||
|
||||
static final CharPredicate UPPERCASE = Character::isUpperCase;
|
||||
|
||||
static final CharPredicate TITLECASE = Character::isTitleCase;
|
||||
|
||||
// \p{Whitespace}
|
||||
static final CharPredicate WHITE_SPACE = ch ->
|
||||
((((1 << Character.SPACE_SEPARATOR) |
|
||||
(1 << Character.LINE_SEPARATOR) |
|
||||
(1 << Character.PARAGRAPH_SEPARATOR)) >> Character.getType(ch)) & 1)
|
||||
!= 0 || (ch >= 0x9 && ch <= 0xd) || (ch == 0x85);
|
||||
|
||||
// \p{gc=Control}
|
||||
static final CharPredicate CONTROL = ch ->
|
||||
Character.getType(ch) == Character.CONTROL;
|
||||
|
||||
// \p{gc=Punctuation}
|
||||
static final CharPredicate PUNCTUATION = ch ->
|
||||
((((1 << Character.CONNECTOR_PUNCTUATION) |
|
||||
(1 << Character.DASH_PUNCTUATION) |
|
||||
(1 << Character.START_PUNCTUATION) |
|
||||
(1 << Character.END_PUNCTUATION) |
|
||||
(1 << Character.OTHER_PUNCTUATION) |
|
||||
(1 << Character.INITIAL_QUOTE_PUNCTUATION) |
|
||||
(1 << Character.FINAL_QUOTE_PUNCTUATION)) >> Character.getType(ch)) & 1)
|
||||
!= 0;
|
||||
|
||||
// \p{gc=Decimal_Number}
|
||||
// \p{Hex_Digit} -> PropList.txt: Hex_Digit
|
||||
static final CharPredicate HEX_DIGIT = DIGIT.union(
|
||||
ch -> (ch >= 0x0030 && ch <= 0x0039) ||
|
||||
(ch >= 0x0041 && ch <= 0x0046) ||
|
||||
(ch >= 0x0061 && ch <= 0x0066) ||
|
||||
(ch >= 0xFF10 && ch <= 0xFF19) ||
|
||||
(ch >= 0xFF21 && ch <= 0xFF26) ||
|
||||
(ch >= 0xFF41 && ch <= 0xFF46));
|
||||
|
||||
static final CharPredicate ASSIGNED = ch ->
|
||||
Character.getType(ch) != Character.UNASSIGNED;
|
||||
|
||||
// PropList.txt:Noncharacter_Code_Point
|
||||
static final CharPredicate NONCHARACTER_CODE_POINT = ch ->
|
||||
(ch & 0xfffe) == 0xfffe || (ch >= 0xfdd0 && ch <= 0xfdef);
|
||||
|
||||
// \p{alpha}
|
||||
// \p{digit}
|
||||
static final CharPredicate ALNUM = ALPHABETIC.union(DIGIT);
|
||||
|
||||
// \p{Whitespace} --
|
||||
// [\N{LF} \N{VT} \N{FF} \N{CR} \N{NEL} -> 0xa, 0xb, 0xc, 0xd, 0x85
|
||||
// \p{gc=Line_Separator}
|
||||
// \p{gc=Paragraph_Separator}]
|
||||
static final CharPredicate BLANK = ch ->
|
||||
Character.getType(ch) == Character.SPACE_SEPARATOR ||
|
||||
ch == 0x9; // \N{HT}
|
||||
|
||||
// [^
|
||||
// \p{space}
|
||||
// \p{gc=Control}
|
||||
// \p{gc=Surrogate}
|
||||
// \p{gc=Unassigned}]
|
||||
static final CharPredicate GRAPH = ch ->
|
||||
((((1 << Character.SPACE_SEPARATOR) |
|
||||
(1 << Character.LINE_SEPARATOR) |
|
||||
(1 << Character.PARAGRAPH_SEPARATOR) |
|
||||
(1 << Character.CONTROL) |
|
||||
(1 << Character.SURROGATE) |
|
||||
(1 << Character.UNASSIGNED)) >> Character.getType(ch)) & 1)
|
||||
== 0;
|
||||
|
||||
// \p{graph}
|
||||
// \p{blank}
|
||||
// -- \p{cntrl}
|
||||
static final CharPredicate PRINT = GRAPH.union(BLANK).and(CONTROL.negate());
|
||||
|
||||
// 200C..200D PropList.txt:Join_Control
|
||||
static final CharPredicate JOIN_CONTROL = ch -> ch == 0x200C || ch == 0x200D;
|
||||
|
||||
// \p{alpha}
|
||||
// \p{gc=Mark}
|
||||
// \p{digit}
|
||||
// \p{gc=Connector_Punctuation}
|
||||
// \p{Join_Control} 200C..200D
|
||||
static final CharPredicate WORD =
|
||||
ALPHABETIC.union(ch -> ((((1 << Character.NON_SPACING_MARK) |
|
||||
(1 << Character.ENCLOSING_MARK) |
|
||||
(1 << Character.COMBINING_SPACING_MARK) |
|
||||
(1 << Character.DECIMAL_DIGIT_NUMBER) |
|
||||
(1 << Character.CONNECTOR_PUNCTUATION))
|
||||
>> Character.getType(ch)) & 1) != 0,
|
||||
JOIN_CONTROL);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private static final HashMap<String, CharPredicate> posix = new HashMap<>(12);
|
||||
private static final HashMap<String, CharPredicate> uprops = new HashMap<>(18);
|
||||
|
||||
private static void defPosix(String name, CharPredicate p) {
|
||||
posix.put(name, p);
|
||||
}
|
||||
private static void defUProp(String name, CharPredicate p) {
|
||||
uprops.put(name, p);
|
||||
}
|
||||
|
||||
static {
|
||||
defPosix("ALPHA", ALPHABETIC);
|
||||
defPosix("LOWER", LOWERCASE);
|
||||
defPosix("UPPER", UPPERCASE);
|
||||
defPosix("SPACE", WHITE_SPACE);
|
||||
defPosix("PUNCT", PUNCTUATION);
|
||||
defPosix("XDIGIT",HEX_DIGIT);
|
||||
defPosix("ALNUM", ALNUM);
|
||||
defPosix("CNTRL", CONTROL);
|
||||
defPosix("DIGIT", DIGIT);
|
||||
defPosix("BLANK", BLANK);
|
||||
defPosix("GRAPH", GRAPH);
|
||||
defPosix("PRINT", PRINT);
|
||||
|
||||
defUProp("ALPHABETIC", ALPHABETIC);
|
||||
defUProp("ASSIGNED", ASSIGNED);
|
||||
defUProp("CONTROL", CONTROL);
|
||||
defUProp("HEXDIGIT", HEX_DIGIT);
|
||||
defUProp("IDEOGRAPHIC", IDEOGRAPHIC);
|
||||
defUProp("JOINCONTROL", JOIN_CONTROL);
|
||||
defUProp("LETTER", LETTER);
|
||||
defUProp("LOWERCASE", LOWERCASE);
|
||||
defUProp("NONCHARACTERCODEPOINT", NONCHARACTER_CODE_POINT);
|
||||
defUProp("TITLECASE", TITLECASE);
|
||||
defUProp("PUNCTUATION", PUNCTUATION);
|
||||
defUProp("UPPERCASE", UPPERCASE);
|
||||
defUProp("WHITESPACE", WHITE_SPACE);
|
||||
defUProp("WORD", WORD);
|
||||
defUProp("WHITE_SPACE", WHITE_SPACE);
|
||||
defUProp("HEX_DIGIT", HEX_DIGIT);
|
||||
defUProp("NONCHARACTER_CODE_POINT", NONCHARACTER_CODE_POINT);
|
||||
defUProp("JOIN_CONTROL", JOIN_CONTROL);
|
||||
}
|
||||
|
||||
public static CharPredicate forUnicodeProperty(String propName) {
|
||||
propName = propName.toUpperCase(Locale.ROOT);
|
||||
CharPredicate p = uprops.get(propName);
|
||||
if (p != null)
|
||||
return p;
|
||||
return posix.get(propName);
|
||||
}
|
||||
|
||||
public static CharPredicate forPOSIXName(String propName) {
|
||||
return posix.get(propName.toUpperCase(Locale.ENGLISH));
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Returns a predicate matching all characters belong to a named
|
||||
* UnicodeScript.
|
||||
*/
|
||||
static CharPredicate forUnicodeScript(String name) {
|
||||
final Character.UnicodeScript script;
|
||||
try {
|
||||
script = Character.UnicodeScript.forName(name);
|
||||
return ch -> script == Character.UnicodeScript.of(ch);
|
||||
} catch (IllegalArgumentException iae) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a predicate matching all characters in a UnicodeBlock.
|
||||
*/
|
||||
static CharPredicate forUnicodeBlock(String name) {
|
||||
final Character.UnicodeBlock block;
|
||||
try {
|
||||
block = Character.UnicodeBlock.forName(name);
|
||||
return ch -> block == Character.UnicodeBlock.of(ch);
|
||||
} catch (IllegalArgumentException iae) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// unicode categories, aliases, properties, java methods ...
|
||||
|
||||
private static final HashMap<String, CharPredicate> props = new HashMap<>(128);
|
||||
|
||||
/**
|
||||
* Returns a predicate matching all characters in a named property.
|
||||
*/
|
||||
static CharPredicate forProperty(String name) {
|
||||
return props.get(name);
|
||||
}
|
||||
|
||||
private static void defProp(String name, CharPredicate p) {
|
||||
props.put(name, p);
|
||||
}
|
||||
|
||||
private static void defCategory(String name, final int typeMask) {
|
||||
CharPredicate p = ch -> (typeMask & (1 << Character.getType(ch))) != 0;
|
||||
props.put(name, p);
|
||||
}
|
||||
|
||||
private static void defRange(String name, final int lower, final int upper) {
|
||||
BmpCharPredicate p = ch -> lower <= ch && ch <= upper;
|
||||
props.put(name, p);
|
||||
}
|
||||
|
||||
private static void defCtype(String name, final int ctype) {
|
||||
BmpCharPredicate p = ch -> ch < 128 && ASCII.isType(ch, ctype);
|
||||
// PrintPattern.pmap.put(p, name);
|
||||
props.put(name, p);
|
||||
}
|
||||
|
||||
static {
|
||||
// Unicode character property aliases, defined in
|
||||
// http://www.unicode.org/Public/UNIDATA/PropertyValueAliases.txt
|
||||
defCategory("Cn", 1<<Character.UNASSIGNED);
|
||||
defCategory("Lu", 1<<Character.UPPERCASE_LETTER);
|
||||
defCategory("Ll", 1<<Character.LOWERCASE_LETTER);
|
||||
defCategory("Lt", 1<<Character.TITLECASE_LETTER);
|
||||
defCategory("Lm", 1<<Character.MODIFIER_LETTER);
|
||||
defCategory("Lo", 1<<Character.OTHER_LETTER);
|
||||
defCategory("Mn", 1<<Character.NON_SPACING_MARK);
|
||||
defCategory("Me", 1<<Character.ENCLOSING_MARK);
|
||||
defCategory("Mc", 1<<Character.COMBINING_SPACING_MARK);
|
||||
defCategory("Nd", 1<<Character.DECIMAL_DIGIT_NUMBER);
|
||||
defCategory("Nl", 1<<Character.LETTER_NUMBER);
|
||||
defCategory("No", 1<<Character.OTHER_NUMBER);
|
||||
defCategory("Zs", 1<<Character.SPACE_SEPARATOR);
|
||||
defCategory("Zl", 1<<Character.LINE_SEPARATOR);
|
||||
defCategory("Zp", 1<<Character.PARAGRAPH_SEPARATOR);
|
||||
defCategory("Cc", 1<<Character.CONTROL);
|
||||
defCategory("Cf", 1<<Character.FORMAT);
|
||||
defCategory("Co", 1<<Character.PRIVATE_USE);
|
||||
defCategory("Cs", 1<<Character.SURROGATE);
|
||||
defCategory("Pd", 1<<Character.DASH_PUNCTUATION);
|
||||
defCategory("Ps", 1<<Character.START_PUNCTUATION);
|
||||
defCategory("Pe", 1<<Character.END_PUNCTUATION);
|
||||
defCategory("Pc", 1<<Character.CONNECTOR_PUNCTUATION);
|
||||
defCategory("Po", 1<<Character.OTHER_PUNCTUATION);
|
||||
defCategory("Sm", 1<<Character.MATH_SYMBOL);
|
||||
defCategory("Sc", 1<<Character.CURRENCY_SYMBOL);
|
||||
defCategory("Sk", 1<<Character.MODIFIER_SYMBOL);
|
||||
defCategory("So", 1<<Character.OTHER_SYMBOL);
|
||||
defCategory("Pi", 1<<Character.INITIAL_QUOTE_PUNCTUATION);
|
||||
defCategory("Pf", 1<<Character.FINAL_QUOTE_PUNCTUATION);
|
||||
defCategory("L", ((1<<Character.UPPERCASE_LETTER) |
|
||||
(1<<Character.LOWERCASE_LETTER) |
|
||||
(1<<Character.TITLECASE_LETTER) |
|
||||
(1<<Character.MODIFIER_LETTER) |
|
||||
(1<<Character.OTHER_LETTER)));
|
||||
defCategory("M", ((1<<Character.NON_SPACING_MARK) |
|
||||
(1<<Character.ENCLOSING_MARK) |
|
||||
(1<<Character.COMBINING_SPACING_MARK)));
|
||||
defCategory("N", ((1<<Character.DECIMAL_DIGIT_NUMBER) |
|
||||
(1<<Character.LETTER_NUMBER) |
|
||||
(1<<Character.OTHER_NUMBER)));
|
||||
defCategory("Z", ((1<<Character.SPACE_SEPARATOR) |
|
||||
(1<<Character.LINE_SEPARATOR) |
|
||||
(1<<Character.PARAGRAPH_SEPARATOR)));
|
||||
defCategory("C", ((1<<Character.CONTROL) |
|
||||
(1<<Character.FORMAT) |
|
||||
(1<<Character.PRIVATE_USE) |
|
||||
(1<<Character.SURROGATE))); // Other
|
||||
defCategory("P", ((1<<Character.DASH_PUNCTUATION) |
|
||||
(1<<Character.START_PUNCTUATION) |
|
||||
(1<<Character.END_PUNCTUATION) |
|
||||
(1<<Character.CONNECTOR_PUNCTUATION) |
|
||||
(1<<Character.OTHER_PUNCTUATION) |
|
||||
(1<<Character.INITIAL_QUOTE_PUNCTUATION) |
|
||||
(1<<Character.FINAL_QUOTE_PUNCTUATION)));
|
||||
defCategory("S", ((1<<Character.MATH_SYMBOL) |
|
||||
(1<<Character.CURRENCY_SYMBOL) |
|
||||
(1<<Character.MODIFIER_SYMBOL) |
|
||||
(1<<Character.OTHER_SYMBOL)));
|
||||
defCategory("LC", ((1<<Character.UPPERCASE_LETTER) |
|
||||
(1<<Character.LOWERCASE_LETTER) |
|
||||
(1<<Character.TITLECASE_LETTER)));
|
||||
defCategory("LD", ((1<<Character.UPPERCASE_LETTER) |
|
||||
(1<<Character.LOWERCASE_LETTER) |
|
||||
(1<<Character.TITLECASE_LETTER) |
|
||||
(1<<Character.MODIFIER_LETTER) |
|
||||
(1<<Character.OTHER_LETTER) |
|
||||
(1<<Character.DECIMAL_DIGIT_NUMBER)));
|
||||
defRange("L1", 0x00, 0xFF); // Latin-1
|
||||
props.put("all", ch -> true);
|
||||
|
||||
// Posix regular expression character classes, defined in
|
||||
// http://www.unix.org/onlinepubs/009695399/basedefs/xbd_chap09.html
|
||||
defRange("ASCII", 0x00, 0x7F); // ASCII
|
||||
defCtype("Alnum", ASCII.ALNUM); // Alphanumeric characters
|
||||
defCtype("Alpha", ASCII.ALPHA); // Alphabetic characters
|
||||
defCtype("Blank", ASCII.BLANK); // Space and tab characters
|
||||
defCtype("Cntrl", ASCII.CNTRL); // Control characters
|
||||
defRange("Digit", '0', '9'); // Numeric characters
|
||||
defCtype("Graph", ASCII.GRAPH); // printable and visible
|
||||
defRange("Lower", 'a', 'z'); // Lower-case alphabetic
|
||||
defRange("Print", 0x20, 0x7E); // Printable characters
|
||||
defCtype("Punct", ASCII.PUNCT); // Punctuation characters
|
||||
defCtype("Space", ASCII.SPACE); // Space characters
|
||||
defRange("Upper", 'A', 'Z'); // Upper-case alphabetic
|
||||
defCtype("XDigit",ASCII.XDIGIT); // hexadecimal digits
|
||||
|
||||
// Java character properties, defined by methods in Character.java
|
||||
defProp("javaLowerCase", java.lang.Character::isLowerCase);
|
||||
defProp("javaUpperCase", Character::isUpperCase);
|
||||
defProp("javaAlphabetic", java.lang.Character::isAlphabetic);
|
||||
defProp("javaIdeographic", java.lang.Character::isIdeographic);
|
||||
defProp("javaTitleCase", java.lang.Character::isTitleCase);
|
||||
defProp("javaDigit", java.lang.Character::isDigit);
|
||||
defProp("javaDefined", java.lang.Character::isDefined);
|
||||
defProp("javaLetter", java.lang.Character::isLetter);
|
||||
defProp("javaLetterOrDigit", java.lang.Character::isLetterOrDigit);
|
||||
defProp("javaJavaIdentifierStart", java.lang.Character::isJavaIdentifierStart);
|
||||
defProp("javaJavaIdentifierPart", java.lang.Character::isJavaIdentifierPart);
|
||||
defProp("javaUnicodeIdentifierStart", java.lang.Character::isUnicodeIdentifierStart);
|
||||
defProp("javaUnicodeIdentifierPart", java.lang.Character::isUnicodeIdentifierPart);
|
||||
defProp("javaIdentifierIgnorable", java.lang.Character::isIdentifierIgnorable);
|
||||
defProp("javaSpaceChar", java.lang.Character::isSpaceChar);
|
||||
defProp("javaWhitespace", java.lang.Character::isWhitespace);
|
||||
defProp("javaISOControl", java.lang.Character::isISOControl);
|
||||
defProp("javaMirrored", java.lang.Character::isMirrored);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Posix ASCII variants, not in the lookup map
|
||||
*/
|
||||
static final BmpCharPredicate ASCII_DIGIT = ch -> ch < 128 && ASCII.isDigit(ch);
|
||||
static final BmpCharPredicate ASCII_WORD = ch -> ch < 128 && ASCII.isWord(ch);
|
||||
static final BmpCharPredicate ASCII_SPACE = ch -> ch < 128 && ASCII.isSpace(ch);
|
||||
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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 java.util.regex;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* A lightweight hashset implementation for positive 'int'. Not safe for
|
||||
* concurrent access.
|
||||
*/
|
||||
class IntHashSet {
|
||||
private int[] entries;
|
||||
private int[] hashes;
|
||||
private int pos = 0;
|
||||
|
||||
public IntHashSet() {
|
||||
this.entries = new int[16 << 1]; // initCapacity = 16;
|
||||
this.hashes = new int[(16 / 2) | 1]; // odd -> fewer collisions
|
||||
Arrays.fill(this.entries, -1);
|
||||
Arrays.fill(this.hashes, -1);
|
||||
}
|
||||
|
||||
public boolean contains(int i) {
|
||||
int h = hashes[i % hashes.length];
|
||||
while (h != -1) {
|
||||
if (entries[h] == i)
|
||||
return true;
|
||||
h = entries[h + 1];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void add(int i) {
|
||||
int h0 = i % hashes.length;
|
||||
int next = hashes[h0];
|
||||
// if invoker guarantees contains(i) checked before add(i)
|
||||
// the following check is not needed.
|
||||
int next0 = next;
|
||||
while (next0 != -1) {
|
||||
if (entries[next0 ] == i)
|
||||
return;
|
||||
next0 = entries[next0 + 1];
|
||||
}
|
||||
hashes[h0] = pos;
|
||||
entries[pos++] = i;
|
||||
entries[pos++] = next;
|
||||
if (pos == entries.length)
|
||||
expand();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
Arrays.fill(this.entries, -1);
|
||||
Arrays.fill(this.hashes, -1);
|
||||
pos = 0;
|
||||
}
|
||||
|
||||
private void expand() {
|
||||
int[] old = entries;
|
||||
int[] es = new int[old.length << 1];
|
||||
int hlen = (old.length / 2) | 1;
|
||||
int[] hs = new int[hlen];
|
||||
Arrays.fill(es, -1);
|
||||
Arrays.fill(hs, -1);
|
||||
for (int n = 0; n < pos;) { // re-hashing
|
||||
int i = old[n];
|
||||
int hsh = i % hlen;
|
||||
int next = hs[hsh];
|
||||
hs[hsh] = n;
|
||||
es[n++] = i;
|
||||
es[n++] = next;
|
||||
}
|
||||
this.entries = es;
|
||||
this.hashes = hs;
|
||||
}
|
||||
}
|
@ -177,6 +177,14 @@ public final class Matcher implements MatchResult {
|
||||
*/
|
||||
int[] locals;
|
||||
|
||||
/**
|
||||
* Storage used by top greedy Loop node to store a specific hash set to
|
||||
* keep the beginning index of the failed repetition match. The nodes
|
||||
* themselves are stateless, so they rely on this field to hold state
|
||||
* during a match.
|
||||
*/
|
||||
IntHashSet[] localsPos;
|
||||
|
||||
/**
|
||||
* Boolean indicating whether or not more input could change
|
||||
* the results of the last match.
|
||||
@ -239,6 +247,7 @@ public final class Matcher implements MatchResult {
|
||||
int parentGroupCount = Math.max(parent.capturingGroupCount, 10);
|
||||
groups = new int[parentGroupCount * 2];
|
||||
locals = new int[parent.localCount];
|
||||
localsPos = new IntHashSet[parent.localTCNCount];
|
||||
|
||||
// Put fields into initial states
|
||||
reset();
|
||||
@ -375,6 +384,7 @@ public final class Matcher implements MatchResult {
|
||||
groups[i] = -1;
|
||||
for (int i = 0; i < locals.length; i++)
|
||||
locals[i] = -1;
|
||||
localsPos = new IntHashSet[parentPattern.localTCNCount];
|
||||
modCount++;
|
||||
return this;
|
||||
}
|
||||
@ -397,6 +407,10 @@ public final class Matcher implements MatchResult {
|
||||
groups[i] = -1;
|
||||
for(int i=0; i<locals.length; i++)
|
||||
locals[i] = -1;
|
||||
for (int i = 0; i < localsPos.length; i++) {
|
||||
if (localsPos[i] != null)
|
||||
localsPos[i].clear();
|
||||
}
|
||||
lastAppendPosition = 0;
|
||||
from = 0;
|
||||
to = getTextLength();
|
||||
@ -1706,6 +1720,10 @@ public final class Matcher implements MatchResult {
|
||||
this.oldLast = oldLast < 0 ? from : oldLast;
|
||||
for (int i = 0; i < groups.length; i++)
|
||||
groups[i] = -1;
|
||||
for (int i = 0; i < localsPos.length; i++) {
|
||||
if (localsPos[i] != null)
|
||||
localsPos[i].clear();
|
||||
}
|
||||
acceptMode = NOANCHOR;
|
||||
boolean result = parentPattern.root.match(this, from, text);
|
||||
if (!result)
|
||||
@ -1729,6 +1747,10 @@ public final class Matcher implements MatchResult {
|
||||
this.oldLast = oldLast < 0 ? from : oldLast;
|
||||
for (int i = 0; i < groups.length; i++)
|
||||
groups[i] = -1;
|
||||
for (int i = 0; i < localsPos.length; i++) {
|
||||
if (localsPos[i] != null)
|
||||
localsPos[i].clear();
|
||||
}
|
||||
acceptMode = anchor;
|
||||
boolean result = parentPattern.matchRoot.match(this, from, text);
|
||||
if (!result)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,220 @@
|
||||
/*
|
||||
* 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 java.util.regex;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.regex.Pattern.CharPredicate;
|
||||
import java.util.regex.CharPredicates;
|
||||
import static java.util.regex.ASCII.*;
|
||||
|
||||
/**
|
||||
* A utility class to print out the pattern node tree.
|
||||
*/
|
||||
|
||||
class PrintPattern {
|
||||
|
||||
private static HashMap<Pattern.Node, Integer> ids = new HashMap<>();
|
||||
|
||||
private static void print(Pattern.Node node, String text, int depth) {
|
||||
if (!ids.containsKey(node))
|
||||
ids.put(node, ids.size());
|
||||
print("%6d:%" + (depth==0? "": depth<<1) + "s<%s>", ids.get(node), "", text);
|
||||
if (ids.containsKey(node.next))
|
||||
print(" (=>%d)", ids.get(node.next));
|
||||
print("%n");
|
||||
}
|
||||
|
||||
private static void print(String s, int depth) {
|
||||
print(" %" + (depth==0?"":depth<<1) + "s<%s>%n", "", s);
|
||||
}
|
||||
|
||||
private static void print(String fmt, Object ... args) {
|
||||
System.err.printf(fmt, args);
|
||||
}
|
||||
|
||||
private static String toStringCPS(int[] cps) {
|
||||
StringBuilder sb = new StringBuilder(cps.length);
|
||||
for (int cp : cps)
|
||||
sb.append(toStringCP(cp));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static String toStringCP(int cp) {
|
||||
return (isPrint(cp) ? "" + (char)cp
|
||||
: "\\u" + Integer.toString(cp, 16));
|
||||
}
|
||||
|
||||
private static String toStringRange(int min, int max) {
|
||||
if (max == Pattern.MAX_REPS) {
|
||||
if (min == 0)
|
||||
return " * ";
|
||||
else if (min == 1)
|
||||
return " + ";
|
||||
return "{" + min + ", max}";
|
||||
}
|
||||
return "{" + min + ", " + max + "}";
|
||||
}
|
||||
|
||||
private static String toStringCtype(int type) {
|
||||
switch(type) {
|
||||
case UPPER: return "ASCII.UPPER";
|
||||
case LOWER: return "ASCII.LOWER";
|
||||
case DIGIT: return "ASCII.DIGIT";
|
||||
case SPACE: return "ASCII.SPACE";
|
||||
case PUNCT: return "ASCII.PUNCT";
|
||||
case CNTRL: return "ASCII.CNTRL";
|
||||
case BLANK: return "ASCII.BLANK";
|
||||
case UNDER: return "ASCII.UNDER";
|
||||
case ASCII: return "ASCII.ASCII";
|
||||
case ALPHA: return "ASCII.ALPHA";
|
||||
case ALNUM: return "ASCII.ALNUM";
|
||||
case GRAPH: return "ASCII.GRAPH";
|
||||
case WORD: return "ASCII.WORD";
|
||||
case XDIGIT: return "ASCII.XDIGIT";
|
||||
default: return "ASCII ?";
|
||||
}
|
||||
}
|
||||
|
||||
private static String toString(Pattern.Node node) {
|
||||
String name = node.getClass().getName();
|
||||
return name.substring(name.lastIndexOf('$') + 1);
|
||||
}
|
||||
|
||||
static HashMap<CharPredicate, String> pmap;
|
||||
static {
|
||||
pmap = new HashMap<>();
|
||||
pmap.put(Pattern.ALL, "All");
|
||||
pmap.put(Pattern.DOT, "Dot");
|
||||
pmap.put(Pattern.UNIXDOT, "UnixDot");
|
||||
pmap.put(Pattern.VertWS, "VertWS");
|
||||
pmap.put(Pattern.HorizWS, "HorizWS");
|
||||
|
||||
pmap.put(CharPredicates.ASCII_DIGIT, "ASCII.DIGIT");
|
||||
pmap.put(CharPredicates.ASCII_WORD, "ASCII.WORD");
|
||||
pmap.put(CharPredicates.ASCII_SPACE, "ASCII.SPACE");
|
||||
}
|
||||
|
||||
static void walk(Pattern.Node node, int depth) {
|
||||
depth++;
|
||||
while(node != null) {
|
||||
String name = toString(node);
|
||||
String str;
|
||||
if (node instanceof Pattern.Prolog) {
|
||||
print(node, name, depth);
|
||||
// print the loop here
|
||||
Pattern.Loop loop = ((Pattern.Prolog)node).loop;
|
||||
name = toString(loop);
|
||||
str = name + " " + toStringRange(loop.cmin, loop.cmax);
|
||||
print(loop, str, depth);
|
||||
walk(loop.body, depth);
|
||||
print("/" + name, depth);
|
||||
node = loop;
|
||||
} else if (node instanceof Pattern.Loop) {
|
||||
return; // stop here, body.next -> loop
|
||||
} else if (node instanceof Pattern.Curly) {
|
||||
Pattern.Curly c = (Pattern.Curly)node;
|
||||
str = "Curly " + c.type + " " + toStringRange(c.cmin, c.cmax);
|
||||
print(node, str, depth);
|
||||
walk(c.atom, depth);
|
||||
print("/Curly", depth);
|
||||
} else if (node instanceof Pattern.GroupCurly) {
|
||||
Pattern.GroupCurly gc = (Pattern.GroupCurly)node;
|
||||
str = "GroupCurly " + gc.groupIndex / 2 +
|
||||
", " + gc.type + " " + toStringRange(gc.cmin, gc.cmax);
|
||||
print(node, str, depth);
|
||||
walk(gc.atom, depth);
|
||||
print("/GroupCurly", depth);
|
||||
} else if (node instanceof Pattern.GroupHead) {
|
||||
Pattern.GroupHead head = (Pattern.GroupHead)node;
|
||||
Pattern.GroupTail tail = head.tail;
|
||||
print(head, "Group.head " + (tail.groupIndex / 2), depth);
|
||||
walk(head.next, depth);
|
||||
print(tail, "/Group.tail " + (tail.groupIndex / 2), depth);
|
||||
node = tail;
|
||||
} else if (node instanceof Pattern.GroupTail) {
|
||||
return; // stopper
|
||||
} else if (node instanceof Pattern.Ques) {
|
||||
print(node, "Ques " + ((Pattern.Ques)node).type, depth);
|
||||
walk(((Pattern.Ques)node).atom, depth);
|
||||
print("/Ques", depth);
|
||||
} else if (node instanceof Pattern.Branch) {
|
||||
Pattern.Branch b = (Pattern.Branch)node;
|
||||
print(b, name, depth);
|
||||
int i = 0;
|
||||
while (true) {
|
||||
if (b.atoms[i] != null) {
|
||||
walk(b.atoms[i], depth);
|
||||
} else {
|
||||
print(" (accepted)", depth);
|
||||
}
|
||||
if (++i == b.size)
|
||||
break;
|
||||
print("-branch.separator-", depth);
|
||||
}
|
||||
node = b.conn;
|
||||
print(node, "/Branch", depth);
|
||||
} else if (node instanceof Pattern.BranchConn) {
|
||||
return;
|
||||
} else if (node instanceof Pattern.CharProperty) {
|
||||
str = pmap.get(((Pattern.CharProperty)node).predicate);
|
||||
if (str == null)
|
||||
str = toString(node);
|
||||
else
|
||||
str = "Single \"" + str + "\"";
|
||||
print(node, str, depth);
|
||||
} else if (node instanceof Pattern.SliceNode) {
|
||||
str = name + " \"" +
|
||||
toStringCPS(((Pattern.SliceNode)node).buffer) + "\"";
|
||||
print(node, str, depth);
|
||||
} else if (node instanceof Pattern.CharPropertyGreedy) {
|
||||
Pattern.CharPropertyGreedy gcp = (Pattern.CharPropertyGreedy)node;
|
||||
String pstr = pmap.get(gcp.predicate);
|
||||
if (pstr == null)
|
||||
pstr = gcp.predicate.toString();
|
||||
else
|
||||
pstr = "Single \"" + pstr + "\"";
|
||||
str = name + " " + pstr + ((gcp.cmin == 0) ? "*" : "+");
|
||||
print(node, str, depth);
|
||||
} else if (node instanceof Pattern.BackRef) {
|
||||
str = "GroupBackRef " + ((Pattern.BackRef)node).groupIndex / 2;
|
||||
print(node, str, depth);
|
||||
} else if (node instanceof Pattern.LastNode) {
|
||||
print(node, "END", depth);
|
||||
} else if (node == Pattern.accept) {
|
||||
return;
|
||||
} else {
|
||||
print(node, name, depth);
|
||||
}
|
||||
node = node.next;
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
Pattern p = Pattern.compile(args[0]);
|
||||
System.out.println(" Pattern: " + p);
|
||||
walk(p.root, 0);
|
||||
}
|
||||
}
|
@ -1,246 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2013, 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 java.util.regex;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
|
||||
enum UnicodeProp {
|
||||
|
||||
ALPHABETIC {
|
||||
public boolean is(int ch) {
|
||||
return Character.isAlphabetic(ch);
|
||||
}
|
||||
},
|
||||
|
||||
LETTER {
|
||||
public boolean is(int ch) {
|
||||
return Character.isLetter(ch);
|
||||
}
|
||||
},
|
||||
|
||||
IDEOGRAPHIC {
|
||||
public boolean is(int ch) {
|
||||
return Character.isIdeographic(ch);
|
||||
}
|
||||
},
|
||||
|
||||
LOWERCASE {
|
||||
public boolean is(int ch) {
|
||||
return Character.isLowerCase(ch);
|
||||
}
|
||||
},
|
||||
|
||||
UPPERCASE {
|
||||
public boolean is(int ch) {
|
||||
return Character.isUpperCase(ch);
|
||||
}
|
||||
},
|
||||
|
||||
TITLECASE {
|
||||
public boolean is(int ch) {
|
||||
return Character.isTitleCase(ch);
|
||||
}
|
||||
},
|
||||
|
||||
WHITE_SPACE {
|
||||
// \p{Whitespace}
|
||||
public boolean is(int ch) {
|
||||
return ((((1 << Character.SPACE_SEPARATOR) |
|
||||
(1 << Character.LINE_SEPARATOR) |
|
||||
(1 << Character.PARAGRAPH_SEPARATOR)) >> Character.getType(ch)) & 1)
|
||||
!= 0 || (ch >= 0x9 && ch <= 0xd) || (ch == 0x85);
|
||||
}
|
||||
},
|
||||
|
||||
CONTROL {
|
||||
// \p{gc=Control}
|
||||
public boolean is(int ch) {
|
||||
return Character.getType(ch) == Character.CONTROL;
|
||||
}
|
||||
},
|
||||
|
||||
PUNCTUATION {
|
||||
// \p{gc=Punctuation}
|
||||
public boolean is(int ch) {
|
||||
return ((((1 << Character.CONNECTOR_PUNCTUATION) |
|
||||
(1 << Character.DASH_PUNCTUATION) |
|
||||
(1 << Character.START_PUNCTUATION) |
|
||||
(1 << Character.END_PUNCTUATION) |
|
||||
(1 << Character.OTHER_PUNCTUATION) |
|
||||
(1 << Character.INITIAL_QUOTE_PUNCTUATION) |
|
||||
(1 << Character.FINAL_QUOTE_PUNCTUATION)) >> Character.getType(ch)) & 1)
|
||||
!= 0;
|
||||
}
|
||||
},
|
||||
|
||||
HEX_DIGIT {
|
||||
// \p{gc=Decimal_Number}
|
||||
// \p{Hex_Digit} -> PropList.txt: Hex_Digit
|
||||
public boolean is(int ch) {
|
||||
return DIGIT.is(ch) ||
|
||||
(ch >= 0x0030 && ch <= 0x0039) ||
|
||||
(ch >= 0x0041 && ch <= 0x0046) ||
|
||||
(ch >= 0x0061 && ch <= 0x0066) ||
|
||||
(ch >= 0xFF10 && ch <= 0xFF19) ||
|
||||
(ch >= 0xFF21 && ch <= 0xFF26) ||
|
||||
(ch >= 0xFF41 && ch <= 0xFF46);
|
||||
}
|
||||
},
|
||||
|
||||
ASSIGNED {
|
||||
public boolean is(int ch) {
|
||||
return Character.getType(ch) != Character.UNASSIGNED;
|
||||
}
|
||||
},
|
||||
|
||||
NONCHARACTER_CODE_POINT {
|
||||
// PropList.txt:Noncharacter_Code_Point
|
||||
public boolean is(int ch) {
|
||||
return (ch & 0xfffe) == 0xfffe || (ch >= 0xfdd0 && ch <= 0xfdef);
|
||||
}
|
||||
},
|
||||
|
||||
DIGIT {
|
||||
// \p{gc=Decimal_Number}
|
||||
public boolean is(int ch) {
|
||||
return Character.isDigit(ch);
|
||||
}
|
||||
},
|
||||
|
||||
ALNUM {
|
||||
// \p{alpha}
|
||||
// \p{digit}
|
||||
public boolean is(int ch) {
|
||||
return ALPHABETIC.is(ch) || DIGIT.is(ch);
|
||||
}
|
||||
},
|
||||
|
||||
BLANK {
|
||||
// \p{Whitespace} --
|
||||
// [\N{LF} \N{VT} \N{FF} \N{CR} \N{NEL} -> 0xa, 0xb, 0xc, 0xd, 0x85
|
||||
// \p{gc=Line_Separator}
|
||||
// \p{gc=Paragraph_Separator}]
|
||||
public boolean is(int ch) {
|
||||
return Character.getType(ch) == Character.SPACE_SEPARATOR ||
|
||||
ch == 0x9; // \N{HT}
|
||||
}
|
||||
},
|
||||
|
||||
GRAPH {
|
||||
// [^
|
||||
// \p{space}
|
||||
// \p{gc=Control}
|
||||
// \p{gc=Surrogate}
|
||||
// \p{gc=Unassigned}]
|
||||
public boolean is(int ch) {
|
||||
return ((((1 << Character.SPACE_SEPARATOR) |
|
||||
(1 << Character.LINE_SEPARATOR) |
|
||||
(1 << Character.PARAGRAPH_SEPARATOR) |
|
||||
(1 << Character.CONTROL) |
|
||||
(1 << Character.SURROGATE) |
|
||||
(1 << Character.UNASSIGNED)) >> Character.getType(ch)) & 1)
|
||||
== 0;
|
||||
}
|
||||
},
|
||||
|
||||
PRINT {
|
||||
// \p{graph}
|
||||
// \p{blank}
|
||||
// -- \p{cntrl}
|
||||
public boolean is(int ch) {
|
||||
return (GRAPH.is(ch) || BLANK.is(ch)) && !CONTROL.is(ch);
|
||||
}
|
||||
},
|
||||
|
||||
WORD {
|
||||
// \p{alpha}
|
||||
// \p{gc=Mark}
|
||||
// \p{digit}
|
||||
// \p{gc=Connector_Punctuation}
|
||||
// \p{Join_Control} 200C..200D
|
||||
|
||||
public boolean is(int ch) {
|
||||
return ALPHABETIC.is(ch) ||
|
||||
((((1 << Character.NON_SPACING_MARK) |
|
||||
(1 << Character.ENCLOSING_MARK) |
|
||||
(1 << Character.COMBINING_SPACING_MARK) |
|
||||
(1 << Character.DECIMAL_DIGIT_NUMBER) |
|
||||
(1 << Character.CONNECTOR_PUNCTUATION)) >> Character.getType(ch)) & 1)
|
||||
!= 0 ||
|
||||
JOIN_CONTROL.is(ch);
|
||||
}
|
||||
},
|
||||
|
||||
JOIN_CONTROL {
|
||||
// 200C..200D PropList.txt:Join_Control
|
||||
public boolean is(int ch) {
|
||||
return (ch == 0x200C || ch == 0x200D);
|
||||
}
|
||||
};
|
||||
|
||||
private static final HashMap<String, String> posix = new HashMap<>();
|
||||
private static final HashMap<String, String> aliases = new HashMap<>();
|
||||
static {
|
||||
posix.put("ALPHA", "ALPHABETIC");
|
||||
posix.put("LOWER", "LOWERCASE");
|
||||
posix.put("UPPER", "UPPERCASE");
|
||||
posix.put("SPACE", "WHITE_SPACE");
|
||||
posix.put("PUNCT", "PUNCTUATION");
|
||||
posix.put("XDIGIT","HEX_DIGIT");
|
||||
posix.put("ALNUM", "ALNUM");
|
||||
posix.put("CNTRL", "CONTROL");
|
||||
posix.put("DIGIT", "DIGIT");
|
||||
posix.put("BLANK", "BLANK");
|
||||
posix.put("GRAPH", "GRAPH");
|
||||
posix.put("PRINT", "PRINT");
|
||||
|
||||
aliases.put("WHITESPACE", "WHITE_SPACE");
|
||||
aliases.put("HEXDIGIT","HEX_DIGIT");
|
||||
aliases.put("NONCHARACTERCODEPOINT", "NONCHARACTER_CODE_POINT");
|
||||
aliases.put("JOINCONTROL", "JOIN_CONTROL");
|
||||
}
|
||||
|
||||
public static UnicodeProp forName(String propName) {
|
||||
propName = propName.toUpperCase(Locale.ENGLISH);
|
||||
String alias = aliases.get(propName);
|
||||
if (alias != null)
|
||||
propName = alias;
|
||||
try {
|
||||
return valueOf (propName);
|
||||
} catch (IllegalArgumentException x) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static UnicodeProp forPOSIXName(String propName) {
|
||||
propName = posix.get(propName.toUpperCase(Locale.ENGLISH));
|
||||
if (propName == null)
|
||||
return null;
|
||||
return valueOf (propName);
|
||||
}
|
||||
|
||||
public abstract boolean is(int ch);
|
||||
}
|
@ -40,7 +40,7 @@ public class UnsupportedCallbackException extends Exception {
|
||||
private Callback callback;
|
||||
|
||||
/**
|
||||
* Constructs a {@code UnsupportedCallbackException}
|
||||
* Constructs an {@code UnsupportedCallbackException}
|
||||
* with no detail message.
|
||||
*
|
||||
* @param callback the unrecognized {@code Callback}.
|
||||
|
@ -390,10 +390,25 @@ public class VM {
|
||||
private static final int JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT = 0x0020;
|
||||
|
||||
/*
|
||||
* Returns the first non-null class loader up the execution stack,
|
||||
* or null if only code from the null class loader is on the stack.
|
||||
* Returns the first user-defined class loader up the execution stack,
|
||||
* or the platform class loader if only code from the platform or
|
||||
* bootstrap class loader is on the stack.
|
||||
*/
|
||||
public static native ClassLoader latestUserDefinedLoader();
|
||||
public static ClassLoader latestUserDefinedLoader() {
|
||||
ClassLoader loader = latestUserDefinedLoader0();
|
||||
return loader != null ? loader : ClassLoader.getPlatformClassLoader();
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the first user-defined class loader up the execution stack,
|
||||
* or null if only code from the platform or bootstrap class loader is
|
||||
* on the stack. VM does not keep a reference of platform loader and so
|
||||
* it returns null.
|
||||
*
|
||||
* This method should be replaced with StackWalker::walk and then we can
|
||||
* remove the logic in the VM.
|
||||
*/
|
||||
private static native ClassLoader latestUserDefinedLoader0();
|
||||
|
||||
/**
|
||||
* Returns {@code true} if we are in a set UID program.
|
||||
|
@ -784,7 +784,7 @@ public abstract class FtpClient implements java.io.Closeable {
|
||||
*
|
||||
* @param path the pathname of the directory to list or {@code null}
|
||||
* for the current working directoty.
|
||||
* @return a {@code Iterator} of files or {@code null} if the
|
||||
* @return an {@code Iterator} of files or {@code null} if the
|
||||
* command failed.
|
||||
* @throws IOException if an error occurred during the transmission
|
||||
* @see #setDirParser(FtpDirParser)
|
||||
|
@ -51,12 +51,9 @@ import static java.security.DrbgParameters.Capability.*;
|
||||
* configuration is eagerly called to set up parameters, and instantiation
|
||||
* is lazily called only when nextBytes or reseed is called.
|
||||
* <p>
|
||||
* Synchronized keyword should be added to all externally callable engine
|
||||
* methods including {@link #engineReseed}, {@link #engineSetSeed}, and
|
||||
* {@link #engineNextBytes} (but not {@link #engineGenerateSeed}).
|
||||
* Internal methods like {@link #configure} and {@link #instantiateAlgorithm}
|
||||
* are not synchronized. They will either be called in a constructor or
|
||||
* in another synchronized method.
|
||||
* SecureRandom methods like reseed and nextBytes are not thread-safe.
|
||||
* An implementation is required to protect shared access to instantiate states
|
||||
* (instantiated, nonce) and DRBG states (v, c, key, reseedCounter).
|
||||
*/
|
||||
public abstract class AbstractDrbg extends SecureRandomSpi {
|
||||
|
||||
@ -78,8 +75,10 @@ public abstract class AbstractDrbg extends SecureRandomSpi {
|
||||
* Reseed counter of a DRBG instance. A mechanism should increment it
|
||||
* after each random bits generation and reset it in reseed. A mechanism
|
||||
* does <em>not</em> need to compare it to {@link #reseedInterval}.
|
||||
*
|
||||
* Volatile, will be used in a double checked locking.
|
||||
*/
|
||||
protected transient int reseedCounter = 0;
|
||||
protected transient volatile int reseedCounter = 0;
|
||||
|
||||
// Mech features. If not same as below, must be redefined in constructor.
|
||||
|
||||
@ -268,10 +267,9 @@ public abstract class AbstractDrbg extends SecureRandomSpi {
|
||||
* {@code DEFAULT_STRENGTH} is 128) for HashDRBG:
|
||||
* <pre>
|
||||
* requested effective
|
||||
* (SHA-1, -1) (SHA-1,128)
|
||||
* (SHA-1, 112) (SHA-1,112)
|
||||
* (SHA-1, 192) IAE
|
||||
* (SHA-224, 256) IAE
|
||||
* (SHA-256, -1) (SHA-256,128)
|
||||
* (SHA-256, 112) (SHA-256,112)
|
||||
* (SHA-256, 128) (SHA-256,128)
|
||||
* (SHA-3, -1) IAE
|
||||
* (null, -1) (SHA-256,128)
|
||||
@ -383,9 +381,14 @@ public abstract class AbstractDrbg extends SecureRandomSpi {
|
||||
instantiateIfNecessary(null);
|
||||
|
||||
// Step 7: Auto reseed
|
||||
// Double checked locking, safe because reseedCounter is volatile
|
||||
if (reseedCounter > reseedInterval || pr) {
|
||||
reseedAlgorithm(getEntropyInput(pr), ai);
|
||||
ai = null;
|
||||
synchronized (this) {
|
||||
if (reseedCounter > reseedInterval || pr) {
|
||||
reseedAlgorithm(getEntropyInput(pr), ai);
|
||||
ai = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 8, 10: Generate_algorithm
|
||||
@ -615,8 +618,7 @@ public abstract class AbstractDrbg extends SecureRandomSpi {
|
||||
* @throws IllegalArgumentException if {@code params} is
|
||||
* inappropriate for this SecureRandom.
|
||||
*/
|
||||
protected final synchronized void configure(
|
||||
SecureRandomParameters params) {
|
||||
protected final void configure(SecureRandomParameters params) {
|
||||
if (debug != null) {
|
||||
debug.println(this, "configure " + this + " with " + params);
|
||||
}
|
||||
|
@ -39,8 +39,6 @@ public abstract class AbstractHashDrbg extends AbstractDrbg {
|
||||
|
||||
private static int alg2strength(String algorithm) {
|
||||
switch (algorithm.toUpperCase(Locale.ROOT)) {
|
||||
case "SHA-1":
|
||||
return 128;
|
||||
case "SHA-224":
|
||||
case "SHA-512/224":
|
||||
return 192;
|
||||
@ -82,10 +80,6 @@ public abstract class AbstractHashDrbg extends AbstractDrbg {
|
||||
this.securityStrength = tryStrength;
|
||||
}
|
||||
switch (algorithm.toUpperCase(Locale.ROOT)) {
|
||||
case "SHA-1":
|
||||
this.seedLen = 440 / 8;
|
||||
this.outLen = 160 / 8;
|
||||
break;
|
||||
case "SHA-224":
|
||||
case "SHA-512/224":
|
||||
this.seedLen = 440 / 8;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2006, 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
|
||||
@ -441,17 +441,89 @@ final class ByteArrayAccess {
|
||||
(outOfs < 0) || ((out.length - outOfs) < len)) {
|
||||
throw new ArrayIndexOutOfBoundsException();
|
||||
}
|
||||
len += outOfs;
|
||||
while (outOfs < len) {
|
||||
long i = in[inOfs++];
|
||||
out[outOfs++] = (byte)(i >> 56);
|
||||
out[outOfs++] = (byte)(i >> 48);
|
||||
out[outOfs++] = (byte)(i >> 40);
|
||||
out[outOfs++] = (byte)(i >> 32);
|
||||
out[outOfs++] = (byte)(i >> 24);
|
||||
out[outOfs++] = (byte)(i >> 16);
|
||||
out[outOfs++] = (byte)(i >> 8);
|
||||
out[outOfs++] = (byte)(i );
|
||||
if (littleEndianUnaligned) {
|
||||
outOfs += byteArrayOfs;
|
||||
len += outOfs;
|
||||
while (outOfs < len) {
|
||||
unsafe.putLong(out, (long)outOfs, reverseBytes(in[inOfs++]));
|
||||
outOfs += 8;
|
||||
}
|
||||
} else {
|
||||
len += outOfs;
|
||||
while (outOfs < len) {
|
||||
long i = in[inOfs++];
|
||||
out[outOfs++] = (byte)(i >> 56);
|
||||
out[outOfs++] = (byte)(i >> 48);
|
||||
out[outOfs++] = (byte)(i >> 40);
|
||||
out[outOfs++] = (byte)(i >> 32);
|
||||
out[outOfs++] = (byte)(i >> 24);
|
||||
out[outOfs++] = (byte)(i >> 16);
|
||||
out[outOfs++] = (byte)(i >> 8);
|
||||
out[outOfs++] = (byte)(i );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* byte[] to long[] conversion, little endian byte order
|
||||
*/
|
||||
static void b2lLittle(byte[] in, int inOfs, long[] out, int outOfs, int len) {
|
||||
if ((inOfs < 0) || ((in.length - inOfs) < len) ||
|
||||
((outOfs < 0) || (out.length - outOfs) < len/8)) {
|
||||
throw new ArrayIndexOutOfBoundsException();
|
||||
}
|
||||
if (littleEndianUnaligned) {
|
||||
inOfs += byteArrayOfs;
|
||||
len += inOfs;
|
||||
while (inOfs < len) {
|
||||
out[outOfs++] = unsafe.getLong(in, (long)inOfs);
|
||||
inOfs += 8;
|
||||
}
|
||||
} else {
|
||||
len += inOfs;
|
||||
while (inOfs < len) {
|
||||
out[outOfs++] = ((in[inOfs ] & 0xffL)
|
||||
| ((in[inOfs + 1] & 0xffL) << 8)
|
||||
| ((in[inOfs + 2] & 0xffL) << 16)
|
||||
| ((in[inOfs + 3] & 0xffL) << 24)
|
||||
| ((in[inOfs + 4] & 0xffL) << 32)
|
||||
| ((in[inOfs + 5] & 0xffL) << 40)
|
||||
| ((in[inOfs + 6] & 0xffL) << 48)
|
||||
| ((in[inOfs + 7] & 0xffL) << 56));
|
||||
inOfs += 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* long[] to byte[] conversion, little endian byte order
|
||||
*/
|
||||
static void l2bLittle(long[] in, int inOfs, byte[] out, int outOfs, int len) {
|
||||
if ((inOfs < 0) || ((in.length - inOfs) < len/8) ||
|
||||
(outOfs < 0) || ((out.length - outOfs) < len)) {
|
||||
throw new ArrayIndexOutOfBoundsException();
|
||||
}
|
||||
if (littleEndianUnaligned) {
|
||||
outOfs += byteArrayOfs;
|
||||
len += outOfs;
|
||||
while (outOfs < len) {
|
||||
unsafe.putLong(out, (long)outOfs, in[inOfs++]);
|
||||
outOfs += 8;
|
||||
}
|
||||
} else {
|
||||
len += outOfs;
|
||||
while (outOfs < len) {
|
||||
long i = in[inOfs++];
|
||||
out[outOfs++] = (byte)(i );
|
||||
out[outOfs++] = (byte)(i >> 8);
|
||||
out[outOfs++] = (byte)(i >> 16);
|
||||
out[outOfs++] = (byte)(i >> 24);
|
||||
out[outOfs++] = (byte)(i >> 32);
|
||||
out[outOfs++] = (byte)(i >> 40);
|
||||
out[outOfs++] = (byte)(i >> 48);
|
||||
out[outOfs++] = (byte)(i >> 56);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,6 @@ package sun.security.provider;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.io.IOException;
|
||||
import java.security.*;
|
||||
@ -68,11 +67,6 @@ public class CtrDrbg extends AbstractDrbg {
|
||||
|
||||
private static int alg2strength(String algorithm) {
|
||||
switch (algorithm.toUpperCase(Locale.ROOT)) {
|
||||
case "TDEA":
|
||||
case "3KEYTDEA":
|
||||
case "3 KEY TDEA":
|
||||
case "DESEDE":
|
||||
return 112;
|
||||
case "AES-128":
|
||||
return 128;
|
||||
case "AES-192":
|
||||
@ -120,16 +114,6 @@ public class CtrDrbg extends AbstractDrbg {
|
||||
this.securityStrength = tryStrength;
|
||||
}
|
||||
switch (algorithm.toUpperCase(Locale.ROOT)) {
|
||||
case "TDEA":
|
||||
case "3KEYTDEA":
|
||||
case "3 KEY TDEA":
|
||||
case "DESEDE":
|
||||
algorithm = "DESede";
|
||||
this.keyAlg = "DESede";
|
||||
this.cipherAlg = "DESede/ECB/NoPadding";
|
||||
this.blockLen = 64 / 8;
|
||||
this.keyLen = 168 / 8;
|
||||
break;
|
||||
case "AES-128":
|
||||
case "AES-192":
|
||||
case "AES-256":
|
||||
@ -224,7 +208,7 @@ public class CtrDrbg extends AbstractDrbg {
|
||||
// Step 2.1. Increment
|
||||
addOne(v, ctrLen);
|
||||
// Step 2.2. Block_Encrypt
|
||||
cipher.init(Cipher.ENCRYPT_MODE, getKey(keyAlg, k));
|
||||
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(k, keyAlg));
|
||||
// Step 2.3. Encrypt into right position, no need to cat
|
||||
cipher.doFinal(v, 0, blockLen, temp, i * blockLen);
|
||||
}
|
||||
@ -316,7 +300,7 @@ public class CtrDrbg extends AbstractDrbg {
|
||||
|
||||
for (int i = 0; i * blockLen < seedLen; i++) {
|
||||
try {
|
||||
cipher.init(Cipher.ENCRYPT_MODE, getKey(keyAlg, k));
|
||||
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(k, keyAlg));
|
||||
int tailLen = temp.length - blockLen*i;
|
||||
if (tailLen > blockLen) {
|
||||
tailLen = blockLen;
|
||||
@ -340,7 +324,7 @@ public class CtrDrbg extends AbstractDrbg {
|
||||
inputBlock[j] ^= chain[j];
|
||||
}
|
||||
try {
|
||||
cipher.init(Cipher.ENCRYPT_MODE, getKey(keyAlg, k));
|
||||
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(k, keyAlg));
|
||||
chain = cipher.doFinal(inputBlock);
|
||||
} catch (GeneralSecurityException e) {
|
||||
throw new InternalError(e);
|
||||
@ -350,7 +334,7 @@ public class CtrDrbg extends AbstractDrbg {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void reseedAlgorithm(
|
||||
protected synchronized void reseedAlgorithm(
|
||||
byte[] ei,
|
||||
byte[] additionalInput) {
|
||||
if (usedf) {
|
||||
@ -456,7 +440,7 @@ public class CtrDrbg extends AbstractDrbg {
|
||||
addOne(v, ctrLen);
|
||||
try {
|
||||
// Step 4.2. Encrypt
|
||||
cipher.init(Cipher.ENCRYPT_MODE, getKey(keyAlg, k));
|
||||
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(k, keyAlg));
|
||||
byte[] out = cipher.doFinal(v);
|
||||
|
||||
// Step 4.3 and 5. Cat bytes and leftmost
|
||||
@ -479,43 +463,6 @@ public class CtrDrbg extends AbstractDrbg {
|
||||
// Step 8. Return
|
||||
}
|
||||
|
||||
private static void des7to8(
|
||||
byte[] key56, int off56, byte[] key64, int off64) {
|
||||
key64[off64 + 0] = (byte)
|
||||
(key56[off56 + 0] & 0xFE); // << 0
|
||||
key64[off64 + 1] = (byte)
|
||||
((key56[off56 + 0] << 7) | ((key56[off56 + 1] & 0xFF) >>> 1));
|
||||
key64[off64 + 2] = (byte)
|
||||
((key56[off56 + 1] << 6) | ((key56[off56 + 2] & 0xFF) >>> 2));
|
||||
key64[off64 + 3] = (byte)
|
||||
((key56[off56 + 2] << 5) | ((key56[off56 + 3] & 0xFF) >>> 3));
|
||||
key64[off64 + 4] = (byte)
|
||||
((key56[off56 + 3] << 4) | ((key56[off56 + 4] & 0xFF) >>> 4));
|
||||
key64[off64 + 5] = (byte)
|
||||
((key56[off56 + 4] << 3) | ((key56[off56 + 5] & 0xFF) >>> 5));
|
||||
key64[off64 + 6] = (byte)
|
||||
((key56[off56 + 5] << 2) | ((key56[off56 + 6] & 0xFF) >>> 6));
|
||||
key64[off64 + 7] = (byte)
|
||||
(key56[off56 + 6] << 1);
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
// if even # bits, make uneven, XOR with 1 (uneven & 1)
|
||||
// for uneven # bits, make even, XOR with 0 (even & 1)
|
||||
key64[off64 + i] ^= Integer.bitCount(key64[off64 + i] ^ 1) & 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static SecretKey getKey(String keyAlg, byte[] k) {
|
||||
if (keyAlg.equals("DESede")) {
|
||||
byte[] k2 = new byte[24];
|
||||
des7to8(k, 0, k2, 0);
|
||||
des7to8(k, 7, k2, 8);
|
||||
des7to8(k, 14, k2, 16);
|
||||
k = k2;
|
||||
}
|
||||
return new SecretKeySpec(k, keyAlg);
|
||||
}
|
||||
|
||||
private void readObject(java.io.ObjectInputStream s)
|
||||
throws IOException, ClassNotFoundException {
|
||||
s.defaultReadObject ();
|
||||
|
@ -115,7 +115,7 @@ public class HashDrbg extends AbstractHashDrbg {
|
||||
|
||||
// This method is used by both instantiation and reseeding.
|
||||
@Override
|
||||
protected final void hashReseedInternal(byte[] input) {
|
||||
protected final synchronized void hashReseedInternal(byte[] input) {
|
||||
|
||||
// 800-90Ar1 10.1.1.2: Instantiate Process.
|
||||
// 800-90Ar1 10.1.1.3: Reseed Process.
|
||||
|
@ -115,7 +115,7 @@ public class HmacDrbg extends AbstractHashDrbg {
|
||||
|
||||
// This method is used by both instantiation and reseeding.
|
||||
@Override
|
||||
protected final void hashReseedInternal(byte[] input) {
|
||||
protected final synchronized void hashReseedInternal(byte[] input) {
|
||||
|
||||
// 800-90Ar1 10.1.2.3: Instantiate Process.
|
||||
// 800-90Ar1 10.1.2.4: Reseed Process.
|
||||
|
300
jdk/src/java.base/share/classes/sun/security/provider/SHA3.java
Normal file
300
jdk/src/java.base/share/classes/sun/security/provider/SHA3.java
Normal file
@ -0,0 +1,300 @@
|
||||
/*
|
||||
* 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 sun.security.provider;
|
||||
|
||||
import static sun.security.provider.ByteArrayAccess.*;
|
||||
import java.nio.*;
|
||||
import java.util.*;
|
||||
import java.security.*;
|
||||
|
||||
/**
|
||||
* This class implements the Secure Hash Algorithm SHA-3 developed by
|
||||
* the National Institute of Standards and Technology along with the
|
||||
* National Security Agency as defined in FIPS PUB 202.
|
||||
*
|
||||
* <p>It implements java.security.MessageDigestSpi, and can be used
|
||||
* through Java Cryptography Architecture (JCA), as a pluggable
|
||||
* MessageDigest implementation.
|
||||
*
|
||||
* @since 9
|
||||
* @author Valerie Peng
|
||||
*/
|
||||
abstract class SHA3 extends DigestBase {
|
||||
|
||||
private static final int WIDTH = 200; // in bytes, e.g. 1600 bits
|
||||
private static final int DM = 5; // dimension of lanes
|
||||
|
||||
private static final int NR = 24; // number of rounds
|
||||
|
||||
// precomputed round constants needed by the step mapping Iota
|
||||
private static final long[] RC_CONSTANTS = {
|
||||
0x01L, 0x8082L, 0x800000000000808aL,
|
||||
0x8000000080008000L, 0x808bL, 0x80000001L,
|
||||
0x8000000080008081L, 0x8000000000008009L, 0x8aL,
|
||||
0x88L, 0x80008009L, 0x8000000aL,
|
||||
0x8000808bL, 0x800000000000008bL, 0x8000000000008089L,
|
||||
0x8000000000008003L, 0x8000000000008002L, 0x8000000000000080L,
|
||||
0x800aL, 0x800000008000000aL, 0x8000000080008081L,
|
||||
0x8000000000008080L, 0x80000001L, 0x8000000080008008L,
|
||||
};
|
||||
|
||||
private byte[] state;
|
||||
|
||||
/**
|
||||
* Creates a new SHA-3 object.
|
||||
*/
|
||||
SHA3(String name, int digestLength) {
|
||||
super(name, digestLength, (WIDTH - (2 * digestLength)));
|
||||
implReset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Core compression function. Processes blockSize bytes at a time
|
||||
* and updates the state of this object.
|
||||
*/
|
||||
void implCompress(byte[] b, int ofs) {
|
||||
for (int i = 0; i < buffer.length; i++) {
|
||||
state[i] ^= b[ofs++];
|
||||
}
|
||||
state = keccak(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the digest. Subclasses do not need to reset() themselves,
|
||||
* DigestBase calls implReset() when necessary.
|
||||
*/
|
||||
void implDigest(byte[] out, int ofs) {
|
||||
int numOfPadding =
|
||||
setPaddingBytes(buffer, (int)(bytesProcessed % buffer.length));
|
||||
if (numOfPadding < 1) {
|
||||
throw new ProviderException("Incorrect pad size: " + numOfPadding);
|
||||
}
|
||||
for (int i = 0; i < buffer.length; i++) {
|
||||
state[i] ^= buffer[i];
|
||||
}
|
||||
state = keccak(state);
|
||||
System.arraycopy(state, 0, out, ofs, engineGetDigestLength());
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the internal state to start a new hash.
|
||||
*/
|
||||
void implReset() {
|
||||
state = new byte[WIDTH];
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function for circular shift the specified long
|
||||
* value to the left for n bits.
|
||||
*/
|
||||
private static long circularShiftLeft(long lane, int n) {
|
||||
return ((lane << n) | (lane >>> (64 - n)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function for padding the specified data based on the
|
||||
* pad10*1 algorithm (section 5.1) and the 2-bit suffix "01" required
|
||||
* for SHA-3 hash (section 6.1).
|
||||
*/
|
||||
private static int setPaddingBytes(byte[] in, int len) {
|
||||
if (len != in.length) {
|
||||
// erase leftover values
|
||||
Arrays.fill(in, len, in.length, (byte)0);
|
||||
// directly store the padding bytes into the input
|
||||
// as the specified buffer is allocated w/ size = rateR
|
||||
in[len] |= (byte) 0x06;
|
||||
in[in.length - 1] |= (byte) 0x80;
|
||||
}
|
||||
return (in.length - len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function for transforming the specified state from
|
||||
* the byte array format into array of lanes as defined in
|
||||
* section 3.1.2.
|
||||
*/
|
||||
private static long[][] bytes2Lanes(byte[] s) {
|
||||
if (s.length != WIDTH) {
|
||||
throw new ProviderException("Error: incorrect input size " +
|
||||
s.length);
|
||||
}
|
||||
// The conversion traverses along x-axis before y-axis. So, y is the
|
||||
// first dimension and x is the second dimension.
|
||||
long[][] s2 = new long[DM][DM];
|
||||
int sOfs = 0;
|
||||
for (int y = 0; y < DM; y++, sOfs += 40) {
|
||||
b2lLittle(s, sOfs, s2[y], 0, 40);
|
||||
}
|
||||
return s2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function for transforming the specified arrays of
|
||||
* lanes into a byte array as defined in section 3.1.3.
|
||||
*/
|
||||
private static byte[] lanes2Bytes(long[][] m) {
|
||||
byte[] s = new byte[WIDTH];
|
||||
int sOfs = 0;
|
||||
// The conversion traverses along x-axis before y-axis. So, y is the
|
||||
// first dimension and x is the second dimension.
|
||||
for (int y = 0; y < DM; y++, sOfs += 40) {
|
||||
l2bLittle(m[y], 0, s, sOfs, 40);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Step mapping Theta as defined in section 3.2.1 .
|
||||
*/
|
||||
private static long[][] smTheta(long[][] a) {
|
||||
long[] c = new long[DM];
|
||||
for (int i = 0; i < DM; i++) {
|
||||
c[i] = a[0][i]^a[1][i]^a[2][i]^a[3][i]^a[4][i];
|
||||
}
|
||||
long[] d = new long[DM];
|
||||
for (int i = 0; i < DM; i++) {
|
||||
long c1 = c[(i + 4) % DM];
|
||||
// left shift and wrap the leftmost bit into the rightmost bit
|
||||
long c2 = circularShiftLeft(c[(i + 1) % DM], 1);
|
||||
d[i] = c1^c2;
|
||||
}
|
||||
for (int y = 0; y < DM; y++) {
|
||||
for (int x = 0; x < DM; x++) {
|
||||
a[y][x] ^= d[x];
|
||||
}
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* Step mapping Rho as defined in section 3.2.2.
|
||||
*/
|
||||
private static long[][] smRho(long[][] a) {
|
||||
long[][] a2 = new long[DM][DM];
|
||||
a2[0][0] = a[0][0];
|
||||
int xNext, yNext;
|
||||
for (int t = 0, x = 1, y = 0; t <= 23; t++, x = xNext, y = yNext) {
|
||||
int numberOfShift = ((t + 1)*(t + 2)/2) % 64;
|
||||
a2[y][x] = circularShiftLeft(a[y][x], numberOfShift);
|
||||
xNext = y;
|
||||
yNext = (2 * x + 3 * y) % DM;
|
||||
}
|
||||
return a2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Step mapping Pi as defined in section 3.2.3.
|
||||
*/
|
||||
private static long[][] smPi(long[][] a) {
|
||||
long[][] a2 = new long[DM][DM];
|
||||
for (int y = 0; y < DM; y++) {
|
||||
for (int x = 0; x < DM; x++) {
|
||||
a2[y][x] = a[x][(x + 3 * y) % DM];
|
||||
}
|
||||
}
|
||||
return a2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Step mapping Chi as defined in section 3.2.4.
|
||||
*/
|
||||
private static long[][] smChi(long[][] a) {
|
||||
long[][] a2 = new long[DM][DM];
|
||||
for (int y = 0; y < DM; y++) {
|
||||
for (int x = 0; x < DM; x++) {
|
||||
a2[y][x] = a[y][x] ^
|
||||
((a[y][(x + 1) % DM] ^ 0xFFFFFFFFFFFFFFFFL) &
|
||||
a[y][(x + 2) % DM]);
|
||||
}
|
||||
}
|
||||
return a2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Step mapping Iota as defined in section 3.2.5.
|
||||
*
|
||||
* @return the processed state array
|
||||
* @param state the state array to be processed
|
||||
*/
|
||||
private static long[][] smIota(long[][] a, int rndIndex) {
|
||||
a[0][0] ^= RC_CONSTANTS[rndIndex];
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function Keccak as defined in section 5.2 with
|
||||
* rate r = 1600 and capacity c = (digest length x 2).
|
||||
*/
|
||||
private static byte[] keccak(byte[] state) {
|
||||
long[][] lanes = bytes2Lanes(state);
|
||||
for (int ir = 0; ir < NR; ir++) {
|
||||
lanes = smIota(smChi(smPi(smRho(smTheta(lanes)))), ir);
|
||||
}
|
||||
return lanes2Bytes(lanes);
|
||||
}
|
||||
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
SHA3 copy = (SHA3) super.clone();
|
||||
copy.state = copy.state.clone();
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* SHA3-224 implementation class.
|
||||
*/
|
||||
public static final class SHA224 extends SHA3 {
|
||||
public SHA224() {
|
||||
super("SHA3-224", 28);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SHA3-256 implementation class.
|
||||
*/
|
||||
public static final class SHA256 extends SHA3 {
|
||||
public SHA256() {
|
||||
super("SHA3-256", 32);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SHAs-384 implementation class.
|
||||
*/
|
||||
public static final class SHA384 extends SHA3 {
|
||||
public SHA384() {
|
||||
super("SHA3-384", 48);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SHA3-512 implementation class.
|
||||
*/
|
||||
public static final class SHA512 extends SHA3 {
|
||||
public SHA512() {
|
||||
super("SHA3-512", 64);
|
||||
}
|
||||
}
|
||||
}
|
@ -156,6 +156,7 @@ implements java.io.Serializable {
|
||||
}
|
||||
}
|
||||
state = digest.digest(seed);
|
||||
remCount = 0;
|
||||
}
|
||||
|
||||
private static void updateState(byte[] state, byte[] output) {
|
||||
|
@ -211,6 +211,25 @@ final class SunEntries {
|
||||
map.put("Alg.Alias.MessageDigest.OID.2.16.840.1.101.3.4.2.6",
|
||||
"SHA-512/256");
|
||||
|
||||
map.put("MessageDigest.SHA3-224", "sun.security.provider.SHA3$SHA224");
|
||||
map.put("Alg.Alias.MessageDigest.2.16.840.1.101.3.4.2.7", "SHA3-224");
|
||||
map.put("Alg.Alias.MessageDigest.OID.2.16.840.1.101.3.4.2.7",
|
||||
"SHA3-224");
|
||||
|
||||
map.put("MessageDigest.SHA3-256", "sun.security.provider.SHA3$SHA256");
|
||||
map.put("Alg.Alias.MessageDigest.2.16.840.1.101.3.4.2.8", "SHA3-256");
|
||||
map.put("Alg.Alias.MessageDigest.OID.2.16.840.1.101.3.4.2.8",
|
||||
"SHA3-256");
|
||||
map.put("MessageDigest.SHA3-384", "sun.security.provider.SHA3$SHA384");
|
||||
map.put("Alg.Alias.MessageDigest.2.16.840.1.101.3.4.2.9", "SHA3-384");
|
||||
map.put("Alg.Alias.MessageDigest.OID.2.16.840.1.101.3.4.2.9",
|
||||
"SHA3-384");
|
||||
map.put("MessageDigest.SHA3-512", "sun.security.provider.SHA3$SHA512");
|
||||
map.put("Alg.Alias.MessageDigest.2.16.840.1.101.3.4.2.10", "SHA3-512");
|
||||
map.put("Alg.Alias.MessageDigest.OID.2.16.840.1.101.3.4.2.10",
|
||||
"SHA3-512");
|
||||
|
||||
|
||||
/*
|
||||
* Algorithm Parameter Generator engines
|
||||
*/
|
||||
|
@ -51,7 +51,7 @@ public final class OCSPNonceExtension extends Extension {
|
||||
private byte[] nonceData = null;
|
||||
|
||||
/**
|
||||
* Create a {@code OCSPNonceExtension} by providing the nonce length.
|
||||
* Create an {@code OCSPNonceExtension} by providing the nonce length.
|
||||
* The criticality is set to false, and the OID for the extension will
|
||||
* be the value defined by "id-pkix-ocsp-nonce" from RFC 6960.
|
||||
*
|
||||
@ -66,7 +66,7 @@ public final class OCSPNonceExtension extends Extension {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@code OCSPNonceExtension} by providing the nonce length and
|
||||
* Create an {@code OCSPNonceExtension} by providing the nonce length and
|
||||
* criticality setting. The OID for the extension will
|
||||
* be the value defined by "id-pkix-ocsp-nonce" from RFC 6960.
|
||||
*
|
||||
@ -96,7 +96,7 @@ public final class OCSPNonceExtension extends Extension {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@code OCSPNonceExtension} by providing a nonce value.
|
||||
* Create an {@code OCSPNonceExtension} by providing a nonce value.
|
||||
* The criticality is set to false, and the OID for the extension will
|
||||
* be the value defined by "id-pkix-ocsp-nonce" from RFC 6960.
|
||||
*
|
||||
@ -114,7 +114,7 @@ public final class OCSPNonceExtension extends Extension {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@code OCSPNonceExtension} by providing a nonce value and
|
||||
* Create an {@code OCSPNonceExtension} by providing a nonce value and
|
||||
* criticality setting. The OID for the extension will
|
||||
* be the value defined by "id-pkix-ocsp-nonce" from RFC 6960.
|
||||
*
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2002, 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
|
||||
@ -108,7 +108,7 @@ final class HandshakeHash {
|
||||
* a hash for the certificate verify message is required.
|
||||
*/
|
||||
HandshakeHash(boolean needCertificateVerify) {
|
||||
clonesNeeded = needCertificateVerify ? 3 : 2;
|
||||
clonesNeeded = needCertificateVerify ? 4 : 3;
|
||||
}
|
||||
|
||||
void reserve(ByteBuffer input) {
|
||||
|
@ -123,7 +123,7 @@ final class SSLSocketInputRecord extends InputRecord implements SSLRecord {
|
||||
*/
|
||||
//
|
||||
// Short header is using here. We reverse the code here
|
||||
// in case it it used in the future.
|
||||
// in case it is used in the future.
|
||||
//
|
||||
// int mask = (isShort ? 0x7F : 0x3F);
|
||||
// len = ((byteZero & mask) << 8) +
|
||||
|
@ -552,58 +552,61 @@ public class AlgorithmId implements Serializable, DerEncoder {
|
||||
return AlgorithmId.sha512WithECDSA_oid;
|
||||
}
|
||||
|
||||
// See if any of the installed providers supply a mapping from
|
||||
// the given algorithm name to an OID string
|
||||
String oidString;
|
||||
if (!initOidTable) {
|
||||
Provider[] provs = Security.getProviders();
|
||||
for (int i=0; i<provs.length; i++) {
|
||||
for (Enumeration<Object> enum_ = provs[i].keys();
|
||||
enum_.hasMoreElements(); ) {
|
||||
String alias = (String)enum_.nextElement();
|
||||
String upperCaseAlias = alias.toUpperCase(Locale.ENGLISH);
|
||||
int index;
|
||||
if (upperCaseAlias.startsWith("ALG.ALIAS") &&
|
||||
(index=upperCaseAlias.indexOf("OID.", 0)) != -1) {
|
||||
index += "OID.".length();
|
||||
if (index == alias.length()) {
|
||||
// invalid alias entry
|
||||
break;
|
||||
}
|
||||
if (oidTable == null) {
|
||||
oidTable = new HashMap<>();
|
||||
}
|
||||
oidString = alias.substring(index);
|
||||
String stdAlgName = provs[i].getProperty(alias);
|
||||
if (stdAlgName != null) {
|
||||
stdAlgName = stdAlgName.toUpperCase(Locale.ENGLISH);
|
||||
}
|
||||
if (stdAlgName != null &&
|
||||
oidTable.get(stdAlgName) == null) {
|
||||
oidTable.put(stdAlgName,
|
||||
new ObjectIdentifier(oidString));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (oidTable == null) {
|
||||
oidTable = Collections.<String,ObjectIdentifier>emptyMap();
|
||||
}
|
||||
initOidTable = true;
|
||||
}
|
||||
|
||||
return oidTable.get(name.toUpperCase(Locale.ENGLISH));
|
||||
return oidTable().get(name.toUpperCase(Locale.ENGLISH));
|
||||
}
|
||||
|
||||
private static ObjectIdentifier oid(int ... values) {
|
||||
return ObjectIdentifier.newInternal(values);
|
||||
}
|
||||
|
||||
private static boolean initOidTable = false;
|
||||
private static Map<String,ObjectIdentifier> oidTable;
|
||||
private static volatile Map<String,ObjectIdentifier> oidTable;
|
||||
private static final Map<ObjectIdentifier,String> nameTable;
|
||||
|
||||
/** Returns the oidTable, lazily initializing it on first access. */
|
||||
private static Map<String,ObjectIdentifier> oidTable()
|
||||
throws IOException {
|
||||
// Double checked locking; safe because oidTable is volatile
|
||||
Map<String,ObjectIdentifier> tab;
|
||||
if ((tab = oidTable) == null) {
|
||||
synchronized (AlgorithmId.class) {
|
||||
if ((tab = oidTable) == null)
|
||||
oidTable = tab = computeOidTable();
|
||||
}
|
||||
}
|
||||
return tab;
|
||||
}
|
||||
|
||||
/** Collects the algorithm names from the installed providers. */
|
||||
private static HashMap<String,ObjectIdentifier> computeOidTable()
|
||||
throws IOException {
|
||||
HashMap<String,ObjectIdentifier> tab = new HashMap<>();
|
||||
for (Provider provider : Security.getProviders()) {
|
||||
for (Object key : provider.keySet()) {
|
||||
String alias = (String)key;
|
||||
String upperCaseAlias = alias.toUpperCase(Locale.ENGLISH);
|
||||
int index;
|
||||
if (upperCaseAlias.startsWith("ALG.ALIAS") &&
|
||||
(index=upperCaseAlias.indexOf("OID.", 0)) != -1) {
|
||||
index += "OID.".length();
|
||||
if (index == alias.length()) {
|
||||
// invalid alias entry
|
||||
break;
|
||||
}
|
||||
String oidString = alias.substring(index);
|
||||
String stdAlgName = provider.getProperty(alias);
|
||||
if (stdAlgName != null) {
|
||||
stdAlgName = stdAlgName.toUpperCase(Locale.ENGLISH);
|
||||
}
|
||||
if (stdAlgName != null &&
|
||||
tab.get(stdAlgName) == null) {
|
||||
tab.put(stdAlgName, new ObjectIdentifier(oidString));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return tab;
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
|
||||
/*
|
||||
|
@ -206,16 +206,15 @@ securerandom.strongAlgorithms=NativePRNGBlocking:SUN,DRBG:SUN
|
||||
# "Hash_DRBG" | "HMAC_DRBG" | "CTR_DRBG"
|
||||
#
|
||||
# // The DRBG algorithm name. The "SHA-***" names are for Hash_DRBG and
|
||||
# // HMAC_DRBG, default "SHA-256". "3KeyTDEA" and "AES-***" names are for
|
||||
# // CTR_DRBG, default "AES-128" when using the limited cryptographic
|
||||
# // or "AES-256" when using the unlimited.
|
||||
# // HMAC_DRBG, default "SHA-256". The "AES-***" names are for CTR_DRBG,
|
||||
# // default "AES-128" when using the limited cryptographic or "AES-256"
|
||||
# // when using the unlimited.
|
||||
# algorithm_name:
|
||||
# "SHA-1" | "SHA-224" | "SHA-512/224" | "SHA-256" |
|
||||
# "SHA-224" | "SHA-512/224" | "SHA-256" |
|
||||
# "SHA-512/256" | "SHA-384" | "SHA-512" |
|
||||
# "3KeyTDEA" | "AES-128" | "AES-192" | "AES-256"
|
||||
# "AES-128" | "AES-192" | "AES-256"
|
||||
#
|
||||
# // Security strength requested. Default "128", or "112"
|
||||
# // if mech_name is CTR_DRBG and algorithm_name is "3KeyTDEA"
|
||||
# // Security strength requested. Default "128"
|
||||
# strength:
|
||||
# "112" | "128" | "192" | "256"
|
||||
#
|
||||
@ -234,7 +233,7 @@ securerandom.strongAlgorithms=NativePRNGBlocking:SUN,DRBG:SUN
|
||||
# "use_df" | "no_df"
|
||||
#
|
||||
# Examples,
|
||||
# securerandom.drbg.config=Hash_DRBG,SHA-1,112,none
|
||||
# securerandom.drbg.config=Hash_DRBG,SHA-224,112,none
|
||||
# securerandom.drbg.config=CTR_DRBG,AES-256,192,pr_and_reseed,use_df
|
||||
#
|
||||
# The default value is an empty string, which is equivalent to
|
||||
|
@ -36,7 +36,7 @@ static JNINativeMethod methods[] = {
|
||||
};
|
||||
|
||||
JNIEXPORT jobject JNICALL
|
||||
Java_jdk_internal_misc_VM_latestUserDefinedLoader(JNIEnv *env, jclass cls) {
|
||||
Java_jdk_internal_misc_VM_latestUserDefinedLoader0(JNIEnv *env, jclass cls) {
|
||||
return JVM_LatestUserDefinedLoader(env);
|
||||
}
|
||||
|
||||
|
@ -469,7 +469,8 @@ public final class NativePRNG extends SecureRandomSpi {
|
||||
try {
|
||||
seedOut.write(seed);
|
||||
} catch (IOException e) {
|
||||
throw new ProviderException("setSeed() failed", e);
|
||||
// Ignored. On Mac OS X, /dev/urandom can be opened
|
||||
// for write, but actual write is not permitted.
|
||||
}
|
||||
}
|
||||
getMixRandom().engineSetSeed(seed);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,159 +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. 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
|
||||
*/
|
||||
package java.net.http;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.CharacterCodingException;
|
||||
import java.nio.charset.CharsetDecoder;
|
||||
import java.nio.charset.CharsetEncoder;
|
||||
import java.nio.charset.CoderResult;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
// The purpose of this class is to separate charset-related tasks from the main
|
||||
// WebSocket logic, simplifying where possible.
|
||||
//
|
||||
// * Coders hide the differences between coding and flushing stages on the
|
||||
// API level
|
||||
// * Verifier abstracts the way the verification is performed
|
||||
// (spoiler: it's a decoding into a throw-away buffer)
|
||||
//
|
||||
// Coding methods throw exceptions instead of returning coding result denoting
|
||||
// errors, since any kind of handling and recovery is not expected.
|
||||
final class CharsetToolkit {
|
||||
|
||||
private CharsetToolkit() { }
|
||||
|
||||
static final class Verifier {
|
||||
|
||||
private final CharsetDecoder decoder = UTF_8.newDecoder();
|
||||
// A buffer used to check validity of UTF-8 byte stream by decoding it.
|
||||
// The contents of this buffer are never used.
|
||||
// The size is arbitrary, though it should probably be chosen from the
|
||||
// performance perspective since it affects the total number of calls to
|
||||
// decoder.decode() and amount of work in each of these calls
|
||||
private final CharBuffer blackHole = CharBuffer.allocate(1024);
|
||||
|
||||
void verify(ByteBuffer in, boolean endOfInput)
|
||||
throws CharacterCodingException {
|
||||
while (true) {
|
||||
// Since decoder.flush() cannot produce an error, it's not
|
||||
// helpful for verification. Therefore this step is skipped.
|
||||
CoderResult r = decoder.decode(in, blackHole, endOfInput);
|
||||
if (r.isOverflow()) {
|
||||
blackHole.clear();
|
||||
} else if (r.isUnderflow()) {
|
||||
break;
|
||||
} else if (r.isError()) {
|
||||
r.throwException();
|
||||
} else {
|
||||
// Should not happen
|
||||
throw new InternalError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Verifier reset() {
|
||||
decoder.reset();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
static final class Encoder {
|
||||
|
||||
private final CharsetEncoder encoder = UTF_8.newEncoder();
|
||||
private boolean coding = true;
|
||||
|
||||
CoderResult encode(CharBuffer in, ByteBuffer out, boolean endOfInput)
|
||||
throws CharacterCodingException {
|
||||
|
||||
if (coding) {
|
||||
CoderResult r = encoder.encode(in, out, endOfInput);
|
||||
if (r.isOverflow()) {
|
||||
return r;
|
||||
} else if (r.isUnderflow()) {
|
||||
if (endOfInput) {
|
||||
coding = false;
|
||||
} else {
|
||||
return r;
|
||||
}
|
||||
} else if (r.isError()) {
|
||||
r.throwException();
|
||||
} else {
|
||||
// Should not happen
|
||||
throw new InternalError();
|
||||
}
|
||||
}
|
||||
assert !coding;
|
||||
return encoder.flush(out);
|
||||
}
|
||||
|
||||
Encoder reset() {
|
||||
coding = true;
|
||||
encoder.reset();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
static CharBuffer decode(ByteBuffer in) throws CharacterCodingException {
|
||||
return UTF_8.newDecoder().decode(in);
|
||||
}
|
||||
|
||||
static final class Decoder {
|
||||
|
||||
private final CharsetDecoder decoder = UTF_8.newDecoder();
|
||||
private boolean coding = true; // Either coding or flushing
|
||||
|
||||
CoderResult decode(ByteBuffer in, CharBuffer out, boolean endOfInput)
|
||||
throws CharacterCodingException {
|
||||
|
||||
if (coding) {
|
||||
CoderResult r = decoder.decode(in, out, endOfInput);
|
||||
if (r.isOverflow()) {
|
||||
return r;
|
||||
} else if (r.isUnderflow()) {
|
||||
if (endOfInput) {
|
||||
coding = false;
|
||||
} else {
|
||||
return r;
|
||||
}
|
||||
} else if (r.isError()) {
|
||||
r.throwException();
|
||||
} else {
|
||||
// Should not happen
|
||||
throw new InternalError();
|
||||
}
|
||||
}
|
||||
assert !coding;
|
||||
return decoder.flush(out);
|
||||
}
|
||||
|
||||
Decoder reset() {
|
||||
coding = true;
|
||||
decoder.reset();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
@ -39,18 +39,22 @@ final class RawChannel implements ByteChannel, GatheringByteChannel {
|
||||
|
||||
private final HttpClientImpl client;
|
||||
private final HttpConnection connection;
|
||||
private volatile boolean closed;
|
||||
|
||||
private interface RawEvent {
|
||||
|
||||
/** must return the selector interest op flags OR'd. */
|
||||
/**
|
||||
* must return the selector interest op flags OR'd.
|
||||
*/
|
||||
int interestOps();
|
||||
|
||||
/** called when event occurs. */
|
||||
/**
|
||||
* called when event occurs.
|
||||
*/
|
||||
void handle();
|
||||
}
|
||||
|
||||
interface NonBlockingEvent extends RawEvent { }
|
||||
interface NonBlockingEvent extends RawEvent {
|
||||
}
|
||||
|
||||
RawChannel(HttpClientImpl client, HttpConnection connection) {
|
||||
this.client = client;
|
||||
@ -127,12 +131,11 @@ final class RawChannel implements ByteChannel, GatheringByteChannel {
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return !closed;
|
||||
return connection.isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
closed = true;
|
||||
connection.close();
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,9 @@ import java.nio.ByteBuffer;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.LongConsumer;
|
||||
|
||||
@ -409,13 +412,20 @@ class Stream extends ExchangeImpl {
|
||||
@Override
|
||||
HttpResponseImpl getResponse() throws IOException {
|
||||
try {
|
||||
return getResponseAsync(null).join();
|
||||
} catch (Throwable e) {
|
||||
if (request.timeval() > 0) {
|
||||
return getResponseAsync(null).get(
|
||||
request.timeval(), TimeUnit.MILLISECONDS);
|
||||
} else {
|
||||
return getResponseAsync(null).join();
|
||||
}
|
||||
} catch (TimeoutException e) {
|
||||
throw new HttpTimeoutException("Response timed out");
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
Throwable t = e.getCause();
|
||||
if (t instanceof IOException) {
|
||||
throw (IOException)t;
|
||||
}
|
||||
throw e;
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
390
jdk/src/java.httpclient/share/classes/java/net/http/WS.java
Normal file
390
jdk/src/java.httpclient/share/classes/java/net/http/WS.java
Normal file
@ -0,0 +1,390 @@
|
||||
/*
|
||||
* 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
|
||||
* 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 java.net.http;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ProtocolException;
|
||||
import java.net.http.WSOpeningHandshake.Result;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.lang.System.Logger.Level.ERROR;
|
||||
import static java.lang.System.Logger.Level.WARNING;
|
||||
import static java.net.http.WSUtils.logger;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/*
|
||||
* A WebSocket client.
|
||||
*
|
||||
* Consists of two independent parts; a transmitter responsible for sending
|
||||
* messages, and a receiver which notifies the listener of incoming messages.
|
||||
*/
|
||||
final class WS implements WebSocket {
|
||||
|
||||
private final String subprotocol;
|
||||
private final RawChannel channel;
|
||||
private final WSTransmitter transmitter;
|
||||
private final WSReceiver receiver;
|
||||
private final Listener listener;
|
||||
private final Object stateLock = new Object();
|
||||
private volatile State state = State.CONNECTED;
|
||||
private final CompletableFuture<Void> whenClosed = new CompletableFuture<>();
|
||||
|
||||
static CompletableFuture<WebSocket> newInstanceAsync(WSBuilder b) {
|
||||
CompletableFuture<Result> result = new WSOpeningHandshake(b).performAsync();
|
||||
Listener listener = b.getListener();
|
||||
Executor executor = b.getClient().executorService();
|
||||
return result.thenApply(r -> {
|
||||
WS ws = new WS(listener, r.subprotocol, r.channel, executor);
|
||||
ws.start();
|
||||
return ws;
|
||||
});
|
||||
}
|
||||
|
||||
private WS(Listener listener, String subprotocol, RawChannel channel,
|
||||
Executor executor) {
|
||||
this.listener = wrapListener(listener);
|
||||
this.channel = channel;
|
||||
this.subprotocol = subprotocol;
|
||||
Consumer<Throwable> errorHandler = error -> {
|
||||
if (error == null) {
|
||||
throw new InternalError();
|
||||
}
|
||||
// If the channel is closed, we need to update the state, to denote
|
||||
// there's no point in trying to continue using WebSocket
|
||||
if (!channel.isOpen()) {
|
||||
synchronized (stateLock) {
|
||||
tryChangeState(State.ERROR);
|
||||
}
|
||||
}
|
||||
};
|
||||
transmitter = new WSTransmitter(executor, channel, errorHandler);
|
||||
receiver = new WSReceiver(this.listener, this, executor, channel);
|
||||
}
|
||||
|
||||
private void start() {
|
||||
receiver.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> sendText(ByteBuffer message, boolean isLast) {
|
||||
throw new UnsupportedOperationException("Not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> sendText(CharSequence message, boolean isLast) {
|
||||
requireNonNull(message, "message");
|
||||
synchronized (stateLock) {
|
||||
checkState();
|
||||
return transmitter.sendText(message, isLast);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> sendText(Stream<? extends CharSequence> message) {
|
||||
requireNonNull(message, "message");
|
||||
synchronized (stateLock) {
|
||||
checkState();
|
||||
return transmitter.sendText(message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> sendBinary(ByteBuffer message, boolean isLast) {
|
||||
requireNonNull(message, "message");
|
||||
synchronized (stateLock) {
|
||||
checkState();
|
||||
return transmitter.sendBinary(message, isLast);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> sendPing(ByteBuffer message) {
|
||||
requireNonNull(message, "message");
|
||||
synchronized (stateLock) {
|
||||
checkState();
|
||||
return transmitter.sendPing(message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> sendPong(ByteBuffer message) {
|
||||
requireNonNull(message, "message");
|
||||
synchronized (stateLock) {
|
||||
checkState();
|
||||
return transmitter.sendPong(message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> sendClose(CloseCode code, CharSequence reason) {
|
||||
requireNonNull(code, "code");
|
||||
requireNonNull(reason, "reason");
|
||||
synchronized (stateLock) {
|
||||
return doSendClose(() -> transmitter.sendClose(code, reason));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> sendClose() {
|
||||
synchronized (stateLock) {
|
||||
return doSendClose(() -> transmitter.sendClose());
|
||||
}
|
||||
}
|
||||
|
||||
private CompletableFuture<Void> doSendClose(Supplier<CompletableFuture<Void>> s) {
|
||||
checkState();
|
||||
boolean closeChannel = false;
|
||||
synchronized (stateLock) {
|
||||
if (state == State.CLOSED_REMOTELY) {
|
||||
closeChannel = tryChangeState(State.CLOSED);
|
||||
} else {
|
||||
tryChangeState(State.CLOSED_LOCALLY);
|
||||
}
|
||||
}
|
||||
CompletableFuture<Void> sent = s.get();
|
||||
if (closeChannel) {
|
||||
sent.whenComplete((v, t) -> {
|
||||
try {
|
||||
channel.close();
|
||||
} catch (IOException e) {
|
||||
logger.log(ERROR, "Error transitioning to state " + State.CLOSED, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
return sent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long request(long n) {
|
||||
if (n < 0L) {
|
||||
throw new IllegalArgumentException("The number must not be negative: " + n);
|
||||
}
|
||||
return receiver.request(n);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSubprotocol() {
|
||||
return subprotocol;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return state.isTerminal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abort() throws IOException {
|
||||
synchronized (stateLock) {
|
||||
tryChangeState(State.ABORTED);
|
||||
}
|
||||
channel.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + "[" + state + "]";
|
||||
}
|
||||
|
||||
private void checkState() {
|
||||
if (state.isTerminal() || state == State.CLOSED_LOCALLY) {
|
||||
throw new IllegalStateException("WebSocket is closed [" + state + "]");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Wraps the user's listener passed to the constructor into own listener to
|
||||
* intercept transitions to terminal states (onClose and onError) and to act
|
||||
* upon exceptions and values from the user's listener.
|
||||
*/
|
||||
private Listener wrapListener(Listener listener) {
|
||||
return new Listener() {
|
||||
|
||||
// Listener's method MUST be invoked in a happen-before order
|
||||
private final Object visibilityLock = new Object();
|
||||
|
||||
@Override
|
||||
public void onOpen(WebSocket webSocket) {
|
||||
synchronized (visibilityLock) {
|
||||
listener.onOpen(webSocket);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletionStage<?> onText(WebSocket webSocket, Text message,
|
||||
MessagePart part) {
|
||||
synchronized (visibilityLock) {
|
||||
return listener.onText(webSocket, message, part);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletionStage<?> onBinary(WebSocket webSocket, ByteBuffer message,
|
||||
MessagePart part) {
|
||||
synchronized (visibilityLock) {
|
||||
return listener.onBinary(webSocket, message, part);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletionStage<?> onPing(WebSocket webSocket, ByteBuffer message) {
|
||||
synchronized (visibilityLock) {
|
||||
return listener.onPing(webSocket, message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletionStage<?> onPong(WebSocket webSocket, ByteBuffer message) {
|
||||
synchronized (visibilityLock) {
|
||||
return listener.onPong(webSocket, message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(WebSocket webSocket, Optional<CloseCode> code, String reason) {
|
||||
synchronized (stateLock) {
|
||||
if (state == State.CLOSED_REMOTELY || state.isTerminal()) {
|
||||
throw new InternalError("Unexpected onClose in state " + state);
|
||||
} else if (state == State.CLOSED_LOCALLY) {
|
||||
try {
|
||||
channel.close();
|
||||
} catch (IOException e) {
|
||||
logger.log(ERROR, "Error transitioning to state " + State.CLOSED, e);
|
||||
}
|
||||
tryChangeState(State.CLOSED);
|
||||
} else if (state == State.CONNECTED) {
|
||||
tryChangeState(State.CLOSED_REMOTELY);
|
||||
}
|
||||
}
|
||||
synchronized (visibilityLock) {
|
||||
listener.onClose(webSocket, code, reason);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(WebSocket webSocket, Throwable error) {
|
||||
// An error doesn't necessarily mean the connection must be
|
||||
// closed automatically
|
||||
if (!channel.isOpen()) {
|
||||
synchronized (stateLock) {
|
||||
tryChangeState(State.ERROR);
|
||||
}
|
||||
} else if (error instanceof ProtocolException
|
||||
&& error.getCause() instanceof WSProtocolException) {
|
||||
WSProtocolException cause = (WSProtocolException) error.getCause();
|
||||
logger.log(WARNING, "Failing connection {0}, reason: ''{1}''",
|
||||
webSocket, cause.getMessage());
|
||||
CloseCode cc = cause.getCloseCode();
|
||||
transmitter.sendClose(cc, "").whenComplete((v, t) -> {
|
||||
synchronized (stateLock) {
|
||||
tryChangeState(State.ERROR);
|
||||
}
|
||||
try {
|
||||
channel.close();
|
||||
} catch (IOException e) {
|
||||
logger.log(ERROR, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
synchronized (visibilityLock) {
|
||||
listener.onError(webSocket, error);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private boolean tryChangeState(State newState) {
|
||||
assert Thread.holdsLock(stateLock);
|
||||
if (state.isTerminal()) {
|
||||
return false;
|
||||
}
|
||||
state = newState;
|
||||
if (newState.isTerminal()) {
|
||||
whenClosed.complete(null);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
CompletionStage<Void> whenClosed() {
|
||||
return whenClosed;
|
||||
}
|
||||
|
||||
/*
|
||||
* WebSocket connection internal state.
|
||||
*/
|
||||
private enum State {
|
||||
|
||||
/*
|
||||
* Initial WebSocket state. The WebSocket is connected (i.e. remains in
|
||||
* this state) unless proven otherwise. For example, by reading or
|
||||
* writing operations on the channel.
|
||||
*/
|
||||
CONNECTED,
|
||||
|
||||
/*
|
||||
* A Close message has been received by the client. No more messages
|
||||
* will be received.
|
||||
*/
|
||||
CLOSED_REMOTELY,
|
||||
|
||||
/*
|
||||
* A Close message has been sent by the client. No more messages can be
|
||||
* sent.
|
||||
*/
|
||||
CLOSED_LOCALLY,
|
||||
|
||||
/*
|
||||
* Close messages has been both sent and received (closing handshake)
|
||||
* and TCP connection closed. Closed _cleanly_ in terms of RFC 6455.
|
||||
*/
|
||||
CLOSED,
|
||||
|
||||
/*
|
||||
* The connection has been aborted by the client. Closed not _cleanly_
|
||||
* in terms of RFC 6455.
|
||||
*/
|
||||
ABORTED,
|
||||
|
||||
/*
|
||||
* The connection has been terminated due to a protocol or I/O error.
|
||||
* Might happen during sending or receiving.
|
||||
*/
|
||||
ERROR;
|
||||
|
||||
/*
|
||||
* Returns `true` if this state is terminal. If WebSocket has transited
|
||||
* to such a state, if remains in it forever.
|
||||
*/
|
||||
boolean isTerminal() {
|
||||
return this == CLOSED || this == ABORTED || this == ERROR;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* 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
|
||||
* 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 java.net.http;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
final class WSBuilder implements WebSocket.Builder {
|
||||
|
||||
private static final Set<String> FORBIDDEN_HEADERS =
|
||||
new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
|
||||
|
||||
static {
|
||||
List<String> headers = List.of("Connection", "Upgrade",
|
||||
"Sec-WebSocket-Accept", "Sec-WebSocket-Extensions",
|
||||
"Sec-WebSocket-Key", "Sec-WebSocket-Protocol",
|
||||
"Sec-WebSocket-Version");
|
||||
FORBIDDEN_HEADERS.addAll(headers);
|
||||
}
|
||||
|
||||
private final URI uri;
|
||||
private final HttpClient client;
|
||||
private final LinkedHashMap<String, List<String>> headers = new LinkedHashMap<>();
|
||||
private final WebSocket.Listener listener;
|
||||
private Collection<String> subprotocols = Collections.emptyList();
|
||||
private long timeout;
|
||||
private TimeUnit timeUnit;
|
||||
|
||||
WSBuilder(URI uri, HttpClient client, WebSocket.Listener listener) {
|
||||
checkURI(requireNonNull(uri, "uri"));
|
||||
requireNonNull(client, "client");
|
||||
requireNonNull(listener, "listener");
|
||||
this.uri = uri;
|
||||
this.listener = listener;
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebSocket.Builder header(String name, String value) {
|
||||
requireNonNull(name, "name");
|
||||
requireNonNull(value, "value");
|
||||
if (FORBIDDEN_HEADERS.contains(name)) {
|
||||
throw new IllegalArgumentException(
|
||||
format("Header '%s' is used in the WebSocket Protocol", name));
|
||||
}
|
||||
List<String> values = headers.computeIfAbsent(name, n -> new LinkedList<>());
|
||||
values.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebSocket.Builder subprotocols(String mostPreferred, String... lesserPreferred) {
|
||||
requireNonNull(mostPreferred, "mostPreferred");
|
||||
requireNonNull(lesserPreferred, "lesserPreferred");
|
||||
this.subprotocols = checkSubprotocols(mostPreferred, lesserPreferred);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebSocket.Builder connectTimeout(long timeout, TimeUnit unit) {
|
||||
if (timeout < 0) {
|
||||
throw new IllegalArgumentException("Negative timeout: " + timeout);
|
||||
}
|
||||
requireNonNull(unit, "unit");
|
||||
this.timeout = timeout;
|
||||
this.timeUnit = unit;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<WebSocket> buildAsync() {
|
||||
return WS.newInstanceAsync(this);
|
||||
}
|
||||
|
||||
private static URI checkURI(URI uri) {
|
||||
String s = uri.getScheme();
|
||||
if (!("ws".equalsIgnoreCase(s) || "wss".equalsIgnoreCase(s))) {
|
||||
throw new IllegalArgumentException
|
||||
("URI scheme not ws or wss (RFC 6455 3.): " + s);
|
||||
}
|
||||
String fragment = uri.getFragment();
|
||||
if (fragment != null) {
|
||||
throw new IllegalArgumentException(format
|
||||
("Fragment not allowed in a WebSocket URI (RFC 6455 3.): '%s'",
|
||||
fragment));
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
|
||||
URI getUri() { return uri; }
|
||||
|
||||
HttpClient getClient() { return client; }
|
||||
|
||||
Map<String, List<String>> getHeaders() {
|
||||
LinkedHashMap<String, List<String>> copy = new LinkedHashMap<>(headers.size());
|
||||
headers.forEach((name, values) -> copy.put(name, new LinkedList<>(values)));
|
||||
return copy;
|
||||
}
|
||||
|
||||
WebSocket.Listener getListener() { return listener; }
|
||||
|
||||
Collection<String> getSubprotocols() {
|
||||
return new ArrayList<>(subprotocols);
|
||||
}
|
||||
|
||||
long getTimeout() { return timeout; }
|
||||
|
||||
TimeUnit getTimeUnit() { return timeUnit; }
|
||||
|
||||
private static Collection<String> checkSubprotocols(String mostPreferred,
|
||||
String... lesserPreferred) {
|
||||
checkSubprotocolSyntax(mostPreferred, "mostPreferred");
|
||||
LinkedHashSet<String> sp = new LinkedHashSet<>(1 + lesserPreferred.length);
|
||||
sp.add(mostPreferred);
|
||||
for (int i = 0; i < lesserPreferred.length; i++) {
|
||||
String p = lesserPreferred[i];
|
||||
String location = format("lesserPreferred[%s]", i);
|
||||
requireNonNull(p, location);
|
||||
checkSubprotocolSyntax(p, location);
|
||||
if (!sp.add(p)) {
|
||||
throw new IllegalArgumentException(format(
|
||||
"Duplicate subprotocols (RFC 6455 4.1.): '%s'", p));
|
||||
}
|
||||
}
|
||||
return sp;
|
||||
}
|
||||
|
||||
private static void checkSubprotocolSyntax(String subprotocol, String location) {
|
||||
if (subprotocol.isEmpty()) {
|
||||
throw new IllegalArgumentException
|
||||
("Subprotocol name is empty (RFC 6455 4.1.): " + location);
|
||||
}
|
||||
if (!subprotocol.chars().allMatch(c -> 0x21 <= c && c <= 0x7e)) {
|
||||
throw new IllegalArgumentException
|
||||
("Subprotocol name contains illegal characters (RFC 6455 4.1.): "
|
||||
+ location);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,126 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
package java.net.http;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.CharacterCodingException;
|
||||
import java.nio.charset.CharsetDecoder;
|
||||
import java.nio.charset.CharsetEncoder;
|
||||
import java.nio.charset.CoderResult;
|
||||
import java.nio.charset.CodingErrorAction;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static java.lang.System.Logger.Level.WARNING;
|
||||
import static java.net.http.WSUtils.EMPTY_BYTE_BUFFER;
|
||||
import static java.net.http.WSUtils.logger;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
/*
|
||||
* A collection of tools for UTF-8 coding.
|
||||
*/
|
||||
final class WSCharsetToolkit {
|
||||
|
||||
private WSCharsetToolkit() { }
|
||||
|
||||
static final class Encoder {
|
||||
|
||||
private final CharsetEncoder encoder = UTF_8.newEncoder();
|
||||
|
||||
ByteBuffer encode(CharBuffer in) throws CharacterCodingException {
|
||||
return encoder.encode(in);
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// ByteBuffer[] encode(CharBuffer in) throws CharacterCodingException {
|
||||
// return encoder.encode(in);
|
||||
// }
|
||||
}
|
||||
|
||||
static CharBuffer decode(ByteBuffer in) throws CharacterCodingException {
|
||||
return UTF_8.newDecoder().decode(in);
|
||||
}
|
||||
|
||||
static final class Decoder {
|
||||
|
||||
private final CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
|
||||
|
||||
{
|
||||
decoder.onMalformedInput(CodingErrorAction.REPORT);
|
||||
decoder.onUnmappableCharacter(CodingErrorAction.REPORT);
|
||||
}
|
||||
|
||||
private ByteBuffer leftovers = EMPTY_BYTE_BUFFER;
|
||||
|
||||
WSShared<CharBuffer> decode(WSShared<ByteBuffer> in, boolean endOfInput)
|
||||
throws CharacterCodingException {
|
||||
ByteBuffer b;
|
||||
int rem = leftovers.remaining();
|
||||
if (rem != 0) {
|
||||
// TODO: We won't need this wasteful allocation & copying when
|
||||
// JDK-8155222 has been resolved
|
||||
b = ByteBuffer.allocate(rem + in.remaining());
|
||||
b.put(leftovers).put(in.buffer()).flip();
|
||||
} else {
|
||||
b = in.buffer();
|
||||
}
|
||||
CharBuffer out = CharBuffer.allocate(b.remaining());
|
||||
CoderResult r = decoder.decode(b, out, endOfInput);
|
||||
if (r.isError()) {
|
||||
r.throwException();
|
||||
}
|
||||
if (b.hasRemaining()) {
|
||||
leftovers = ByteBuffer.allocate(b.remaining()).put(b).flip();
|
||||
} else {
|
||||
leftovers = EMPTY_BYTE_BUFFER;
|
||||
}
|
||||
// Since it's UTF-8, the assumption is leftovers.remaining() < 4
|
||||
// (i.e. small). Otherwise a shared buffer should be used
|
||||
if (!(leftovers.remaining() < 4)) {
|
||||
logger.log(WARNING,
|
||||
"The size of decoding leftovers is greater than expected: {0}",
|
||||
leftovers.remaining());
|
||||
}
|
||||
b.position(b.limit()); // As if we always read to the end
|
||||
in.dispose();
|
||||
// Decoder promises that in the case of endOfInput == true:
|
||||
// "...any remaining undecoded input will be treated as being
|
||||
// malformed"
|
||||
assert !(endOfInput && leftovers.hasRemaining()) : endOfInput + ", " + leftovers;
|
||||
if (endOfInput) {
|
||||
r = decoder.flush(out);
|
||||
decoder.reset();
|
||||
if (r.isOverflow()) {
|
||||
// FIXME: for now I know flush() does nothing. But the
|
||||
// implementation of UTF8 decoder might change. And if now
|
||||
// flush() is a no-op, it is not guaranteed to remain so in
|
||||
// the future
|
||||
throw new InternalError("Not yet implemented");
|
||||
}
|
||||
}
|
||||
out.flip();
|
||||
return WSShared.wrap(out);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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 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 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 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 java.net.http;
|
||||
|
||||
interface WSDisposable {
|
||||
|
||||
void dispose();
|
||||
}
|
@ -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 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 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 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 java.net.http;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
|
||||
final class WSDisposableText implements WebSocket.Text, WSDisposable {
|
||||
|
||||
private final WSShared<CharBuffer> text;
|
||||
|
||||
WSDisposableText(WSShared<CharBuffer> text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int length() {
|
||||
return text.buffer().length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public char charAt(int index) {
|
||||
return text.buffer().charAt(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence subSequence(int start, int end) {
|
||||
return text.buffer().subSequence(start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer asByteBuffer() {
|
||||
throw new UnsupportedOperationException("To be removed from the API");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return text.buffer().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
text.dispose();
|
||||
}
|
||||
}
|
486
jdk/src/java.httpclient/share/classes/java/net/http/WSFrame.java
Normal file
486
jdk/src/java.httpclient/share/classes/java/net/http/WSFrame.java
Normal file
@ -0,0 +1,486 @@
|
||||
/*
|
||||
* 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 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 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 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 java.net.http;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static java.net.http.WSFrame.Opcode.ofCode;
|
||||
import static java.net.http.WSUtils.dump;
|
||||
|
||||
/*
|
||||
* A collection of utilities for reading, writing, and masking frames.
|
||||
*/
|
||||
final class WSFrame {
|
||||
|
||||
private WSFrame() { }
|
||||
|
||||
static final int MAX_HEADER_SIZE_BYTES = 2 + 8 + 4;
|
||||
|
||||
enum Opcode {
|
||||
|
||||
CONTINUATION (0x0),
|
||||
TEXT (0x1),
|
||||
BINARY (0x2),
|
||||
NON_CONTROL_0x3(0x3),
|
||||
NON_CONTROL_0x4(0x4),
|
||||
NON_CONTROL_0x5(0x5),
|
||||
NON_CONTROL_0x6(0x6),
|
||||
NON_CONTROL_0x7(0x7),
|
||||
CLOSE (0x8),
|
||||
PING (0x9),
|
||||
PONG (0xA),
|
||||
CONTROL_0xB (0xB),
|
||||
CONTROL_0xC (0xC),
|
||||
CONTROL_0xD (0xD),
|
||||
CONTROL_0xE (0xE),
|
||||
CONTROL_0xF (0xF);
|
||||
|
||||
private static final Opcode[] opcodes;
|
||||
|
||||
static {
|
||||
Opcode[] values = values();
|
||||
opcodes = new Opcode[values.length];
|
||||
for (Opcode c : values) {
|
||||
assert opcodes[c.code] == null
|
||||
: WSUtils.dump(c, c.code, opcodes[c.code]);
|
||||
opcodes[c.code] = c;
|
||||
}
|
||||
}
|
||||
|
||||
private final byte code;
|
||||
private final char shiftedCode;
|
||||
private final String description;
|
||||
|
||||
Opcode(int code) {
|
||||
this.code = (byte) code;
|
||||
this.shiftedCode = (char) (code << 8);
|
||||
this.description = format("%x (%s)", code, name());
|
||||
}
|
||||
|
||||
boolean isControl() {
|
||||
return (code & 0x8) != 0;
|
||||
}
|
||||
|
||||
static Opcode ofCode(int code) {
|
||||
return opcodes[code & 0xF];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* A utility to mask payload data.
|
||||
*/
|
||||
static final class Masker {
|
||||
|
||||
private final ByteBuffer acc = ByteBuffer.allocate(8);
|
||||
private final int[] maskBytes = new int[4];
|
||||
private int offset;
|
||||
private long maskLong;
|
||||
|
||||
/*
|
||||
* Sets up the mask.
|
||||
*/
|
||||
Masker mask(int value) {
|
||||
acc.clear().putInt(value).putInt(value).flip();
|
||||
for (int i = 0; i < maskBytes.length; i++) {
|
||||
maskBytes[i] = acc.get(i);
|
||||
}
|
||||
offset = 0;
|
||||
maskLong = acc.getLong(0);
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads as many bytes as possible from the given input buffer, writing
|
||||
* the resulting masked bytes to the given output buffer.
|
||||
*
|
||||
* src.remaining() <= dst.remaining() // TODO: do we need this restriction?
|
||||
* 'src' and 'dst' can be the same ByteBuffer
|
||||
*/
|
||||
Masker applyMask(ByteBuffer src, ByteBuffer dst) {
|
||||
if (src.remaining() > dst.remaining()) {
|
||||
throw new IllegalArgumentException(dump(src, dst));
|
||||
}
|
||||
begin(src, dst);
|
||||
loop(src, dst);
|
||||
end(src, dst);
|
||||
return this;
|
||||
}
|
||||
|
||||
// Applying the remaining of the mask (strictly not more than 3 bytes)
|
||||
// byte-wise
|
||||
private void begin(ByteBuffer src, ByteBuffer dst) {
|
||||
if (offset > 0) {
|
||||
for (int i = src.position(), j = dst.position();
|
||||
offset < 4 && i <= src.limit() - 1 && j <= dst.limit() - 1;
|
||||
i++, j++, offset++) {
|
||||
dst.put(j, (byte) (src.get(i) ^ maskBytes[offset]));
|
||||
dst.position(j + 1);
|
||||
src.position(i + 1);
|
||||
}
|
||||
offset &= 3;
|
||||
}
|
||||
}
|
||||
|
||||
private void loop(ByteBuffer src, ByteBuffer dst) {
|
||||
int i = src.position();
|
||||
int j = dst.position();
|
||||
final int srcLim = src.limit() - 8;
|
||||
final int dstLim = dst.limit() - 8;
|
||||
for (; i <= srcLim && j <= dstLim; i += 8, j += 8) {
|
||||
dst.putLong(j, (src.getLong(i) ^ maskLong));
|
||||
}
|
||||
if (i > src.limit()) {
|
||||
src.position(i - 8);
|
||||
} else {
|
||||
src.position(i);
|
||||
}
|
||||
if (j > dst.limit()) {
|
||||
dst.position(j - 8);
|
||||
} else {
|
||||
dst.position(j);
|
||||
}
|
||||
}
|
||||
|
||||
// Applying the mask to the remaining bytes byte-wise (don't make any
|
||||
// assumptions on how many, hopefully not more than 7 for 64bit arch)
|
||||
private void end(ByteBuffer src, ByteBuffer dst) {
|
||||
for (int i = src.position(), j = dst.position();
|
||||
i <= src.limit() - 1 && j <= dst.limit() - 1;
|
||||
i++, j++, offset = (offset + 1) & 3) { // offset cycle through 0..3
|
||||
dst.put(j, (byte) (src.get(i) ^ maskBytes[offset]));
|
||||
src.position(i + 1);
|
||||
dst.position(j + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* A builder of frame headers, capable of writing to a given buffer.
|
||||
*
|
||||
* The builder does not enforce any protocol-level rules, it simply writes
|
||||
* a header structure to the buffer. The order of calls to intermediate
|
||||
* methods is not significant.
|
||||
*/
|
||||
static final class HeaderBuilder {
|
||||
|
||||
private char firstChar;
|
||||
private long payloadLen;
|
||||
private int maskingKey;
|
||||
private boolean mask;
|
||||
|
||||
HeaderBuilder fin(boolean value) {
|
||||
if (value) {
|
||||
firstChar |= 0b10000000_00000000;
|
||||
} else {
|
||||
firstChar &= ~0b10000000_00000000;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
HeaderBuilder rsv1(boolean value) {
|
||||
if (value) {
|
||||
firstChar |= 0b01000000_00000000;
|
||||
} else {
|
||||
firstChar &= ~0b01000000_00000000;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
HeaderBuilder rsv2(boolean value) {
|
||||
if (value) {
|
||||
firstChar |= 0b00100000_00000000;
|
||||
} else {
|
||||
firstChar &= ~0b00100000_00000000;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
HeaderBuilder rsv3(boolean value) {
|
||||
if (value) {
|
||||
firstChar |= 0b00010000_00000000;
|
||||
} else {
|
||||
firstChar &= ~0b00010000_00000000;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
HeaderBuilder opcode(Opcode value) {
|
||||
firstChar = (char) ((firstChar & 0xF0FF) | value.shiftedCode);
|
||||
return this;
|
||||
}
|
||||
|
||||
HeaderBuilder payloadLen(long value) {
|
||||
payloadLen = value;
|
||||
firstChar &= 0b11111111_10000000; // Clear previous payload length leftovers
|
||||
if (payloadLen < 126) {
|
||||
firstChar |= payloadLen;
|
||||
} else if (payloadLen < 65535) {
|
||||
firstChar |= 126;
|
||||
} else {
|
||||
firstChar |= 127;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
HeaderBuilder mask(int value) {
|
||||
firstChar |= 0b00000000_10000000;
|
||||
maskingKey = value;
|
||||
mask = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
HeaderBuilder noMask() {
|
||||
firstChar &= ~0b00000000_10000000;
|
||||
mask = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes the header to the given buffer.
|
||||
*
|
||||
* The buffer must have at least MAX_HEADER_SIZE_BYTES remaining. The
|
||||
* buffer's position is incremented by the number of bytes written.
|
||||
*/
|
||||
void build(ByteBuffer buffer) {
|
||||
buffer.putChar(firstChar);
|
||||
if (payloadLen >= 126) {
|
||||
if (payloadLen < 65535) {
|
||||
buffer.putChar((char) payloadLen);
|
||||
} else {
|
||||
buffer.putLong(payloadLen);
|
||||
}
|
||||
}
|
||||
if (mask) {
|
||||
buffer.putInt(maskingKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* A consumer of frame parts.
|
||||
*
|
||||
* Guaranteed to be called in the following order by the Frame.Reader:
|
||||
*
|
||||
* fin rsv1 rsv2 rsv3 opcode mask payloadLength maskingKey? payloadData+ endFrame
|
||||
*/
|
||||
interface Consumer {
|
||||
|
||||
void fin(boolean value);
|
||||
|
||||
void rsv1(boolean value);
|
||||
|
||||
void rsv2(boolean value);
|
||||
|
||||
void rsv3(boolean value);
|
||||
|
||||
void opcode(Opcode value);
|
||||
|
||||
void mask(boolean value);
|
||||
|
||||
void payloadLen(long value);
|
||||
|
||||
void maskingKey(int value);
|
||||
|
||||
/*
|
||||
* Called when a part of the payload is ready to be consumed.
|
||||
*
|
||||
* Though may not yield a complete payload in a single invocation, i.e.
|
||||
*
|
||||
* data.remaining() < payloadLen
|
||||
*
|
||||
* the sum of `data.remaining()` passed to all invocations of this
|
||||
* method will be equal to 'payloadLen', reported in
|
||||
* `void payloadLen(long value)`
|
||||
*
|
||||
* No unmasking is done.
|
||||
*/
|
||||
void payloadData(WSShared<ByteBuffer> data, boolean isLast);
|
||||
|
||||
void endFrame(); // TODO: remove (payloadData(isLast=true)) should be enough
|
||||
}
|
||||
|
||||
/*
|
||||
* A Reader of Frames.
|
||||
*
|
||||
* No protocol-level rules are enforced, only frame structure.
|
||||
*/
|
||||
static final class Reader {
|
||||
|
||||
private static final int AWAITING_FIRST_BYTE = 1;
|
||||
private static final int AWAITING_SECOND_BYTE = 2;
|
||||
private static final int READING_16_LENGTH = 4;
|
||||
private static final int READING_64_LENGTH = 8;
|
||||
private static final int READING_MASK = 16;
|
||||
private static final int READING_PAYLOAD = 32;
|
||||
|
||||
// A private buffer used to simplify multi-byte integers reading
|
||||
private final ByteBuffer accumulator = ByteBuffer.allocate(8);
|
||||
private int state = AWAITING_FIRST_BYTE;
|
||||
private boolean mask;
|
||||
private long payloadLength;
|
||||
|
||||
/*
|
||||
* Reads at most one frame from the given buffer invoking the consumer's
|
||||
* methods corresponding to the frame elements found.
|
||||
*
|
||||
* As much of the frame's payload, if any, is read. The buffers position
|
||||
* is updated to reflect the number of bytes read.
|
||||
*
|
||||
* Throws WSProtocolException if the frame is malformed.
|
||||
*/
|
||||
void readFrame(WSShared<ByteBuffer> shared, Consumer consumer) {
|
||||
ByteBuffer input = shared.buffer();
|
||||
loop:
|
||||
while (true) {
|
||||
byte b;
|
||||
switch (state) {
|
||||
case AWAITING_FIRST_BYTE:
|
||||
if (!input.hasRemaining()) {
|
||||
break loop;
|
||||
}
|
||||
b = input.get();
|
||||
consumer.fin( (b & 0b10000000) != 0);
|
||||
consumer.rsv1((b & 0b01000000) != 0);
|
||||
consumer.rsv2((b & 0b00100000) != 0);
|
||||
consumer.rsv3((b & 0b00010000) != 0);
|
||||
consumer.opcode(ofCode(b));
|
||||
state = AWAITING_SECOND_BYTE;
|
||||
continue loop;
|
||||
case AWAITING_SECOND_BYTE:
|
||||
if (!input.hasRemaining()) {
|
||||
break loop;
|
||||
}
|
||||
b = input.get();
|
||||
consumer.mask(mask = (b & 0b10000000) != 0);
|
||||
byte p1 = (byte) (b & 0b01111111);
|
||||
if (p1 < 126) {
|
||||
assert p1 >= 0 : p1;
|
||||
consumer.payloadLen(payloadLength = p1);
|
||||
state = mask ? READING_MASK : READING_PAYLOAD;
|
||||
} else if (p1 < 127) {
|
||||
state = READING_16_LENGTH;
|
||||
} else {
|
||||
state = READING_64_LENGTH;
|
||||
}
|
||||
continue loop;
|
||||
case READING_16_LENGTH:
|
||||
if (!input.hasRemaining()) {
|
||||
break loop;
|
||||
}
|
||||
b = input.get();
|
||||
if (accumulator.put(b).position() < 2) {
|
||||
continue loop;
|
||||
}
|
||||
payloadLength = accumulator.flip().getChar();
|
||||
if (payloadLength < 126) {
|
||||
throw notMinimalEncoding(payloadLength, 2);
|
||||
}
|
||||
consumer.payloadLen(payloadLength);
|
||||
accumulator.clear();
|
||||
state = mask ? READING_MASK : READING_PAYLOAD;
|
||||
continue loop;
|
||||
case READING_64_LENGTH:
|
||||
if (!input.hasRemaining()) {
|
||||
break loop;
|
||||
}
|
||||
b = input.get();
|
||||
if (accumulator.put(b).position() < 8) {
|
||||
continue loop;
|
||||
}
|
||||
payloadLength = accumulator.flip().getLong();
|
||||
if (payloadLength < 0) {
|
||||
throw negativePayload(payloadLength);
|
||||
} else if (payloadLength < 65535) {
|
||||
throw notMinimalEncoding(payloadLength, 8);
|
||||
}
|
||||
consumer.payloadLen(payloadLength);
|
||||
accumulator.clear();
|
||||
state = mask ? READING_MASK : READING_PAYLOAD;
|
||||
continue loop;
|
||||
case READING_MASK:
|
||||
if (!input.hasRemaining()) {
|
||||
break loop;
|
||||
}
|
||||
b = input.get();
|
||||
if (accumulator.put(b).position() != 4) {
|
||||
continue loop;
|
||||
}
|
||||
consumer.maskingKey(accumulator.flip().getInt());
|
||||
accumulator.clear();
|
||||
state = READING_PAYLOAD;
|
||||
continue loop;
|
||||
case READING_PAYLOAD:
|
||||
// This state does not require any bytes to be available
|
||||
// in the input buffer in order to proceed
|
||||
boolean fullyRead;
|
||||
int limit;
|
||||
if (payloadLength <= input.remaining()) {
|
||||
limit = input.position() + (int) payloadLength;
|
||||
payloadLength = 0;
|
||||
fullyRead = true;
|
||||
} else {
|
||||
limit = input.limit();
|
||||
payloadLength -= input.remaining();
|
||||
fullyRead = false;
|
||||
}
|
||||
// FIXME: consider a case where payloadLen != 0,
|
||||
// but input.remaining() == 0
|
||||
//
|
||||
// There shouldn't be an invocation of payloadData with
|
||||
// an empty buffer, as it would be an artifact of
|
||||
// reading
|
||||
consumer.payloadData(shared.share(input.position(), limit), fullyRead);
|
||||
// Update the position manually, since reading the
|
||||
// payload doesn't advance buffer's position
|
||||
input.position(limit);
|
||||
if (fullyRead) {
|
||||
consumer.endFrame();
|
||||
state = AWAITING_FIRST_BYTE;
|
||||
}
|
||||
break loop;
|
||||
default:
|
||||
throw new InternalError(String.valueOf(state));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static WSProtocolException negativePayload(long payloadLength) {
|
||||
return new WSProtocolException
|
||||
("5.2.", format("Negative 64-bit payload length %s", payloadLength));
|
||||
}
|
||||
|
||||
private static WSProtocolException notMinimalEncoding(long payloadLength, int numBytes) {
|
||||
return new WSProtocolException
|
||||
("5.2.", format("Payload length (%s) is not encoded with minimal number (%s) of bytes",
|
||||
payloadLength, numBytes));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,289 @@
|
||||
/*
|
||||
* 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 java.net.http;
|
||||
|
||||
import java.net.http.WSFrame.Opcode;
|
||||
import java.net.http.WebSocket.MessagePart;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.CharacterCodingException;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static java.lang.System.Logger.Level.TRACE;
|
||||
import static java.net.http.WSUtils.dump;
|
||||
import static java.net.http.WSUtils.logger;
|
||||
import static java.net.http.WebSocket.CloseCode.NOT_CONSISTENT;
|
||||
import static java.net.http.WebSocket.CloseCode.of;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/*
|
||||
* Consumes frame parts and notifies a message consumer, when there is
|
||||
* sufficient data to produce a message, or part thereof.
|
||||
*
|
||||
* Data consumed but not yet translated is accumulated until it's sufficient to
|
||||
* form a message.
|
||||
*/
|
||||
final class WSFrameConsumer implements WSFrame.Consumer {
|
||||
|
||||
private final AtomicInteger invocationOrder = new AtomicInteger();
|
||||
|
||||
private final WSMessageConsumer output;
|
||||
private final WSCharsetToolkit.Decoder decoder = new WSCharsetToolkit.Decoder();
|
||||
private boolean fin;
|
||||
private Opcode opcode, originatingOpcode;
|
||||
private MessagePart part = MessagePart.WHOLE;
|
||||
private long payloadLen;
|
||||
private WSShared<ByteBuffer> binaryData;
|
||||
|
||||
WSFrameConsumer(WSMessageConsumer output) {
|
||||
this.output = requireNonNull(output);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fin(boolean value) {
|
||||
assert invocationOrder.compareAndSet(0, 1) : dump(invocationOrder, value);
|
||||
if (logger.isLoggable(TRACE)) {
|
||||
// Checked for being loggable because of autoboxing of 'value'
|
||||
logger.log(TRACE, "Reading fin: {0}", value);
|
||||
}
|
||||
fin = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rsv1(boolean value) {
|
||||
assert invocationOrder.compareAndSet(1, 2) : dump(invocationOrder, value);
|
||||
if (logger.isLoggable(TRACE)) {
|
||||
logger.log(TRACE, "Reading rsv1: {0}", value);
|
||||
}
|
||||
if (value) {
|
||||
throw new WSProtocolException("5.2.", "rsv1 bit is set unexpectedly");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rsv2(boolean value) {
|
||||
assert invocationOrder.compareAndSet(2, 3) : dump(invocationOrder, value);
|
||||
if (logger.isLoggable(TRACE)) {
|
||||
logger.log(TRACE, "Reading rsv2: {0}", value);
|
||||
}
|
||||
if (value) {
|
||||
throw new WSProtocolException("5.2.", "rsv2 bit is set unexpectedly");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rsv3(boolean value) {
|
||||
assert invocationOrder.compareAndSet(3, 4) : dump(invocationOrder, value);
|
||||
if (logger.isLoggable(TRACE)) {
|
||||
logger.log(TRACE, "Reading rsv3: {0}", value);
|
||||
}
|
||||
if (value) {
|
||||
throw new WSProtocolException("5.2.", "rsv3 bit is set unexpectedly");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void opcode(Opcode v) {
|
||||
assert invocationOrder.compareAndSet(4, 5) : dump(invocationOrder, v);
|
||||
logger.log(TRACE, "Reading opcode: {0}", v);
|
||||
if (v == Opcode.PING || v == Opcode.PONG || v == Opcode.CLOSE) {
|
||||
if (!fin) {
|
||||
throw new WSProtocolException("5.5.", "A fragmented control frame " + v);
|
||||
}
|
||||
opcode = v;
|
||||
} else if (v == Opcode.TEXT || v == Opcode.BINARY) {
|
||||
if (originatingOpcode != null) {
|
||||
throw new WSProtocolException
|
||||
("5.4.", format("An unexpected frame %s (fin=%s)", v, fin));
|
||||
}
|
||||
opcode = v;
|
||||
if (!fin) {
|
||||
originatingOpcode = v;
|
||||
}
|
||||
} else if (v == Opcode.CONTINUATION) {
|
||||
if (originatingOpcode == null) {
|
||||
throw new WSProtocolException
|
||||
("5.4.", format("An unexpected frame %s (fin=%s)", v, fin));
|
||||
}
|
||||
opcode = v;
|
||||
} else {
|
||||
throw new WSProtocolException("5.2.", "An unknown opcode " + v);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mask(boolean value) {
|
||||
assert invocationOrder.compareAndSet(5, 6) : dump(invocationOrder, value);
|
||||
if (logger.isLoggable(TRACE)) {
|
||||
logger.log(TRACE, "Reading mask: {0}", value);
|
||||
}
|
||||
if (value) {
|
||||
throw new WSProtocolException
|
||||
("5.1.", "Received a masked frame from the server");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void payloadLen(long value) {
|
||||
assert invocationOrder.compareAndSet(6, 7) : dump(invocationOrder, value);
|
||||
if (logger.isLoggable(TRACE)) {
|
||||
logger.log(TRACE, "Reading payloadLen: {0}", value);
|
||||
}
|
||||
if (opcode.isControl()) {
|
||||
if (value > 125) {
|
||||
throw new WSProtocolException
|
||||
("5.5.", format("A control frame %s has a payload length of %s",
|
||||
opcode, value));
|
||||
}
|
||||
assert Opcode.CLOSE.isControl();
|
||||
if (opcode == Opcode.CLOSE && value == 1) {
|
||||
throw new WSProtocolException
|
||||
("5.5.1.", "A Close frame's status code is only 1 byte long");
|
||||
}
|
||||
}
|
||||
payloadLen = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void maskingKey(int value) {
|
||||
assert false : dump(invocationOrder, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void payloadData(WSShared<ByteBuffer> data, boolean isLast) {
|
||||
assert invocationOrder.compareAndSet(7, isLast ? 8 : 7)
|
||||
: dump(invocationOrder, data, isLast);
|
||||
if (logger.isLoggable(TRACE)) {
|
||||
logger.log(TRACE, "Reading payloadData: data={0}, isLast={1}", data, isLast);
|
||||
}
|
||||
if (opcode.isControl()) {
|
||||
if (binaryData != null) {
|
||||
binaryData.put(data);
|
||||
data.dispose();
|
||||
} else if (!isLast) {
|
||||
// The first chunk of the message
|
||||
int remaining = data.remaining();
|
||||
// It shouldn't be 125, otherwise the next chunk will be of size
|
||||
// 0, which is not what Reader promises to deliver (eager
|
||||
// reading)
|
||||
assert remaining < 125 : dump(remaining);
|
||||
WSShared<ByteBuffer> b = WSShared.wrap(ByteBuffer.allocate(125)).put(data);
|
||||
data.dispose();
|
||||
binaryData = b; // Will be disposed by the user
|
||||
} else {
|
||||
// The only chunk; will be disposed by the user
|
||||
binaryData = data.position(data.limit()); // FIXME: remove this hack
|
||||
}
|
||||
} else {
|
||||
part = determinePart(isLast);
|
||||
boolean text = opcode == Opcode.TEXT || originatingOpcode == Opcode.TEXT;
|
||||
if (!text) {
|
||||
output.onBinary(part, data);
|
||||
} else {
|
||||
boolean binaryNonEmpty = data.hasRemaining();
|
||||
WSShared<CharBuffer> textData;
|
||||
try {
|
||||
textData = decoder.decode(data, part.isLast());
|
||||
} catch (CharacterCodingException e) {
|
||||
throw new WSProtocolException
|
||||
("5.6.", "Invalid UTF-8 sequence in frame " + opcode, NOT_CONSISTENT, e);
|
||||
}
|
||||
if (!(binaryNonEmpty && !textData.hasRemaining())) {
|
||||
// If there's a binary data, that result in no text, then we
|
||||
// don't deliver anything
|
||||
output.onText(part, new WSDisposableText(textData));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endFrame() {
|
||||
assert invocationOrder.compareAndSet(8, 0) : dump(invocationOrder);
|
||||
if (opcode.isControl()) {
|
||||
binaryData.flip();
|
||||
}
|
||||
switch (opcode) {
|
||||
case CLOSE:
|
||||
WebSocket.CloseCode cc;
|
||||
String reason;
|
||||
if (payloadLen == 0) {
|
||||
cc = null;
|
||||
reason = "";
|
||||
} else {
|
||||
ByteBuffer b = binaryData.buffer();
|
||||
int len = b.remaining();
|
||||
assert 2 <= len && len <= 125 : dump(len, payloadLen);
|
||||
try {
|
||||
cc = of(b.getChar());
|
||||
reason = WSCharsetToolkit.decode(b).toString();
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new WSProtocolException
|
||||
("5.5.1", "Incorrect status code", e);
|
||||
} catch (CharacterCodingException e) {
|
||||
throw new WSProtocolException
|
||||
("5.5.1", "Close reason is a malformed UTF-8 sequence", e);
|
||||
}
|
||||
}
|
||||
binaryData.dispose(); // Manual dispose
|
||||
output.onClose(cc, reason);
|
||||
break;
|
||||
case PING:
|
||||
output.onPing(binaryData);
|
||||
binaryData = null;
|
||||
break;
|
||||
case PONG:
|
||||
output.onPong(binaryData);
|
||||
binaryData = null;
|
||||
break;
|
||||
default:
|
||||
assert opcode == Opcode.TEXT || opcode == Opcode.BINARY
|
||||
|| opcode == Opcode.CONTINUATION : dump(opcode);
|
||||
if (fin) {
|
||||
// It is always the last chunk:
|
||||
// either TEXT(FIN=TRUE)/BINARY(FIN=TRUE) or CONT(FIN=TRUE)
|
||||
originatingOpcode = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
payloadLen = 0;
|
||||
opcode = null;
|
||||
}
|
||||
|
||||
private MessagePart determinePart(boolean isLast) {
|
||||
boolean lastChunk = fin && isLast;
|
||||
switch (part) {
|
||||
case LAST:
|
||||
case WHOLE:
|
||||
return lastChunk ? MessagePart.WHOLE : MessagePart.FIRST;
|
||||
case FIRST:
|
||||
case PART:
|
||||
return lastChunk ? MessagePart.LAST : MessagePart.PART;
|
||||
default:
|
||||
throw new InternalError(String.valueOf(part));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1996, Oracle and/or its affiliates. All rights reserved.
|
||||
* 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
|
||||
@ -22,18 +22,21 @@
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.rmi.transport.proxy;
|
||||
package java.net.http;
|
||||
|
||||
/**
|
||||
* RMISocketInfo is an interface that extensions of the java.net.Socket
|
||||
* class may use to provide more information on its capabilities.
|
||||
*/
|
||||
public interface RMISocketInfo {
|
||||
import java.net.http.WebSocket.CloseCode;
|
||||
import java.net.http.WebSocket.MessagePart;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Return true if this socket can be used for more than one
|
||||
* RMI call. If a socket does not implement this interface, then
|
||||
* it is assumed to be reusable.
|
||||
*/
|
||||
public boolean isReusable();
|
||||
interface WSMessageConsumer {
|
||||
|
||||
void onText(MessagePart part, WSDisposableText data);
|
||||
|
||||
void onBinary(MessagePart part, WSShared<ByteBuffer> data);
|
||||
|
||||
void onPing(WSShared<ByteBuffer> data);
|
||||
|
||||
void onPong(WSShared<ByteBuffer> data);
|
||||
|
||||
void onClose(CloseCode code, CharSequence reason);
|
||||
}
|
@ -0,0 +1,189 @@
|
||||
/*
|
||||
* 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 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 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 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 java.net.http;
|
||||
|
||||
import java.net.http.WSFrame.HeaderBuilder;
|
||||
import java.net.http.WSFrame.Masker;
|
||||
import java.net.http.WSOutgoingMessage.Binary;
|
||||
import java.net.http.WSOutgoingMessage.Close;
|
||||
import java.net.http.WSOutgoingMessage.Ping;
|
||||
import java.net.http.WSOutgoingMessage.Pong;
|
||||
import java.net.http.WSOutgoingMessage.StreamedText;
|
||||
import java.net.http.WSOutgoingMessage.Text;
|
||||
import java.net.http.WSOutgoingMessage.Visitor;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.CharacterCodingException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static java.net.http.WSFrame.MAX_HEADER_SIZE_BYTES;
|
||||
import static java.net.http.WSFrame.Opcode.BINARY;
|
||||
import static java.net.http.WSFrame.Opcode.CLOSE;
|
||||
import static java.net.http.WSFrame.Opcode.CONTINUATION;
|
||||
import static java.net.http.WSFrame.Opcode.PING;
|
||||
import static java.net.http.WSFrame.Opcode.PONG;
|
||||
import static java.net.http.WSFrame.Opcode.TEXT;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/*
|
||||
* A Sender of outgoing messages. Given a message,
|
||||
*
|
||||
* 1) constructs the frame
|
||||
* 2) initiates the channel write
|
||||
* 3) notifies when the message has been sent
|
||||
*/
|
||||
final class WSMessageSender {
|
||||
|
||||
private final Visitor frameBuilderVisitor;
|
||||
private final Consumer<Throwable> completionEventConsumer;
|
||||
private final WSWriter writer;
|
||||
private final ByteBuffer[] buffers = new ByteBuffer[2];
|
||||
|
||||
WSMessageSender(RawChannel channel, Consumer<Throwable> completionEventConsumer) {
|
||||
// Single reusable buffer that holds a header
|
||||
this.buffers[0] = ByteBuffer.allocateDirect(MAX_HEADER_SIZE_BYTES);
|
||||
this.frameBuilderVisitor = new FrameBuilderVisitor();
|
||||
this.completionEventConsumer = completionEventConsumer;
|
||||
this.writer = new WSWriter(channel, this.completionEventConsumer);
|
||||
}
|
||||
|
||||
/*
|
||||
* Tries to send the given message fully. Invoked once per message.
|
||||
*/
|
||||
boolean trySendFully(WSOutgoingMessage m) {
|
||||
requireNonNull(m);
|
||||
synchronized (this) {
|
||||
try {
|
||||
return sendNow(m);
|
||||
} catch (Exception e) {
|
||||
completionEventConsumer.accept(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean sendNow(WSOutgoingMessage m) {
|
||||
buffers[0].clear();
|
||||
m.accept(frameBuilderVisitor);
|
||||
buffers[0].flip();
|
||||
return writer.tryWriteFully(buffers);
|
||||
}
|
||||
|
||||
/*
|
||||
* Builds and initiates a write of a frame, from a given message.
|
||||
*/
|
||||
class FrameBuilderVisitor implements Visitor {
|
||||
|
||||
private final SecureRandom random = new SecureRandom();
|
||||
private final WSCharsetToolkit.Encoder encoder = new WSCharsetToolkit.Encoder();
|
||||
private final Masker masker = new Masker();
|
||||
private final HeaderBuilder headerBuilder = new HeaderBuilder();
|
||||
private boolean previousIsLast = true;
|
||||
|
||||
@Override
|
||||
public void visit(Text message) {
|
||||
try {
|
||||
buffers[1] = encoder.encode(CharBuffer.wrap(message.characters));
|
||||
} catch (CharacterCodingException e) {
|
||||
completionEventConsumer.accept(e);
|
||||
return;
|
||||
}
|
||||
int mask = random.nextInt();
|
||||
maskAndRewind(buffers[1], mask);
|
||||
headerBuilder
|
||||
.fin(message.isLast)
|
||||
.opcode(previousIsLast ? TEXT : CONTINUATION)
|
||||
.payloadLen(buffers[1].remaining())
|
||||
.mask(mask)
|
||||
.build(buffers[0]);
|
||||
previousIsLast = message.isLast;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(StreamedText streamedText) {
|
||||
throw new IllegalArgumentException("Not yet implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Binary message) {
|
||||
buffers[1] = message.bytes;
|
||||
int mask = random.nextInt();
|
||||
maskAndRewind(buffers[1], mask);
|
||||
headerBuilder
|
||||
.fin(message.isLast)
|
||||
.opcode(previousIsLast ? BINARY : CONTINUATION)
|
||||
.payloadLen(message.bytes.remaining())
|
||||
.mask(mask)
|
||||
.build(buffers[0]);
|
||||
previousIsLast = message.isLast;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Ping message) {
|
||||
buffers[1] = message.bytes;
|
||||
int mask = random.nextInt();
|
||||
maskAndRewind(buffers[1], mask);
|
||||
headerBuilder
|
||||
.fin(true)
|
||||
.opcode(PING)
|
||||
.payloadLen(message.bytes.remaining())
|
||||
.mask(mask)
|
||||
.build(buffers[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Pong message) {
|
||||
buffers[1] = message.bytes;
|
||||
int mask = random.nextInt();
|
||||
maskAndRewind(buffers[1], mask);
|
||||
headerBuilder
|
||||
.fin(true)
|
||||
.opcode(PONG)
|
||||
.payloadLen(message.bytes.remaining())
|
||||
.mask(mask)
|
||||
.build(buffers[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(Close message) {
|
||||
buffers[1] = message.bytes;
|
||||
int mask = random.nextInt();
|
||||
maskAndRewind(buffers[1], mask);
|
||||
headerBuilder
|
||||
.fin(true)
|
||||
.opcode(CLOSE)
|
||||
.payloadLen(buffers[1].remaining())
|
||||
.mask(mask)
|
||||
.build(buffers[0]);
|
||||
}
|
||||
|
||||
private void maskAndRewind(ByteBuffer b, int mask) {
|
||||
int oldPos = b.position();
|
||||
masker.mask(mask).applyMask(b, b);
|
||||
b.position(oldPos);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,268 @@
|
||||
/*
|
||||
* 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
|
||||
* 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 java.net.http;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static java.lang.System.Logger.Level.TRACE;
|
||||
import static java.net.http.WSUtils.logger;
|
||||
import static java.net.http.WSUtils.webSocketSpecViolation;
|
||||
|
||||
final class WSOpeningHandshake {
|
||||
|
||||
private static final String HEADER_CONNECTION = "Connection";
|
||||
private static final String HEADER_UPGRADE = "Upgrade";
|
||||
private static final String HEADER_ACCEPT = "Sec-WebSocket-Accept";
|
||||
private static final String HEADER_EXTENSIONS = "Sec-WebSocket-Extensions";
|
||||
private static final String HEADER_KEY = "Sec-WebSocket-Key";
|
||||
private static final String HEADER_PROTOCOL = "Sec-WebSocket-Protocol";
|
||||
private static final String HEADER_VERSION = "Sec-WebSocket-Version";
|
||||
private static final String VALUE_VERSION = "13"; // WebSocket's lucky number
|
||||
|
||||
private static final SecureRandom srandom = new SecureRandom();
|
||||
|
||||
private final MessageDigest sha1;
|
||||
|
||||
{
|
||||
try {
|
||||
sha1 = MessageDigest.getInstance("SHA-1");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
// Shouldn't happen:
|
||||
// SHA-1 must be available in every Java platform implementation
|
||||
throw new InternalError("Minimum platform requirements are not met", e);
|
||||
}
|
||||
}
|
||||
|
||||
private final HttpRequest request;
|
||||
private final Collection<String> subprotocols;
|
||||
private final String nonce;
|
||||
|
||||
WSOpeningHandshake(WSBuilder b) {
|
||||
URI httpURI = createHttpUri(b.getUri());
|
||||
HttpRequest.Builder requestBuilder = b.getClient().request(httpURI);
|
||||
if (b.getTimeUnit() != null) {
|
||||
requestBuilder.timeout(b.getTimeUnit(), b.getTimeout());
|
||||
}
|
||||
Collection<String> s = b.getSubprotocols();
|
||||
if (!s.isEmpty()) {
|
||||
String p = s.stream().collect(Collectors.joining(", "));
|
||||
requestBuilder.header(HEADER_PROTOCOL, p);
|
||||
}
|
||||
requestBuilder.header(HEADER_VERSION, VALUE_VERSION);
|
||||
this.nonce = createNonce();
|
||||
requestBuilder.header(HEADER_KEY, this.nonce);
|
||||
this.request = requestBuilder.GET();
|
||||
HttpRequestImpl r = (HttpRequestImpl) this.request;
|
||||
r.isWebSocket(true);
|
||||
r.setSystemHeader(HEADER_UPGRADE, "websocket");
|
||||
r.setSystemHeader(HEADER_CONNECTION, "Upgrade");
|
||||
this.subprotocols = s;
|
||||
}
|
||||
|
||||
private URI createHttpUri(URI webSocketUri) {
|
||||
// FIXME: check permission for WebSocket URI and translate it into http/https permission
|
||||
logger.log(TRACE, "->createHttpUri(''{0}'')", webSocketUri);
|
||||
String httpScheme = webSocketUri.getScheme().equalsIgnoreCase("ws")
|
||||
? "http"
|
||||
: "https";
|
||||
try {
|
||||
URI uri = new URI(httpScheme,
|
||||
webSocketUri.getUserInfo(),
|
||||
webSocketUri.getHost(),
|
||||
webSocketUri.getPort(),
|
||||
webSocketUri.getPath(),
|
||||
webSocketUri.getQuery(),
|
||||
null);
|
||||
logger.log(TRACE, "<-createHttpUri: ''{0}''", uri);
|
||||
return uri;
|
||||
} catch (URISyntaxException e) {
|
||||
// Shouldn't happen: URI invariant
|
||||
throw new InternalError("Error translating WebSocket URI to HTTP URI", e);
|
||||
}
|
||||
}
|
||||
|
||||
CompletableFuture<Result> performAsync() {
|
||||
// The whole dancing with thenCompose instead of thenApply is because
|
||||
// WebSocketHandshakeException is a checked exception
|
||||
return request.responseAsync()
|
||||
.thenCompose(response -> {
|
||||
try {
|
||||
Result result = handleResponse(response);
|
||||
return CompletableFuture.completedFuture(result);
|
||||
} catch (WebSocketHandshakeException e) {
|
||||
return CompletableFuture.failedFuture(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Result handleResponse(HttpResponse response) throws WebSocketHandshakeException {
|
||||
// By this point all redirects, authentications, etc. (if any) must have
|
||||
// been done by the httpClient used by the WebSocket; so only 101 is
|
||||
// expected
|
||||
int statusCode = response.statusCode();
|
||||
if (statusCode != 101) {
|
||||
String m = webSocketSpecViolation("1.3.",
|
||||
"Unable to complete handshake; HTTP response status code "
|
||||
+ statusCode
|
||||
);
|
||||
throw new WebSocketHandshakeException(m, response);
|
||||
}
|
||||
HttpHeaders h = response.headers();
|
||||
checkHeader(h, response, HEADER_UPGRADE, v -> v.equalsIgnoreCase("websocket"));
|
||||
checkHeader(h, response, HEADER_CONNECTION, v -> v.equalsIgnoreCase("Upgrade"));
|
||||
checkVersion(response, h);
|
||||
checkAccept(response, h);
|
||||
checkExtensions(response, h);
|
||||
String subprotocol = checkAndReturnSubprotocol(response, h);
|
||||
RawChannel channel = ((HttpResponseImpl) response).rawChannel();
|
||||
return new Result(subprotocol, channel);
|
||||
}
|
||||
|
||||
private void checkExtensions(HttpResponse response, HttpHeaders headers)
|
||||
throws WebSocketHandshakeException {
|
||||
List<String> ext = headers.allValues(HEADER_EXTENSIONS);
|
||||
if (!ext.isEmpty()) {
|
||||
String m = webSocketSpecViolation("4.1.",
|
||||
"Server responded with extension(s) though none were requested "
|
||||
+ Arrays.toString(ext.toArray())
|
||||
);
|
||||
throw new WebSocketHandshakeException(m, response);
|
||||
}
|
||||
}
|
||||
|
||||
private String checkAndReturnSubprotocol(HttpResponse response, HttpHeaders headers)
|
||||
throws WebSocketHandshakeException {
|
||||
assert response.statusCode() == 101 : response.statusCode();
|
||||
List<String> sp = headers.allValues(HEADER_PROTOCOL);
|
||||
int size = sp.size();
|
||||
if (size == 0) {
|
||||
// In this case the subprotocol requested (if any) by the client
|
||||
// doesn't matter. If there is no such header in the response, then
|
||||
// the server doesn't want to use any subprotocol
|
||||
return null;
|
||||
} else if (size > 1) {
|
||||
// We don't know anything about toString implementation of this
|
||||
// list, so let's create an array
|
||||
String m = webSocketSpecViolation("4.1.",
|
||||
"Server responded with multiple subprotocols: "
|
||||
+ Arrays.toString(sp.toArray())
|
||||
);
|
||||
throw new WebSocketHandshakeException(m, response);
|
||||
} else {
|
||||
String selectedSubprotocol = sp.get(0);
|
||||
if (this.subprotocols.contains(selectedSubprotocol)) {
|
||||
return selectedSubprotocol;
|
||||
} else {
|
||||
String m = webSocketSpecViolation("4.1.",
|
||||
format("Server responded with a subprotocol " +
|
||||
"not among those requested: '%s'",
|
||||
selectedSubprotocol));
|
||||
throw new WebSocketHandshakeException(m, response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkAccept(HttpResponse response, HttpHeaders headers)
|
||||
throws WebSocketHandshakeException {
|
||||
assert response.statusCode() == 101 : response.statusCode();
|
||||
String x = nonce + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||
sha1.update(x.getBytes(StandardCharsets.ISO_8859_1));
|
||||
String expected = Base64.getEncoder().encodeToString(sha1.digest());
|
||||
checkHeader(headers, response, HEADER_ACCEPT, actual -> actual.trim().equals(expected));
|
||||
}
|
||||
|
||||
private void checkVersion(HttpResponse response, HttpHeaders headers)
|
||||
throws WebSocketHandshakeException {
|
||||
assert response.statusCode() == 101 : response.statusCode();
|
||||
List<String> versions = headers.allValues(HEADER_VERSION);
|
||||
if (versions.isEmpty()) { // That's normal and expected
|
||||
return;
|
||||
}
|
||||
String m = webSocketSpecViolation("4.4.",
|
||||
"Server responded with version(s) "
|
||||
+ Arrays.toString(versions.toArray()));
|
||||
throw new WebSocketHandshakeException(m, response);
|
||||
}
|
||||
|
||||
//
|
||||
// Checks whether there's only one value for the header with the given name
|
||||
// and the value satisfies the predicate.
|
||||
//
|
||||
private static void checkHeader(HttpHeaders headers,
|
||||
HttpResponse response,
|
||||
String headerName,
|
||||
Predicate<? super String> valuePredicate)
|
||||
throws WebSocketHandshakeException {
|
||||
assert response.statusCode() == 101 : response.statusCode();
|
||||
List<String> values = headers.allValues(headerName);
|
||||
if (values.isEmpty()) {
|
||||
String m = webSocketSpecViolation("4.1.",
|
||||
format("Server response field '%s' is missing", headerName)
|
||||
);
|
||||
throw new WebSocketHandshakeException(m, response);
|
||||
} else if (values.size() > 1) {
|
||||
String m = webSocketSpecViolation("4.1.",
|
||||
format("Server response field '%s' has multiple values", headerName)
|
||||
);
|
||||
throw new WebSocketHandshakeException(m, response);
|
||||
}
|
||||
if (!valuePredicate.test(values.get(0))) {
|
||||
String m = webSocketSpecViolation("4.1.",
|
||||
format("Server response field '%s' is incorrect", headerName)
|
||||
);
|
||||
throw new WebSocketHandshakeException(m, response);
|
||||
}
|
||||
}
|
||||
|
||||
private static String createNonce() {
|
||||
byte[] bytes = new byte[16];
|
||||
srandom.nextBytes(bytes);
|
||||
return Base64.getEncoder().encodeToString(bytes);
|
||||
}
|
||||
|
||||
static final class Result {
|
||||
|
||||
final String subprotocol;
|
||||
final RawChannel channel;
|
||||
|
||||
private Result(String subprotocol, RawChannel channel) {
|
||||
this.subprotocol = subprotocol;
|
||||
this.channel = channel;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,164 @@
|
||||
/*
|
||||
* 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 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 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 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 java.net.http;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
abstract class WSOutgoingMessage {
|
||||
|
||||
interface Visitor {
|
||||
void visit(Text message);
|
||||
void visit(StreamedText message);
|
||||
void visit(Binary message);
|
||||
void visit(Ping message);
|
||||
void visit(Pong message);
|
||||
void visit(Close message);
|
||||
}
|
||||
|
||||
abstract void accept(Visitor visitor);
|
||||
|
||||
private WSOutgoingMessage() { }
|
||||
|
||||
static final class Text extends WSOutgoingMessage {
|
||||
|
||||
public final boolean isLast;
|
||||
public final CharSequence characters;
|
||||
|
||||
Text(boolean isLast, CharSequence characters) {
|
||||
this.isLast = isLast;
|
||||
this.characters = characters;
|
||||
}
|
||||
|
||||
@Override
|
||||
void accept(Visitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return WSUtils.toStringSimple(this) + "[isLast=" + isLast
|
||||
+ ", characters=" + WSUtils.toString(characters) + "]";
|
||||
}
|
||||
}
|
||||
|
||||
static final class StreamedText extends WSOutgoingMessage {
|
||||
|
||||
public final Stream<? extends CharSequence> characters;
|
||||
|
||||
StreamedText(Stream<? extends CharSequence> characters) {
|
||||
this.characters = characters;
|
||||
}
|
||||
|
||||
@Override
|
||||
void accept(Visitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return WSUtils.toStringSimple(this) + "[characters=" + characters + "]";
|
||||
}
|
||||
}
|
||||
|
||||
static final class Binary extends WSOutgoingMessage {
|
||||
|
||||
public final boolean isLast;
|
||||
public final ByteBuffer bytes;
|
||||
|
||||
Binary(boolean isLast, ByteBuffer bytes) {
|
||||
this.isLast = isLast;
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
void accept(Visitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return WSUtils.toStringSimple(this) + "[isLast=" + isLast
|
||||
+ ", bytes=" + WSUtils.toString(bytes) + "]";
|
||||
}
|
||||
}
|
||||
|
||||
static final class Ping extends WSOutgoingMessage {
|
||||
|
||||
public final ByteBuffer bytes;
|
||||
|
||||
Ping(ByteBuffer bytes) {
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
void accept(Visitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return WSUtils.toStringSimple(this) + "[" + WSUtils.toString(bytes) + "]";
|
||||
}
|
||||
}
|
||||
|
||||
static final class Pong extends WSOutgoingMessage {
|
||||
|
||||
public final ByteBuffer bytes;
|
||||
|
||||
Pong(ByteBuffer bytes) {
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
void accept(Visitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return WSUtils.toStringSimple(this) + "[" + WSUtils.toString(bytes) + "]";
|
||||
}
|
||||
}
|
||||
|
||||
static final class Close extends WSOutgoingMessage {
|
||||
|
||||
public final ByteBuffer bytes;
|
||||
|
||||
Close(ByteBuffer bytes) {
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
void accept(Visitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return WSUtils.toStringSimple(this) + "[" + WSUtils.toString(bytes) + "]";
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
package java.net.http;
|
||||
|
||||
import java.net.http.WebSocket.CloseCode;
|
||||
|
||||
import static java.net.http.WebSocket.CloseCode.PROTOCOL_ERROR;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
//
|
||||
// Special kind of exception closed from the outside world.
|
||||
//
|
||||
// Used as a "marker exception" for protocol issues in the incoming data, so the
|
||||
// implementation could close the connection and specify an appropriate status
|
||||
// code.
|
||||
//
|
||||
// A separate 'section' argument makes it more uncomfortable to be lazy and to
|
||||
// leave a relevant spec reference empty :-) As a bonus all messages have the
|
||||
// same style.
|
||||
//
|
||||
final class WSProtocolException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final CloseCode closeCode;
|
||||
private final String section;
|
||||
|
||||
WSProtocolException(String section, String detail) {
|
||||
this(section, detail, PROTOCOL_ERROR);
|
||||
}
|
||||
|
||||
WSProtocolException(String section, String detail, Throwable cause) {
|
||||
this(section, detail, PROTOCOL_ERROR, cause);
|
||||
}
|
||||
|
||||
private WSProtocolException(String section, String detail, CloseCode code) {
|
||||
super(formatMessage(section, detail));
|
||||
this.closeCode = requireNonNull(code);
|
||||
this.section = section;
|
||||
}
|
||||
|
||||
WSProtocolException(String section, String detail, CloseCode code,
|
||||
Throwable cause) {
|
||||
super(formatMessage(section, detail), cause);
|
||||
this.closeCode = requireNonNull(code);
|
||||
this.section = section;
|
||||
}
|
||||
|
||||
private static String formatMessage(String section, String detail) {
|
||||
if (requireNonNull(section).isEmpty()) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if (requireNonNull(detail).isEmpty()) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
return WSUtils.webSocketSpecViolation(section, detail);
|
||||
}
|
||||
|
||||
CloseCode getCloseCode() {
|
||||
return closeCode;
|
||||
}
|
||||
|
||||
public String getSection() {
|
||||
return section;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + "[" + closeCode + "]";
|
||||
}
|
||||
}
|
@ -0,0 +1,275 @@
|
||||
/*
|
||||
* 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 java.net.http;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.ProtocolException;
|
||||
import java.net.http.WebSocket.Listener;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static java.lang.System.Logger.Level.ERROR;
|
||||
import static java.net.http.WSUtils.EMPTY_BYTE_BUFFER;
|
||||
import static java.net.http.WSUtils.logger;
|
||||
|
||||
/*
|
||||
* Receives incoming data from the channel and converts it into a sequence of
|
||||
* messages, which are then passed to the listener.
|
||||
*/
|
||||
final class WSReceiver {
|
||||
|
||||
private final Listener listener;
|
||||
private final WebSocket webSocket;
|
||||
private final Supplier<WSShared<ByteBuffer>> buffersSupplier =
|
||||
new WSSharedPool<>(() -> ByteBuffer.allocateDirect(32768), 2);
|
||||
private final RawChannel channel;
|
||||
private final RawChannel.NonBlockingEvent channelEvent;
|
||||
private final WSSignalHandler handler;
|
||||
private final AtomicLong demand = new AtomicLong();
|
||||
private final AtomicBoolean readable = new AtomicBoolean();
|
||||
private boolean started;
|
||||
private volatile boolean closed;
|
||||
private final WSFrame.Reader reader = new WSFrame.Reader();
|
||||
private final WSFrameConsumer frameConsumer;
|
||||
private WSShared<ByteBuffer> buf = WSShared.wrap(EMPTY_BYTE_BUFFER);
|
||||
private WSShared<ByteBuffer> data; // TODO: initialize with leftovers from the RawChannel
|
||||
|
||||
WSReceiver(Listener listener, WebSocket webSocket, Executor executor,
|
||||
RawChannel channel) {
|
||||
this.listener = listener;
|
||||
this.webSocket = webSocket;
|
||||
this.channel = channel;
|
||||
handler = new WSSignalHandler(executor, this::react);
|
||||
channelEvent = createChannelEvent();
|
||||
this.frameConsumer = new WSFrameConsumer(new MessageConsumer());
|
||||
}
|
||||
|
||||
private void react() {
|
||||
synchronized (this) {
|
||||
while (demand.get() > 0 && !closed) {
|
||||
try {
|
||||
if (data == null) {
|
||||
if (!getData()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
reader.readFrame(data, frameConsumer);
|
||||
if (!data.hasRemaining()) {
|
||||
data.dispose();
|
||||
data = null;
|
||||
}
|
||||
// In case of exception we don't need to clean any state,
|
||||
// since it's the terminal condition anyway. Nothing will be
|
||||
// retried.
|
||||
} catch (WSProtocolException e) {
|
||||
// Translate into ProtocolException
|
||||
closeExceptionally(new ProtocolException().initCause(e));
|
||||
} catch (Exception e) {
|
||||
closeExceptionally(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
long request(long n) {
|
||||
long newDemand = demand.accumulateAndGet(n, (p, i) -> p + i < 0 ? Long.MAX_VALUE : p + i);
|
||||
handler.signal();
|
||||
assert newDemand >= 0 : newDemand;
|
||||
return newDemand;
|
||||
}
|
||||
|
||||
private boolean getData() throws IOException {
|
||||
if (!readable.get()) {
|
||||
return false;
|
||||
}
|
||||
if (!buf.hasRemaining()) {
|
||||
buf.dispose();
|
||||
buf = buffersSupplier.get();
|
||||
assert buf.hasRemaining() : buf;
|
||||
}
|
||||
int oldPosition = buf.position();
|
||||
int oldLimit = buf.limit();
|
||||
int numRead = channel.read(buf.buffer());
|
||||
if (numRead > 0) {
|
||||
data = buf.share(oldPosition, oldPosition + numRead);
|
||||
buf.select(buf.limit(), oldLimit); // Move window to the free region
|
||||
return true;
|
||||
} else if (numRead == 0) {
|
||||
readable.set(false);
|
||||
channel.registerEvent(channelEvent);
|
||||
return false;
|
||||
} else {
|
||||
assert numRead < 0 : numRead;
|
||||
throw new WSProtocolException
|
||||
("7.2.1.", "Stream ended before a Close frame has been received");
|
||||
}
|
||||
}
|
||||
|
||||
void start() {
|
||||
synchronized (this) {
|
||||
if (started) {
|
||||
throw new IllegalStateException("Already started");
|
||||
}
|
||||
started = true;
|
||||
try {
|
||||
channel.registerEvent(channelEvent);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
try {
|
||||
listener.onOpen(webSocket);
|
||||
} catch (Exception e) {
|
||||
closeExceptionally(new RuntimeException("onOpen threw an exception", e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void close() { // TODO: move to WS.java
|
||||
closed = true;
|
||||
}
|
||||
|
||||
private void closeExceptionally(Throwable error) { // TODO: move to WS.java
|
||||
close();
|
||||
try {
|
||||
listener.onError(webSocket, error);
|
||||
} catch (Exception e) {
|
||||
logger.log(ERROR, "onError threw an exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
private final class MessageConsumer implements WSMessageConsumer {
|
||||
|
||||
@Override
|
||||
public void onText(WebSocket.MessagePart part, WSDisposableText data) {
|
||||
decrementDemand();
|
||||
CompletionStage<?> cs;
|
||||
try {
|
||||
cs = listener.onText(webSocket, data, part);
|
||||
} catch (Exception e) {
|
||||
closeExceptionally(new RuntimeException("onText threw an exception", e));
|
||||
return;
|
||||
}
|
||||
follow(cs, data, "onText");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBinary(WebSocket.MessagePart part, WSShared<ByteBuffer> data) {
|
||||
decrementDemand();
|
||||
CompletionStage<?> cs;
|
||||
try {
|
||||
cs = listener.onBinary(webSocket, data.buffer(), part);
|
||||
} catch (Exception e) {
|
||||
closeExceptionally(new RuntimeException("onBinary threw an exception", e));
|
||||
return;
|
||||
}
|
||||
follow(cs, data, "onBinary");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPing(WSShared<ByteBuffer> data) {
|
||||
decrementDemand();
|
||||
CompletionStage<?> cs;
|
||||
try {
|
||||
cs = listener.onPing(webSocket, data.buffer());
|
||||
} catch (Exception e) {
|
||||
closeExceptionally(new RuntimeException("onPing threw an exception", e));
|
||||
return;
|
||||
}
|
||||
follow(cs, data, "onPing");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPong(WSShared<ByteBuffer> data) {
|
||||
decrementDemand();
|
||||
CompletionStage<?> cs;
|
||||
try {
|
||||
cs = listener.onPong(webSocket, data.buffer());
|
||||
} catch (Exception e) {
|
||||
closeExceptionally(new RuntimeException("onPong threw an exception", e));
|
||||
return;
|
||||
}
|
||||
follow(cs, data, "onPong");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(WebSocket.CloseCode code, CharSequence reason) {
|
||||
decrementDemand();
|
||||
try {
|
||||
close();
|
||||
listener.onClose(webSocket, Optional.ofNullable(code), reason.toString());
|
||||
} catch (Exception e) {
|
||||
logger.log(ERROR, "onClose threw an exception", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void follow(CompletionStage<?> cs, WSDisposable d, String source) {
|
||||
if (cs == null) {
|
||||
d.dispose();
|
||||
} else {
|
||||
cs.whenComplete((whatever, error) -> {
|
||||
if (error != null) {
|
||||
String m = "CompletionStage returned by " + source + " completed exceptionally";
|
||||
closeExceptionally(new RuntimeException(m, error));
|
||||
}
|
||||
d.dispose();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void decrementDemand() {
|
||||
long newDemand = demand.decrementAndGet();
|
||||
assert newDemand >= 0 : newDemand;
|
||||
}
|
||||
|
||||
private RawChannel.NonBlockingEvent createChannelEvent() {
|
||||
return new RawChannel.NonBlockingEvent() {
|
||||
|
||||
@Override
|
||||
public int interestOps() {
|
||||
return SelectionKey.OP_READ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle() {
|
||||
boolean wasNotReadable = readable.compareAndSet(false, true);
|
||||
assert wasNotReadable;
|
||||
handler.signal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Read readiness event [" + channel + "]";
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,202 @@
|
||||
/*
|
||||
* 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 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 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 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 java.net.http;
|
||||
|
||||
import java.nio.Buffer;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
//
|
||||
// +-----------+---------------+------------ ~ ------+
|
||||
// | shared#1 | shared#2 | non-shared |
|
||||
// +-----------+---------------+------------ ~ ------+
|
||||
// | |
|
||||
// |<------------------ shared0 ---------- ~ ----->|
|
||||
//
|
||||
//
|
||||
// Objects of the type are not thread-safe. It's the responsibility of the
|
||||
// client to access shared buffers safely between threads.
|
||||
//
|
||||
// It would be perfect if we could extend java.nio.Buffer, but it's not an
|
||||
// option since Buffer and all its descendants have package-private
|
||||
// constructors.
|
||||
//
|
||||
abstract class WSShared<T extends Buffer> implements WSDisposable {
|
||||
|
||||
protected final AtomicBoolean disposed = new AtomicBoolean();
|
||||
protected final T buffer;
|
||||
|
||||
protected WSShared(T buffer) {
|
||||
this.buffer = Objects.requireNonNull(buffer);
|
||||
}
|
||||
|
||||
static <T extends Buffer> WSShared<T> wrap(T buffer) {
|
||||
return new WSShared<>(buffer) {
|
||||
@Override
|
||||
WSShared<T> share(int pos, int limit) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: should be a terminal operation as after it returns the buffer might
|
||||
// have escaped (we can't protect it any more)
|
||||
public T buffer() {
|
||||
checkDisposed();
|
||||
return buffer;
|
||||
}
|
||||
|
||||
abstract WSShared<T> share(final int pos, final int limit);
|
||||
|
||||
WSShared<T> select(final int pos, final int limit) {
|
||||
checkRegion(pos, limit, buffer());
|
||||
select(pos, limit, buffer());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
if (!disposed.compareAndSet(false, true)) {
|
||||
throw new IllegalStateException("Has been disposed previously");
|
||||
}
|
||||
}
|
||||
|
||||
int limit() {
|
||||
return buffer().limit();
|
||||
}
|
||||
|
||||
WSShared<T> limit(int newLimit) {
|
||||
buffer().limit(newLimit);
|
||||
return this;
|
||||
}
|
||||
|
||||
int position() {
|
||||
return buffer().position();
|
||||
}
|
||||
|
||||
WSShared<T> position(int newPosition) {
|
||||
buffer().position(newPosition);
|
||||
return this;
|
||||
}
|
||||
|
||||
int remaining() {
|
||||
return buffer().remaining();
|
||||
}
|
||||
|
||||
boolean hasRemaining() {
|
||||
return buffer().hasRemaining();
|
||||
}
|
||||
|
||||
WSShared<T> flip() {
|
||||
buffer().flip();
|
||||
return this;
|
||||
}
|
||||
|
||||
WSShared<T> rewind() {
|
||||
buffer().rewind();
|
||||
return this;
|
||||
}
|
||||
|
||||
WSShared<T> put(WSShared<? extends T> src) {
|
||||
put(this.buffer(), src.buffer());
|
||||
return this;
|
||||
}
|
||||
|
||||
static void checkRegion(int position, int limit, Buffer buffer) {
|
||||
if (position < 0 || position > buffer.capacity()) {
|
||||
throw new IllegalArgumentException("position: " + position);
|
||||
}
|
||||
if (limit < 0 || limit > buffer.capacity()) {
|
||||
throw new IllegalArgumentException("limit: " + limit);
|
||||
}
|
||||
if (limit < position) {
|
||||
throw new IllegalArgumentException
|
||||
("limit < position: limit=" + limit + ", position=" + position);
|
||||
}
|
||||
}
|
||||
|
||||
void select(int newPos, int newLim, Buffer buffer) {
|
||||
int oldPos = buffer.position();
|
||||
int oldLim = buffer.limit();
|
||||
assert 0 <= oldPos && oldPos <= oldLim && oldLim <= buffer.capacity();
|
||||
if (oldLim <= newPos) {
|
||||
buffer().limit(newLim).position(newPos);
|
||||
} else {
|
||||
buffer.position(newPos).limit(newLim);
|
||||
}
|
||||
}
|
||||
|
||||
// The same as dst.put(src)
|
||||
static <T extends Buffer> T put(T dst, T src) {
|
||||
if (dst instanceof ByteBuffer) {
|
||||
((ByteBuffer) dst).put((ByteBuffer) src);
|
||||
} else if (dst instanceof CharBuffer) {
|
||||
((CharBuffer) dst).put((CharBuffer) src);
|
||||
} else {
|
||||
// We don't work with buffers of other types
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
// TODO: Remove when JDK-8150785 has been done
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T extends Buffer> T slice(T buffer) {
|
||||
if (buffer instanceof ByteBuffer) {
|
||||
return (T) ((ByteBuffer) buffer).slice();
|
||||
} else if (buffer instanceof CharBuffer) {
|
||||
return (T) ((CharBuffer) buffer).slice();
|
||||
} else {
|
||||
// We don't work with buffers of other types
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Remove when JDK-8150785 has been done
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T extends Buffer> T duplicate(T buffer) {
|
||||
if (buffer instanceof ByteBuffer) {
|
||||
return (T) ((ByteBuffer) buffer).duplicate();
|
||||
} else if (buffer instanceof CharBuffer) {
|
||||
return (T) ((CharBuffer) buffer).duplicate();
|
||||
} else {
|
||||
// We don't work with buffers of other types
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + "[" + WSUtils.toString(buffer()) + "]";
|
||||
}
|
||||
|
||||
private void checkDisposed() {
|
||||
if (disposed.get()) {
|
||||
throw new IllegalStateException("Has been disposed previously");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* 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 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 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 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 java.net.http;
|
||||
|
||||
import java.nio.Buffer;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static java.lang.System.Logger.Level.TRACE;
|
||||
import static java.net.http.WSShared.duplicate;
|
||||
import static java.net.http.WSUtils.logger;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
final class WSSharedPool<T extends Buffer> implements Supplier<WSShared<T>> {
|
||||
|
||||
private final Supplier<T> factory;
|
||||
private final BlockingQueue<T> queue;
|
||||
|
||||
WSSharedPool(Supplier<T> factory, int maxPoolSize) {
|
||||
this.factory = requireNonNull(factory);
|
||||
this.queue = new LinkedBlockingQueue<>(maxPoolSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pooled get() {
|
||||
T b = queue.poll();
|
||||
if (b == null) {
|
||||
logger.log(TRACE, "Pool {0} contains no free buffers", this);
|
||||
b = requireNonNull(factory.get());
|
||||
}
|
||||
Pooled buf = new Pooled(new AtomicInteger(1), b, duplicate(b));
|
||||
logger.log(TRACE, "Pool {0} created new buffer {1}", this, buf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
private void put(Pooled b) {
|
||||
assert b.disposed.get() && b.refCount.get() == 0
|
||||
: WSUtils.dump(b.disposed, b.refCount, b);
|
||||
b.shared.clear();
|
||||
boolean accepted = queue.offer(b.getShared());
|
||||
if (logger.isLoggable(TRACE)) {
|
||||
if (accepted) {
|
||||
logger.log(TRACE, "Pool {0} accepted {1}", this, b);
|
||||
} else {
|
||||
logger.log(TRACE, "Pool {0} discarded {1}", this, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + "[queue.size=" + queue.size() + "]";
|
||||
}
|
||||
|
||||
private final class Pooled extends WSShared<T> {
|
||||
|
||||
private final AtomicInteger refCount;
|
||||
private final T shared;
|
||||
|
||||
private Pooled(AtomicInteger refCount, T shared, T region) {
|
||||
super(region);
|
||||
this.refCount = refCount;
|
||||
this.shared = shared;
|
||||
}
|
||||
|
||||
private T getShared() {
|
||||
return shared;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Pooled share(final int pos, final int limit) {
|
||||
synchronized (this) {
|
||||
T buffer = buffer();
|
||||
checkRegion(pos, limit, buffer);
|
||||
final int oldPos = buffer.position();
|
||||
final int oldLimit = buffer.limit();
|
||||
select(pos, limit, buffer);
|
||||
T slice = WSShared.slice(buffer);
|
||||
select(oldPos, oldLimit, buffer);
|
||||
referenceAndGetCount();
|
||||
Pooled buf = new Pooled(refCount, shared, slice);
|
||||
logger.log(TRACE, "Shared {0} from {1}", buf, this);
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
logger.log(TRACE, "Disposed {0}", this);
|
||||
super.dispose();
|
||||
if (dereferenceAndGetCount() == 0) {
|
||||
WSSharedPool.this.put(this);
|
||||
}
|
||||
}
|
||||
|
||||
private int referenceAndGetCount() {
|
||||
return refCount.updateAndGet(n -> {
|
||||
if (n != Integer.MAX_VALUE) {
|
||||
return n + 1;
|
||||
} else {
|
||||
throw new IllegalArgumentException
|
||||
("Too many references: " + this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private int dereferenceAndGetCount() {
|
||||
return refCount.updateAndGet(n -> {
|
||||
if (n > 0) {
|
||||
return n - 1;
|
||||
} else {
|
||||
throw new InternalError();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return WSUtils.toStringSimple(this) + "[" + WSUtils.toString(buffer)
|
||||
+ "[refCount=" + refCount + ", disposed=" + disposed + "]]";
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* 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 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 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 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 java.net.http;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
//
|
||||
// The problem:
|
||||
// ------------
|
||||
// 1. For every invocation of 'signal()' there must be at least
|
||||
// 1 invocation of 'handler.run()' that goes after
|
||||
// 2. There must be no more than 1 thread running the 'handler.run()'
|
||||
// at any given time
|
||||
//
|
||||
// For example, imagine each signal increments (+1) some number. Then the
|
||||
// handler responds (eventually) the way that makes the number 0.
|
||||
//
|
||||
// For each signal there's a response. Several signals may be handled by a
|
||||
// single response.
|
||||
//
|
||||
final class WSSignalHandler {
|
||||
|
||||
// In this state the task is neither submitted nor running.
|
||||
// No one is handling signals. If a new signal has been received, the task
|
||||
// has to be submitted to the executor in order to handle this signal.
|
||||
private static final int DONE = 0;
|
||||
|
||||
// In this state the task is running.
|
||||
// * If the signaller has found the task in this state it will try to change
|
||||
// the state to RERUN in order to make the already running task to handle
|
||||
// the new signal before exiting.
|
||||
// * If the task has found itself in this state it will exit.
|
||||
private static final int RUNNING = 1;
|
||||
|
||||
// A signal to the task, that it must rerun on the spot (without being
|
||||
// resubmitted to the executor).
|
||||
// If the task has found itself in this state it resets the state to
|
||||
// RUNNING and repeats the pass.
|
||||
private static final int RERUN = 2;
|
||||
|
||||
private final AtomicInteger state = new AtomicInteger(DONE);
|
||||
|
||||
private final Executor executor;
|
||||
private final Runnable task;
|
||||
|
||||
WSSignalHandler(Executor executor, Runnable handler) {
|
||||
this.executor = requireNonNull(executor);
|
||||
requireNonNull(handler);
|
||||
|
||||
task = () -> {
|
||||
while (!Thread.currentThread().isInterrupted()) {
|
||||
|
||||
try {
|
||||
handler.run();
|
||||
} catch (Exception e) {
|
||||
// Sorry, the task won't be automatically retried;
|
||||
// hope next signals (if any) will kick off the handling
|
||||
state.set(DONE);
|
||||
throw e;
|
||||
}
|
||||
|
||||
int prev = state.getAndUpdate(s -> {
|
||||
if (s == RUNNING) {
|
||||
return DONE;
|
||||
} else {
|
||||
return RUNNING;
|
||||
}
|
||||
});
|
||||
|
||||
// Can't be DONE, since only the task itself may transit state
|
||||
// into DONE (with one exception: RejectedExecution in signal();
|
||||
// but in that case we couldn't be here at all)
|
||||
assert prev == RUNNING || prev == RERUN;
|
||||
|
||||
if (prev == RUNNING) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Invoked by outer code to signal
|
||||
void signal() {
|
||||
|
||||
int prev = state.getAndUpdate(s -> {
|
||||
switch (s) {
|
||||
case RUNNING:
|
||||
return RERUN;
|
||||
case DONE:
|
||||
return RUNNING;
|
||||
case RERUN:
|
||||
return RERUN;
|
||||
default:
|
||||
throw new InternalError(String.valueOf(s));
|
||||
}
|
||||
});
|
||||
|
||||
if (prev != DONE) {
|
||||
// Nothing to do! piggybacking on previous signal
|
||||
return;
|
||||
}
|
||||
try {
|
||||
executor.execute(task);
|
||||
} catch (RejectedExecutionException e) {
|
||||
// Sorry some signal() invocations may have been accepted, but won't
|
||||
// be done, since the 'task' couldn't be submitted
|
||||
state.set(DONE);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,176 @@
|
||||
/*
|
||||
* 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 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 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 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 java.net.http;
|
||||
|
||||
import java.net.http.WSOutgoingMessage.Binary;
|
||||
import java.net.http.WSOutgoingMessage.Close;
|
||||
import java.net.http.WSOutgoingMessage.Ping;
|
||||
import java.net.http.WSOutgoingMessage.Pong;
|
||||
import java.net.http.WSOutgoingMessage.StreamedText;
|
||||
import java.net.http.WSOutgoingMessage.Text;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.CharacterCodingException;
|
||||
import java.nio.charset.CoderResult;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static java.net.http.Pair.pair;
|
||||
|
||||
/*
|
||||
* Prepares outgoing messages for transmission. Verifies the WebSocket state,
|
||||
* places the message on the outbound queue, and notifies the signal handler.
|
||||
*/
|
||||
final class WSTransmitter {
|
||||
|
||||
private final BlockingQueue<Pair<WSOutgoingMessage, CompletableFuture<Void>>>
|
||||
backlog = new LinkedBlockingQueue<>();
|
||||
private final WSMessageSender sender;
|
||||
private final WSSignalHandler handler;
|
||||
private boolean previousMessageSent = true;
|
||||
private boolean canSendBinary = true;
|
||||
private boolean canSendText = true;
|
||||
|
||||
WSTransmitter(Executor executor, RawChannel channel, Consumer<Throwable> errorHandler) {
|
||||
this.handler = new WSSignalHandler(executor, this::handleSignal);
|
||||
Consumer<Throwable> sendCompletion = (error) -> {
|
||||
synchronized (this) {
|
||||
if (error == null) {
|
||||
previousMessageSent = true;
|
||||
handler.signal();
|
||||
} else {
|
||||
errorHandler.accept(error);
|
||||
backlog.forEach(p -> p.second.completeExceptionally(error));
|
||||
backlog.clear();
|
||||
}
|
||||
}
|
||||
};
|
||||
this.sender = new WSMessageSender(channel, sendCompletion);
|
||||
}
|
||||
|
||||
CompletableFuture<Void> sendText(CharSequence message, boolean isLast) {
|
||||
checkAndUpdateText(isLast);
|
||||
return acceptMessage(new Text(isLast, message));
|
||||
}
|
||||
|
||||
CompletableFuture<Void> sendText(Stream<? extends CharSequence> message) {
|
||||
checkAndUpdateText(true);
|
||||
return acceptMessage(new StreamedText(message));
|
||||
}
|
||||
|
||||
CompletableFuture<Void> sendBinary(ByteBuffer message, boolean isLast) {
|
||||
checkAndUpdateBinary(isLast);
|
||||
return acceptMessage(new Binary(isLast, message));
|
||||
}
|
||||
|
||||
CompletableFuture<Void> sendPing(ByteBuffer message) {
|
||||
checkSize(message.remaining(), 125);
|
||||
return acceptMessage(new Ping(message));
|
||||
}
|
||||
|
||||
CompletableFuture<Void> sendPong(ByteBuffer message) {
|
||||
checkSize(message.remaining(), 125);
|
||||
return acceptMessage(new Pong(message));
|
||||
}
|
||||
|
||||
CompletableFuture<Void> sendClose(WebSocket.CloseCode code, CharSequence reason) {
|
||||
return acceptMessage(createCloseMessage(code, reason));
|
||||
}
|
||||
|
||||
CompletableFuture<Void> sendClose() {
|
||||
return acceptMessage(new Close(ByteBuffer.allocate(0)));
|
||||
}
|
||||
|
||||
private CompletableFuture<Void> acceptMessage(WSOutgoingMessage m) {
|
||||
CompletableFuture<Void> cf = new CompletableFuture<>();
|
||||
synchronized (this) {
|
||||
backlog.offer(pair(m, cf));
|
||||
}
|
||||
handler.signal();
|
||||
return cf;
|
||||
}
|
||||
|
||||
/* Callback for pulling messages from the queue, and initiating the send. */
|
||||
private void handleSignal() {
|
||||
synchronized (this) {
|
||||
while (!backlog.isEmpty() && previousMessageSent) {
|
||||
previousMessageSent = false;
|
||||
Pair<WSOutgoingMessage, CompletableFuture<Void>> p = backlog.peek();
|
||||
boolean sent = sender.trySendFully(p.first);
|
||||
if (sent) {
|
||||
backlog.remove();
|
||||
p.second.complete(null);
|
||||
previousMessageSent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Close createCloseMessage(WebSocket.CloseCode code, CharSequence reason) {
|
||||
// TODO: move to construction of CloseDetail (JDK-8155621)
|
||||
ByteBuffer b = ByteBuffer.allocateDirect(125).putChar((char) code.getCode());
|
||||
CoderResult result = StandardCharsets.UTF_8.newEncoder()
|
||||
.encode(CharBuffer.wrap(reason), b, true);
|
||||
if (result.isError()) {
|
||||
try {
|
||||
result.throwException();
|
||||
} catch (CharacterCodingException e) {
|
||||
throw new IllegalArgumentException("Reason is a malformed UTF-16 sequence", e);
|
||||
}
|
||||
} else if (result.isOverflow()) {
|
||||
throw new IllegalArgumentException("Reason is too long");
|
||||
}
|
||||
return new Close(b.flip());
|
||||
}
|
||||
|
||||
private void checkSize(int size, int maxSize) {
|
||||
if (size > maxSize) {
|
||||
throw new IllegalArgumentException(
|
||||
format("The message is too long: %s;" +
|
||||
" expected not longer than %s", size, maxSize)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkAndUpdateText(boolean isLast) {
|
||||
if (!canSendText) {
|
||||
throw new IllegalStateException("Unexpected text message");
|
||||
}
|
||||
canSendBinary = isLast;
|
||||
}
|
||||
|
||||
private void checkAndUpdateBinary(boolean isLast) {
|
||||
if (!canSendBinary) {
|
||||
throw new IllegalStateException("Unexpected binary message");
|
||||
}
|
||||
canSendText = isLast;
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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 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 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 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 java.net.http;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.Buffer;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
|
||||
final class WSUtils {
|
||||
|
||||
private WSUtils() { }
|
||||
|
||||
static final System.Logger logger = System.getLogger("java.net.http.WebSocket");
|
||||
static final ByteBuffer EMPTY_BYTE_BUFFER = ByteBuffer.allocate(0);
|
||||
|
||||
//
|
||||
// Helps to trim long names (packages, nested/inner types) in logs/toString
|
||||
//
|
||||
static String toStringSimple(Object o) {
|
||||
return o.getClass().getSimpleName() + "@" +
|
||||
Integer.toHexString(System.identityHashCode(o));
|
||||
}
|
||||
|
||||
//
|
||||
// 1. It adds a number of remaining bytes;
|
||||
// 2. Standard Buffer-type toString for CharBuffer (since it adheres to the
|
||||
// contract of java.lang.CharSequence.toString() which is both not too
|
||||
// useful and not too private)
|
||||
//
|
||||
static String toString(Buffer b) {
|
||||
return toStringSimple(b)
|
||||
+ "[pos=" + b.position()
|
||||
+ " lim=" + b.limit()
|
||||
+ " cap=" + b.capacity()
|
||||
+ " rem=" + b.remaining() + "]";
|
||||
}
|
||||
|
||||
static String toString(CharSequence s) {
|
||||
return s == null
|
||||
? "null"
|
||||
: toStringSimple(s) + "[len=" + s.length() + "]";
|
||||
}
|
||||
|
||||
static String dump(Object... objects) {
|
||||
return Arrays.toString(objects);
|
||||
}
|
||||
|
||||
static String webSocketSpecViolation(String section, String detail) {
|
||||
return "RFC 6455 " + section + " " + detail;
|
||||
}
|
||||
}
|
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* 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 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 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 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 java.net.http;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/*
|
||||
* Writes ByteBuffer[] to the channel in a non-blocking, asynchronous fashion.
|
||||
*
|
||||
* A client attempts to write data by calling
|
||||
*
|
||||
* boolean tryWriteFully(ByteBuffer[] buffers)
|
||||
*
|
||||
* If the attempt was successful and all the data has been written, then the
|
||||
* method returns `true`.
|
||||
*
|
||||
* If the data has been written partially, then the method returns `false`, and
|
||||
* the writer (this object) attempts to complete the write asynchronously by
|
||||
* calling, possibly more than once
|
||||
*
|
||||
* boolean tryCompleteWrite()
|
||||
*
|
||||
* in its own threads.
|
||||
*
|
||||
* When the write has been completed asynchronously, the callback is signalled
|
||||
* with `null`.
|
||||
*
|
||||
* If an error occurs in any of these stages it will NOT be thrown from the
|
||||
* method. Instead `false` will be returned and the exception will be signalled
|
||||
* to the callback. This is done in order to handle all exceptions in a single
|
||||
* place.
|
||||
*/
|
||||
final class WSWriter {
|
||||
|
||||
private final RawChannel channel;
|
||||
private final RawChannel.NonBlockingEvent writeReadinessHandler;
|
||||
private final Consumer<Throwable> completionCallback;
|
||||
private ByteBuffer[] buffers;
|
||||
private int offset;
|
||||
|
||||
WSWriter(RawChannel channel, Consumer<Throwable> completionCallback) {
|
||||
this.channel = channel;
|
||||
this.completionCallback = completionCallback;
|
||||
this.writeReadinessHandler = createHandler();
|
||||
}
|
||||
|
||||
boolean tryWriteFully(ByteBuffer[] buffers) {
|
||||
synchronized (this) {
|
||||
this.buffers = requireNonNull(buffers);
|
||||
this.offset = 0;
|
||||
}
|
||||
return tryCompleteWrite();
|
||||
}
|
||||
|
||||
private final boolean tryCompleteWrite() {
|
||||
try {
|
||||
return writeNow();
|
||||
} catch (IOException e) {
|
||||
completionCallback.accept(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean writeNow() throws IOException {
|
||||
synchronized (this) {
|
||||
for (; offset != -1; offset = nextUnwrittenIndex(buffers, offset)) {
|
||||
long bytesWritten = channel.write(buffers, offset, buffers.length - offset);
|
||||
if (bytesWritten == 0) {
|
||||
channel.registerEvent(writeReadinessHandler);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static int nextUnwrittenIndex(ByteBuffer[] buffers, int offset) {
|
||||
for (int i = offset; i < buffers.length; i++) {
|
||||
if (buffers[i].hasRemaining()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private RawChannel.NonBlockingEvent createHandler() {
|
||||
return new RawChannel.NonBlockingEvent() {
|
||||
|
||||
@Override
|
||||
public int interestOps() {
|
||||
return SelectionKey.OP_WRITE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle() {
|
||||
if (tryCompleteWrite()) {
|
||||
completionCallback.accept(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Write readiness event [" + channel + "]";
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
1288
jdk/src/java.httpclient/share/classes/java/net/http/WebSocket.java
Normal file
1288
jdk/src/java.httpclient/share/classes/java/net/http/WebSocket.java
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user