From 5f8e39900cd0c35d623b41bc0f35d82019809562 Mon Sep 17 00:00:00 2001 From: Erik Joelsson Date: Tue, 24 Nov 2015 15:40:03 +0100 Subject: [PATCH 01/62] 8143895: Fix LDFLAGS issues after JDK-8056925 Reviewed-by: ihse --- jdk/make/launcher/Launcher-jdk.accessibility.gmk | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/jdk/make/launcher/Launcher-jdk.accessibility.gmk b/jdk/make/launcher/Launcher-jdk.accessibility.gmk index 0fa486377f1..eba521c469a 100644 --- a/jdk/make/launcher/Launcher-jdk.accessibility.gmk +++ b/jdk/make/launcher/Launcher-jdk.accessibility.gmk @@ -73,8 +73,9 @@ ifeq ($(OPENJDK_TARGET_OS), windows) $$(eval $$(call SetupNativeCompilation, BUILD_JACCESSINSPECTOR$1, \ SRC := $(TOPDIR)/jaccessinspector $(TOPDIR)/common \ $(TOPDIR)/toolscommon $(TOPDIR)/include/bridge, \ - CFLAGS := $$(CFLAGS_JDKEXE) $(TOOLS_CFLAGS) -DACCESSBRIDGE_ARCH_$2 /EHsc, \ - LDFLAGS := $$(LDFLAGS_JDKEXE) /STACK:655360 Advapi32.lib User32.lib, \ + CFLAGS := $$(CFLAGS_JDKEXE) $(TOOLS_CFLAGS) -DACCESSBRIDGE_ARCH_$2 -EHsc, \ + LDFLAGS := $$(LDFLAGS_JDKEXE) -stack:655360, \ + LIBS := advapi32.lib user32.lib, \ OBJECT_DIR := $(SUPPORT_OUTPUTDIR)/native/jdk.accessibility/jaccessinspector$1, \ OUTPUT_DIR := $(SUPPORT_OUTPUTDIR)/modules_cmds/jdk.accessibility, \ PROGRAM := jaccessinspector$1, \ @@ -100,8 +101,9 @@ ifeq ($(OPENJDK_TARGET_OS), windows) $$(eval $$(call SetupNativeCompilation,BUILD_JACCESSWALKER$1, \ SRC := $(TOPDIR)/jaccesswalker $(TOPDIR)/common \ $(TOPDIR)/toolscommon $(TOPDIR)/include/bridge, \ - CFLAGS :== $$(CFLAGS_JDKEXE) $(TOOLS_CFLAGS) -DACCESSBRIDGE_ARCH_$2 /EHsc, \ - LDFLAGS := $$(LDFLAGS_JDKEXE) /STACK:655360 Advapi32.lib Comctl32.lib Gdi32.lib User32.lib, \ + CFLAGS := $$(CFLAGS_JDKEXE) $(TOOLS_CFLAGS) -DACCESSBRIDGE_ARCH_$2 -EHsc, \ + LDFLAGS := $$(LDFLAGS_JDKEXE) -stack:655360, \ + LIBS := advapi32.lib comctl32.lib gdi32.lib user32.lib, \ OBJECT_DIR := $(SUPPORT_OUTPUTDIR)/native/jdk.accessibility/jaccesswalker$1, \ OUTPUT_DIR := $(SUPPORT_OUTPUTDIR)/modules_cmds/jdk.accessibility, \ PROGRAM := jaccesswalker$1, \ From 3c1af171595aaf96cc34750bd5e54433d55ffd9e Mon Sep 17 00:00:00 2001 From: Steven Loomis Date: Tue, 24 Nov 2015 13:36:12 -0800 Subject: [PATCH 02/62] 8068619: remove unused internal function in layout No functional change. Removes unused code. Makes JDK's layout engine have the same signature as ICU HarfBuzz's wrapper. Reviewed: http://mail.openjdk.java.net/pipermail/2d-dev/2015-March/005156.html Reviewed-by: prr --- .../libfontmanager/FontInstanceAdapter.cpp | 6 ----- .../libfontmanager/FontInstanceAdapter.h | 1 - .../libfontmanager/layout/LEFontInstance.h | 24 +------------------ 3 files changed, 1 insertion(+), 30 deletions(-) diff --git a/jdk/src/java.desktop/share/native/libfontmanager/FontInstanceAdapter.cpp b/jdk/src/java.desktop/share/native/libfontmanager/FontInstanceAdapter.cpp index 8c8c47593fb..3d133952d4d 100644 --- a/jdk/src/java.desktop/share/native/libfontmanager/FontInstanceAdapter.cpp +++ b/jdk/src/java.desktop/share/native/libfontmanager/FontInstanceAdapter.cpp @@ -67,12 +67,6 @@ FontInstanceAdapter::FontInstanceAdapter(JNIEnv *theEnv, }; -const void *FontInstanceAdapter::getFontTable(LETag tableTag) const -{ - size_t ignored = 0; - return getFontTable(tableTag, ignored); -} - static const LETag cacheMap[LAYOUTCACHE_ENTRIES] = { GPOS_TAG, GDEF_TAG, GSUB_TAG, MORT_TAG, MORX_TAG, KERN_TAG }; diff --git a/jdk/src/java.desktop/share/native/libfontmanager/FontInstanceAdapter.h b/jdk/src/java.desktop/share/native/libfontmanager/FontInstanceAdapter.h index 8d2ee3073a0..0d8322dddbe 100644 --- a/jdk/src/java.desktop/share/native/libfontmanager/FontInstanceAdapter.h +++ b/jdk/src/java.desktop/share/native/libfontmanager/FontInstanceAdapter.h @@ -85,7 +85,6 @@ class FontInstanceAdapter : public LEFontInstance { // tables are cached with the native font scaler data // only supports gsub, gpos, gdef, mort tables at present - virtual const void *getFontTable(LETag tableTag) const; virtual const void *getFontTable(LETag tableTag, size_t &len) const; virtual void *getKernPairs() const { diff --git a/jdk/src/java.desktop/share/native/libfontmanager/layout/LEFontInstance.h b/jdk/src/java.desktop/share/native/libfontmanager/layout/LEFontInstance.h index 2baf2d6c85e..3ac687906fa 100644 --- a/jdk/src/java.desktop/share/native/libfontmanager/layout/LEFontInstance.h +++ b/jdk/src/java.desktop/share/native/libfontmanager/layout/LEFontInstance.h @@ -172,28 +172,6 @@ class U_LAYOUT_API LEFontInstance : public UObject // Font file access // - /** - * This method reads a table from the font. Note that in general, - * it only makes sense to call this method on an LEFontInstance - * which represents a physical font - i.e. one which has been returned by - * getSubFont(). This is because each subfont in a composite font - * will have different tables, and there's no way to know which subfont to access. - * - * Subclasses which represent composite fonts should always return NULL. - * - * Note that implementing this function does not allow for range checking. - * Subclasses that desire the safety of range checking must implement the - * variation which has a length parameter. - * - * @param tableTag - the four byte table tag. (e.g. 'cmap') - * - * @return the address of the table in memory, or NULL - * if the table doesn't exist. - * - * @stable ICU 2.8 - */ - virtual const void *getFontTable(LETag tableTag) const = 0; - /** * This method reads a table from the font. Note that in general, * it only makes sense to call this method on an LEFontInstance @@ -213,7 +191,7 @@ class U_LAYOUT_API LEFontInstance : public UObject * if the table doesn't exist. * @internal */ - virtual const void* getFontTable(LETag tableTag, size_t &length) const { length=-1; return getFontTable(tableTag); } /* -1 = unknown length */ + virtual const void* getFontTable(LETag tableTag, size_t &length) const = 0; virtual void *getKernPairs() const = 0; virtual void setKernPairs(void *pairs) const = 0; From 69b04049b56d38ae5811850c9d44a997fa6bd1d1 Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Wed, 25 Nov 2015 14:44:29 +0300 Subject: [PATCH 03/62] 7063986: Wrong JNi method call in font scaler Reviewed-by: prr, rchamyal --- .../java.desktop/share/native/libfontmanager/freetypeScaler.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jdk/src/java.desktop/share/native/libfontmanager/freetypeScaler.c b/jdk/src/java.desktop/share/native/libfontmanager/freetypeScaler.c index 5fe5372be2f..c2a294f91b7 100644 --- a/jdk/src/java.desktop/share/native/libfontmanager/freetypeScaler.c +++ b/jdk/src/java.desktop/share/native/libfontmanager/freetypeScaler.c @@ -258,7 +258,7 @@ Java_sun_font_FreetypeFontScaler_initNativeScaler( scalerInfo->fontData, scalerInfo->fontDataLength); if (bBuffer != NULL) { - (*env)->CallObjectMethod(env, font2D, + (*env)->CallVoidMethod(env, font2D, sunFontIDs.readFileMID, bBuffer); error = FT_New_Memory_Face(scalerInfo->library, From bcb678575580e78beb157c5706c10b4ea521250a Mon Sep 17 00:00:00 2001 From: Ambarish Rapte Date: Thu, 26 Nov 2015 19:12:28 +0400 Subject: [PATCH 04/62] 8055197: TextField deletes multiline strings Reviewed-by: serb, alexsch --- .../share/classes/java/awt/TextField.java | 21 +- .../java/awt/TextField/EOLTest/EOLTest.java | 202 ++++++++++++++++++ 2 files changed, 221 insertions(+), 2 deletions(-) create mode 100644 jdk/test/java/awt/TextField/EOLTest/EOLTest.java diff --git a/jdk/src/java.desktop/share/classes/java/awt/TextField.java b/jdk/src/java.desktop/share/classes/java/awt/TextField.java index de6b5cda6d4..2dfd19c4128 100644 --- a/jdk/src/java.desktop/share/classes/java/awt/TextField.java +++ b/jdk/src/java.desktop/share/classes/java/awt/TextField.java @@ -198,7 +198,7 @@ public TextField(int columns) throws HeadlessException { * @see java.awt.GraphicsEnvironment#isHeadless */ public TextField(String text, int columns) throws HeadlessException { - super(text); + super(replaceEOL(text)); this.columns = (columns >= 0) ? columns : 0; } @@ -297,12 +297,28 @@ public synchronized void setEchoCharacter(char c) { * @see java.awt.TextComponent#getText */ public void setText(String t) { - super.setText(t); + super.setText(replaceEOL(t)); // This could change the preferred size of the Component. invalidateIfValid(); } + /** + * Replaces EOL characters from the text variable with a space character. + * @param text the new text. + * @return Returns text after replacing EOL characters. + */ + private static String replaceEOL(String text) { + String[] strEOLs = {System.lineSeparator(), "\n"}; + for (String eol : strEOLs) { + if (text.contains(eol)) { + text = text.replace(eol, " "); + } + } + return text; + } + + /** * Indicates whether or not this text field has a * character set for echoing. @@ -704,6 +720,7 @@ private void readObject(ObjectInputStream s) { // HeadlessException will be thrown by TextComponent's readObject s.defaultReadObject(); + text = replaceEOL(text); // Make sure the state we just read in for columns has legal values if (columns < 0) { diff --git a/jdk/test/java/awt/TextField/EOLTest/EOLTest.java b/jdk/test/java/awt/TextField/EOLTest/EOLTest.java new file mode 100644 index 00000000000..a9ab7b0941d --- /dev/null +++ b/jdk/test/java/awt/TextField/EOLTest/EOLTest.java @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + @test + @bug 8055197 7186036 + @summary TextField should replace EOL character with space character + @run main EOLTest + */ + +import java.awt.Frame; +import java.awt.TextField; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInput; +import java.io.ObjectInputStream; +import java.io.ObjectOutput; +import java.io.ObjectOutputStream; + +public class EOLTest { + + private Frame mainFrame; + private TextField textField; + private String testStrEOL; + private boolean isTestFail; + private int testFailCount; + StringBuilder testFailMessage; + private String expectedString = "Row1 Row2 Row3"; + + public EOLTest() { + mainFrame = new Frame(); + mainFrame.setSize(200, 200); + mainFrame.setVisible(true); + testFailMessage = new StringBuilder(); + testStrEOL = "Row1" + System.lineSeparator() + "Row2\nRow3"; + } + + private void testConstructor1() { + textField = new TextField(testStrEOL); + textField.setSize(200, 100); + mainFrame.add(textField); + checkTest(); + mainFrame.remove(textField); + } + + private void testConstructor2() { + textField = new TextField(30); + textField.setSize(200, 100); + mainFrame.add(textField); + textField.setText(testStrEOL); + checkTest(); + mainFrame.remove(textField); + } + + private void testConstructor3() { + textField = new TextField(testStrEOL, 30); + textField.setSize(200, 100); + mainFrame.add(textField); + checkTest(); + mainFrame.remove(textField); + } + + private void testSetText() { + textField = new TextField(); + textField.setSize(200, 100); + textField.setText(testStrEOL); + mainFrame.add(textField); + checkTest(); + mainFrame.remove(textField); + } + + private void testDeserialization() { + TextField textFieldToSerialize = new TextField(testStrEOL); + textFieldToSerialize.setSize(200, 100); + mainFrame.add(textFieldToSerialize); + try { + // Serialize TextField object "textFieldToSerialize". + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutput outStream = new ObjectOutputStream(baos); + outStream.writeObject(textFieldToSerialize); + + // Search the text variable data through serialized object stream. + byte[] streamedBytes = baos.toByteArray(); + int foundLoc = 0; + for (int i = 0; i < streamedBytes.length; ++i) { + if (streamedBytes[i] == expectedString.charAt(0)) { + foundLoc = i; + int j = 1; + for (; j < expectedString.length(); ++j) { + if (streamedBytes[i+j] != expectedString.charAt(j)) { + break; + } + } + if (j == expectedString.length()) { + break; + } + } + foundLoc = -1; + } + + if (foundLoc == -1) { + // Could not find text data in serialized object stream. + throw new Exception("Could not find text data in serialized " + + "object stream."); + } + // Replace space character from serialized stream with + // EOL character for testing de-serialization. + String EOLChar = System.lineSeparator(); + String newExpectedString = ""; + for (int i = foundLoc, j = 0; j < expectedString.length(); ++i, ++j) { + newExpectedString += (char)(streamedBytes[i]); + if (streamedBytes[i] == ' ') { + int k = 0; + for (; k < EOLChar.length(); ++k) { + streamedBytes[i + k] = (byte) EOLChar.charAt(k); + } + i += k-1; + j += k-1; + } + } + // New line character varies with platform, + // ex. For windows '\r\n', for linux '\n'. + // While replacing space from serialized object stream, the length + // of EOL character will affect the expected string as well. + expectedString = newExpectedString; + + // De-serialize TextField object stream. + ByteArrayInputStream bais = new ByteArrayInputStream(streamedBytes); + ObjectInput inStream = new ObjectInputStream(bais); + textField = (TextField) inStream.readObject(); + } catch (Exception ex) { + // Serialization or De-serialization failed. + // Create textField with empty string to show failure. + ex.printStackTrace(); + textField = new TextField(); + } + + checkTest(); + mainFrame.remove(textFieldToSerialize); + } + + private void checkTest() { + if (!textField.getText().equals(expectedString)) { + testFailMessage.append("TestFail line : "); + testFailMessage.append(Thread.currentThread().getStackTrace()[2]. + getLineNumber()); + testFailMessage.append(" TextField.getText() : \""); + testFailMessage.append(textField.getText()); + testFailMessage.append("\" does not match expected string : \""); + testFailMessage.append(expectedString).append("\""); + testFailMessage.append(System.getProperty("line.separator")); + testFailCount++; + isTestFail = true; + } + } + + private void checkFailures() { + if (isTestFail) { + testFailMessage.insert(0, "Test Fail count : " + testFailCount + + System.getProperty("line.separator")); + dispose(); + throw new RuntimeException(testFailMessage.toString()); + } + } + + private void dispose() { + if (mainFrame != null) { + mainFrame.dispose(); + } + } + + public static void main(String[] args) { + EOLTest testEOL = new EOLTest(); + testEOL.testConstructor1(); + testEOL.testConstructor2(); + testEOL.testConstructor3(); + testEOL.testSetText(); + testEOL.testDeserialization(); + testEOL.checkFailures(); + testEOL.dispose(); + } +} \ No newline at end of file From 39ce42b41e470034869b79d8ab1fdcde1456477b Mon Sep 17 00:00:00 2001 From: Semyon Sadetsky Date: Tue, 1 Dec 2015 19:02:50 +0300 Subject: [PATCH 05/62] 8081457: TrayIcon tests fail in OEL 7 only Reviewed-by: alexsch, serb, azvegint --- .../classes/sun/awt/X11/XTrayIconPeer.java | 1 + .../TrayIcon/ActionCommand/ActionCommand.java | 7 +- .../ActionEventMask/ActionEventMask.java | 6 +- .../TrayIcon/ModalityTest/ModalityTest.java | 68 ++++++++++++------ .../MouseEventMask/MouseEventMaskTest.java | 4 +- .../MouseMovedTest/MouseMovedTest.java | 12 +++- .../FunctionalityCheck.java | 71 ++++++++++++------- .../FunctionalityCheck/tray.policy | 2 + .../awt/TrayIcon/SystemTrayIconHelper.java | 42 ++++++++++- .../TrayIconEventModifiersTest.java | 8 ++- .../TrayIconEvents/TrayIconEventsTest.java | 67 +++++++++++------ .../TrayIconMouseTest/TrayIconMouseTest.java | 10 ++- .../TrayIconPopup/TrayIconPopupTest.java | 4 +- 13 files changed, 219 insertions(+), 83 deletions(-) diff --git a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XTrayIconPeer.java b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XTrayIconPeer.java index eba30a7e2af..20b7a9d7899 100644 --- a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XTrayIconPeer.java +++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XTrayIconPeer.java @@ -413,6 +413,7 @@ public Rectangle getBounds() { void addListeners() { canvas.addMouseListener(eventProxy); canvas.addMouseMotionListener(eventProxy); + eframe.addMouseListener(eventProxy); } long getWindow() { diff --git a/jdk/test/java/awt/TrayIcon/ActionCommand/ActionCommand.java b/jdk/test/java/awt/TrayIcon/ActionCommand/ActionCommand.java index 5c5a17e0dbc..ce5495e3585 100644 --- a/jdk/test/java/awt/TrayIcon/ActionCommand/ActionCommand.java +++ b/jdk/test/java/awt/TrayIcon/ActionCommand/ActionCommand.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 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 @@ -59,8 +59,11 @@ public static void main(String[] args) throws Exception { "and rerun test."); } else if (System.getProperty("os.name").toLowerCase().startsWith("mac")){ isMacOS = true; + } else if (SystemTrayIconHelper.isOel7()) { + System.out.println("OEL 7 doesn't support double click in " + + "systray. Skipped"); + return; } - new ActionCommand().doTest(); } } diff --git a/jdk/test/java/awt/TrayIcon/ActionEventMask/ActionEventMask.java b/jdk/test/java/awt/TrayIcon/ActionEventMask/ActionEventMask.java index fb95874eadc..ebafe40e282 100644 --- a/jdk/test/java/awt/TrayIcon/ActionEventMask/ActionEventMask.java +++ b/jdk/test/java/awt/TrayIcon/ActionEventMask/ActionEventMask.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 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 @@ -66,6 +66,10 @@ public static void main(String[] args) throws Exception { } else { if (System.getProperty("os.name").toLowerCase().startsWith("mac")) { isMacOS = true; + } else if (SystemTrayIconHelper.isOel7()) { + System.out.println("OEL 7 doesn't support double click in " + + "systray. Skipped"); + return; } new ActionEventMask().doTest(); } diff --git a/jdk/test/java/awt/TrayIcon/ModalityTest/ModalityTest.java b/jdk/test/java/awt/TrayIcon/ModalityTest/ModalityTest.java index 2118457ee79..4c0bb1d3c4a 100644 --- a/jdk/test/java/awt/TrayIcon/ModalityTest/ModalityTest.java +++ b/jdk/test/java/awt/TrayIcon/ModalityTest/ModalityTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 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 @@ -35,6 +35,7 @@ */ public class ModalityTest { + private static boolean isOEL7; TrayIcon icon; ExtendedRobot robot; Dialog d; @@ -80,7 +81,7 @@ public static void main(String[] args) throws Exception { "\"Always show all icons and notifications on the taskbar\" true " + "to avoid this problem. Or change behavior only for Java SE tray " + "icon and rerun test."); - + isOEL7 = SystemTrayIconHelper.isOel7(); new ModalityTest().doTest(); } } @@ -225,6 +226,12 @@ void doTest() throws Exception { Point iconPosition = SystemTrayIconHelper.getTrayIconLocation(icon); if (iconPosition == null) throw new RuntimeException("Unable to find the icon location!"); + if (isOEL7) { + // close tray + robot.mouseMove(100,100); + robot.click(InputEvent.BUTTON1_MASK); + robot.waitForIdle(2000); + } if (! d.isVisible()) throw new RuntimeException("FAIL: The modal dialog is not yet visible"); @@ -232,27 +239,35 @@ void doTest() throws Exception { robot.mouseMove(iconPosition.x, iconPosition.y); robot.waitForIdle(2000); - SystemTrayIconHelper.doubleClick(robot); + if(!isOEL7) { + SystemTrayIconHelper.doubleClick(robot); - if (! actionPerformed) { - synchronized (actionLock) { - try { - actionLock.wait(3000); - } catch (Exception e) { + if (!actionPerformed) { + synchronized (actionLock) { + try { + actionLock.wait(3000); + } catch (Exception e) { + } } } + if (!actionPerformed) + throw new RuntimeException("FAIL: ActionEvent not triggered when TrayIcon is double clicked"); } - if (! actionPerformed) - throw new RuntimeException("FAIL: ActionEvent not triggered when TrayIcon is double clicked"); for (int i = 0; i < buttonTypes.length; i++) { mousePressed = false; - robot.mousePress(buttonTypes[i]); + if(isOEL7) { + SystemTrayIconHelper.openTrayIfNeeded(robot); + robot.mouseMove(iconPosition.x, iconPosition.y); + robot.click(buttonTypes[i]); + } else { + robot.mousePress(buttonTypes[i]); + } if (! mousePressed) { synchronized (pressLock) { try { - pressLock.wait(3000); + pressLock.wait(6000); } catch (Exception e) { } } @@ -264,12 +279,18 @@ void doTest() throws Exception { mouseReleased = false; mouseClicked = false; - robot.mouseRelease(buttonTypes[i]); + if(isOEL7) { + SystemTrayIconHelper.openTrayIfNeeded(robot); + robot.mouseMove(iconPosition.x, iconPosition.y); + robot.click(buttonTypes[i]); + } else { + robot.mouseRelease(buttonTypes[i]); + } if (! mouseReleased) { synchronized (releaseLock) { try { - releaseLock.wait(3000); + releaseLock.wait(6000); } catch (Exception e) { } } @@ -281,7 +302,7 @@ void doTest() throws Exception { if (! mouseClicked) { synchronized (clickLock) { try { - clickLock.wait(3000); + clickLock.wait(6000); } catch (Exception e) { } } @@ -290,13 +311,14 @@ void doTest() throws Exception { throw new RuntimeException("FAIL: mouseClicked not triggered when " + buttonNames[i] + " pressed & released"); } - - mouseMoved = false; - robot.mouseMove(iconPosition.x, iconPosition.y); - robot.glide(iconPosition.x + 100, iconPosition.y); - - if (! mouseMoved) - if (! SystemTrayIconHelper.skip(0) ) - throw new RuntimeException("FAIL: mouseMoved not triggered even when mouse moved over the icon"); + if (!isOEL7) { + mouseMoved = false; + robot.mouseMove(iconPosition.x, iconPosition.y); + robot.glide(iconPosition.x + 100, iconPosition.y); + + if (!mouseMoved) + if (!SystemTrayIconHelper.skip(0)) + throw new RuntimeException("FAIL: mouseMoved not triggered even when mouse moved over the icon"); + } } } diff --git a/jdk/test/java/awt/TrayIcon/MouseEventMask/MouseEventMaskTest.java b/jdk/test/java/awt/TrayIcon/MouseEventMask/MouseEventMaskTest.java index e6c194ec69c..113ec7e9d71 100644 --- a/jdk/test/java/awt/TrayIcon/MouseEventMask/MouseEventMaskTest.java +++ b/jdk/test/java/awt/TrayIcon/MouseEventMask/MouseEventMaskTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 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 @@ -71,6 +71,8 @@ public static void main(String[] args) throws Exception { "\"Always show all icons and notifications on the taskbar\" true " + "to avoid this problem. Or change behavior only for Java SE tray " + "icon and rerun test."); + } else if (SystemTrayIconHelper.isOel7()) { + return; } new MouseEventMaskTest().doTest(); } diff --git a/jdk/test/java/awt/TrayIcon/MouseMovedTest/MouseMovedTest.java b/jdk/test/java/awt/TrayIcon/MouseMovedTest/MouseMovedTest.java index 04d91f23859..d6e6d760f79 100644 --- a/jdk/test/java/awt/TrayIcon/MouseMovedTest/MouseMovedTest.java +++ b/jdk/test/java/awt/TrayIcon/MouseMovedTest/MouseMovedTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -31,7 +31,7 @@ * @summary Check for mouseMoved event for java.awt.TrayIcon * @author Dmitriy Ermashov (dmitriy.ermashov@oracle.com) * @library ../../../../lib/testlibrary - * @build ExtendedRobot + * @build ExtendedRobot SystemTrayIconHelper * @run main MouseMovedTest */ @@ -39,6 +39,14 @@ public class MouseMovedTest { static volatile boolean moved; public static void main(String[] args) throws Exception { + if (!SystemTray.isSupported()) { + return; + } + + if (SystemTrayIconHelper.isOel7()) { + return; + } + moved = false; TrayIcon icon = new TrayIcon(new BufferedImage(20, 20, BufferedImage.TYPE_INT_RGB), "Test icon"); diff --git a/jdk/test/java/awt/TrayIcon/SecurityCheck/FunctionalityCheck/FunctionalityCheck.java b/jdk/test/java/awt/TrayIcon/SecurityCheck/FunctionalityCheck/FunctionalityCheck.java index 5918ac3dfc0..c9d2eade8c3 100644 --- a/jdk/test/java/awt/TrayIcon/SecurityCheck/FunctionalityCheck/FunctionalityCheck.java +++ b/jdk/test/java/awt/TrayIcon/SecurityCheck/FunctionalityCheck/FunctionalityCheck.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 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 @@ -51,6 +51,7 @@ public class FunctionalityCheck { boolean mouseReleased = false; boolean mouseClicked = false; boolean mouseMoved = false; + static boolean isOEL7; static final int[] buttonTypes = { InputEvent.BUTTON1_MASK, @@ -69,6 +70,7 @@ public static void main(String[] args) throws Exception { System.out.println("SystemTray not supported on the platform under test. " + "Marking the test passed"); } else { + isOEL7 = SystemTrayIconHelper.isOel7(); new FunctionalityCheck().doTest(); } } @@ -188,31 +190,44 @@ private void doTest() throws Exception { Point iconPosition = SystemTrayIconHelper.getTrayIconLocation(icon); if (iconPosition == null) throw new RuntimeException("Unable to find the icon location!"); + if (isOEL7) { + // close tray + robot.mouseMove(100,100); + robot.click(InputEvent.BUTTON1_MASK); + robot.waitForIdle(2000); + } robot.mouseMove(iconPosition.x, iconPosition.y); - robot.waitForIdle(2000); - - SystemTrayIconHelper.doubleClick(robot); + robot.waitForIdle(); + if(!isOEL7) { + SystemTrayIconHelper.doubleClick(robot); - if (! actionPerformed) { - synchronized (actionLock) { - try { - actionLock.wait(3000); - } catch (Exception e) { + if (!actionPerformed) { + synchronized (actionLock) { + try { + actionLock.wait(3000); + } catch (Exception e) { + } } } + if (!actionPerformed) + throw new RuntimeException("FAIL: ActionEvent not triggered when TrayIcon is double clicked"); } - if (! actionPerformed) - throw new RuntimeException("FAIL: ActionEvent not triggered when TrayIcon is double clicked"); for (int i = 0; i < buttonTypes.length; i++) { mousePressed = false; - robot.mousePress(buttonTypes[i]); + if(isOEL7) { + SystemTrayIconHelper.openTrayIfNeeded(robot); + robot.mouseMove(iconPosition.x, iconPosition.y); + robot.click(buttonTypes[i]); + } else { + robot.mousePress(buttonTypes[i]); + } if (! mousePressed) { synchronized (pressLock) { try { - pressLock.wait(3000); + pressLock.wait(6000); } catch (Exception e) { } } @@ -224,12 +239,17 @@ private void doTest() throws Exception { mouseReleased = false; mouseClicked = false; - robot.mouseRelease(buttonTypes[i]); - + if(isOEL7) { + SystemTrayIconHelper.openTrayIfNeeded(robot); + robot.mouseMove(iconPosition.x, iconPosition.y); + robot.click(buttonTypes[i]); + } else { + robot.mouseRelease(buttonTypes[i]); + } if (! mouseReleased) { synchronized (releaseLock) { try { - releaseLock.wait(3000); + releaseLock.wait(6000); } catch (Exception e) { } } @@ -242,7 +262,7 @@ private void doTest() throws Exception { if (! mouseClicked) { synchronized (clickLock) { try { - clickLock.wait(3000); + clickLock.wait(6000); } catch (Exception e) { } } @@ -251,13 +271,14 @@ private void doTest() throws Exception { throw new RuntimeException("FAIL: mouseClicked not triggered when " + buttonNames[i] + " pressed & released"); } - - mouseMoved = false; - robot.mouseMove(iconPosition.x + 100, iconPosition.y); - robot.glide(iconPosition.x, iconPosition.y); - - if (! mouseMoved) - if (! SystemTrayIconHelper.skip(0) ) - throw new RuntimeException("FAIL: mouseMoved not triggered even when mouse moved over the icon"); + if(!isOEL7) { + mouseMoved = false; + robot.mouseMove(iconPosition.x + 100, iconPosition.y); + robot.glide(iconPosition.x, iconPosition.y); + + if (!mouseMoved) + if (!SystemTrayIconHelper.skip(0)) + throw new RuntimeException("FAIL: mouseMoved not triggered even when mouse moved over the icon"); + } } } diff --git a/jdk/test/java/awt/TrayIcon/SecurityCheck/FunctionalityCheck/tray.policy b/jdk/test/java/awt/TrayIcon/SecurityCheck/FunctionalityCheck/tray.policy index 845bfb8d80b..c2c76434cd3 100644 --- a/jdk/test/java/awt/TrayIcon/SecurityCheck/FunctionalityCheck/tray.policy +++ b/jdk/test/java/awt/TrayIcon/SecurityCheck/FunctionalityCheck/tray.policy @@ -5,6 +5,7 @@ grant { permission java.util.PropertyPermission "resultsDir", "read"; permission java.util.PropertyPermission "user.home", "read"; permission java.util.PropertyPermission "os.name", "read"; + permission java.util.PropertyPermission "os.version", "read"; permission java.awt.AWTPermission "accessEventQueue"; permission java.lang.RuntimePermission "setIO"; permission java.lang.RuntimePermission "accessDeclaredMembers"; @@ -17,5 +18,6 @@ grant { permission java.util.PropertyPermission "java.class.path", "read"; permission java.awt.AWTPermission "readDisplayPixels"; permission java.awt.AWTPermission "watchMousePointer"; + }; diff --git a/jdk/test/java/awt/TrayIcon/SystemTrayIconHelper.java b/jdk/test/java/awt/TrayIcon/SystemTrayIconHelper.java index a8f6e2f7cf5..cb36b8fdba3 100644 --- a/jdk/test/java/awt/TrayIcon/SystemTrayIconHelper.java +++ b/jdk/test/java/awt/TrayIcon/SystemTrayIconHelper.java @@ -66,7 +66,9 @@ static Point getTrayIconLocation(TrayIcon icon) throws Exception { for (int x = (int) (screenSize.getWidth()-width); x > 0; x--) { for (int y = (int) (screenSize.getHeight()-height); y > (screenSize.getHeight()-50); y--) { if (imagesEquals(((BufferedImage)icon.getImage()).getSubimage(0, 0, width, height), screen.getSubimage(x, y, width, height))) { - return new Point(x+5, y+5); + Point point = new Point(x + 5, y + 5); + System.out.println("Icon location " + point); + return point; } } } @@ -91,6 +93,7 @@ static Point getTrayIconLocation(TrayIcon icon) throws Exception { point2d = (Point2D)m_getLocation.invoke(peer, new Object[]{model}); Point po = new Point((int)(point2d.getX()), (int)(point2d.getY())); po.translate(10, -5); + System.out.println("Icon location " + po); return po; }catch(Exception e) { e.printStackTrace(); @@ -101,12 +104,15 @@ static Point getTrayIconLocation(TrayIcon icon) throws Exception { // sun.awt.X11.XTrayIconPeer Field f_peer = getField(java.awt.TrayIcon.class, "peer"); + SystemTrayIconHelper.openTrayIfNeeded(robot); + Object peer = f_peer.get(icon); Method m_getLOS = peer.getClass().getDeclaredMethod( "getLocationOnScreen", new Class[]{}); m_getLOS.setAccessible(true); Point point = (Point)m_getLOS.invoke(peer, new Object[]{}); point.translate(5, 5); + System.out.println("Icon location " + point); return point; } catch (Exception e) { e.printStackTrace(); @@ -169,4 +175,38 @@ static boolean skip(int button) { } return false; } + + public static boolean openTrayIfNeeded(Robot robot) { + String sysv = System.getProperty("os.version"); + System.out.println("System version is " + sysv); + //Additional step to raise the system try in Gnome 3 in OEL 7 + if(isOel7()) { + System.out.println("OEL 7 detected"); + GraphicsConfiguration gc = GraphicsEnvironment. + getLocalGraphicsEnvironment().getDefaultScreenDevice(). + getDefaultConfiguration(); + Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(gc); + if(insets.bottom > 0) { + Dimension screenSize = Toolkit.getDefaultToolkit() + .getScreenSize(); + robot.mouseMove(screenSize.width - insets.bottom / 2, + screenSize.height - insets.bottom / 2); + robot.delay(50); + robot.mousePress(InputEvent.BUTTON1_MASK); + robot.delay(50); + robot.mouseRelease(InputEvent.BUTTON1_MASK); + robot.waitForIdle(); + robot.delay(1000); + System.out.println("Tray is opened"); + return true; + } + } + return false; + } + + public static boolean isOel7() { + return System.getProperty("os.name").toLowerCase() + .contains("linux") && System.getProperty("os.version") + .toLowerCase().contains("el7"); + } } diff --git a/jdk/test/java/awt/TrayIcon/TrayIconEventModifiers/TrayIconEventModifiersTest.java b/jdk/test/java/awt/TrayIcon/TrayIconEventModifiers/TrayIconEventModifiersTest.java index 9cdce634a92..b80db75b510 100644 --- a/jdk/test/java/awt/TrayIcon/TrayIconEventModifiers/TrayIconEventModifiersTest.java +++ b/jdk/test/java/awt/TrayIcon/TrayIconEventModifiers/TrayIconEventModifiersTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 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 @@ -121,6 +121,12 @@ public static void main(String[] args) throws Exception { }; } + if (SystemTrayIconHelper.isOel7()) { + System.out.println("OEL 7 doesn't support click modifiers in " + + "systray. Skipped"); + return; + } + new TrayIconEventModifiersTest().doTest(); } } diff --git a/jdk/test/java/awt/TrayIcon/TrayIconEvents/TrayIconEventsTest.java b/jdk/test/java/awt/TrayIcon/TrayIconEvents/TrayIconEventsTest.java index c7c79ea6570..275e51065ca 100644 --- a/jdk/test/java/awt/TrayIcon/TrayIconEvents/TrayIconEventsTest.java +++ b/jdk/test/java/awt/TrayIcon/TrayIconEvents/TrayIconEventsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 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 @@ -37,6 +37,7 @@ public class TrayIconEventsTest { + private static boolean isOEL7; TrayIcon icon; ExtendedRobot robot; @@ -77,6 +78,7 @@ public static void main(String[] args) throws Exception { "\"Always show all icons and notifications on the taskbar\" true " + "to avoid this problem. Or change behavior only for Java SE " + "tray icon."); + isOEL7 = SystemTrayIconHelper.isOel7(); new TrayIconEventsTest().doTest(); } } @@ -195,31 +197,44 @@ void doTest() throws Exception { Point iconPosition = SystemTrayIconHelper.getTrayIconLocation(icon); if (iconPosition == null) throw new RuntimeException("Unable to find the icon location!"); + if (isOEL7) { + // close tray + robot.mouseMove(100,100); + robot.click(InputEvent.BUTTON1_MASK); + robot.waitForIdle(2000); + } robot.mouseMove(iconPosition.x, iconPosition.y); - robot.waitForIdle(2000); - - SystemTrayIconHelper.doubleClick(robot); + robot.waitForIdle(); + if(!isOEL7) { + SystemTrayIconHelper.doubleClick(robot); - if (! actionPerformed) { - synchronized (actionLock) { - try { - actionLock.wait(10000); - } catch (Exception e) { + if (!actionPerformed) { + synchronized (actionLock) { + try { + actionLock.wait(10000); + } catch (Exception e) { + } } } + if (!actionPerformed) + throw new RuntimeException("FAIL: ActionEvent not triggered when TrayIcon is double clicked"); } - if (! actionPerformed) - throw new RuntimeException("FAIL: ActionEvent not triggered when TrayIcon is double clicked"); for (int i = 0; i < buttonTypes.length; i++) { mousePressed = false; - robot.mousePress(buttonTypes[i]); + if(isOEL7) { + SystemTrayIconHelper.openTrayIfNeeded(robot); + robot.mouseMove(iconPosition.x, iconPosition.y); + robot.click(buttonTypes[i]); + } else { + robot.mousePress(buttonTypes[i]); + } if (! mousePressed) { synchronized (pressLock) { try { - pressLock.wait(3000); + pressLock.wait(6000); } catch (Exception e) { } } @@ -231,12 +246,18 @@ void doTest() throws Exception { mouseReleased = false; mouseClicked = false; - robot.mouseRelease(buttonTypes[i]); + if(isOEL7) { + SystemTrayIconHelper.openTrayIfNeeded(robot); + robot.mouseMove(iconPosition.x, iconPosition.y); + robot.click(buttonTypes[i]); + } else { + robot.mouseRelease(buttonTypes[i]); + } if (! mouseReleased) { synchronized (releaseLock) { try { - releaseLock.wait(3000); + releaseLock.wait(6000); } catch (Exception e) { } } @@ -248,7 +269,7 @@ void doTest() throws Exception { if (! mouseClicked) { synchronized (clickLock) { try { - clickLock.wait(3000); + clickLock.wait(6000); } catch (Exception e) { } } @@ -258,12 +279,14 @@ void doTest() throws Exception { buttonNames[i] + " pressed & released"); } - mouseMoved = false; - robot.mouseMove(iconPosition.x + 100, iconPosition.y); - robot.glide(iconPosition.x, iconPosition.y); + if (!isOEL7) { + mouseMoved = false; + robot.mouseMove(iconPosition.x + 100, iconPosition.y); + robot.glide(iconPosition.x, iconPosition.y); - if (! mouseMoved) - if (! SystemTrayIconHelper.skip(0) ) - throw new RuntimeException("FAIL: mouseMoved not triggered even when mouse moved over the icon"); + if (!mouseMoved) + if (!SystemTrayIconHelper.skip(0)) + throw new RuntimeException("FAIL: mouseMoved not triggered even when mouse moved over the icon"); + } } } diff --git a/jdk/test/java/awt/TrayIcon/TrayIconMouseTest/TrayIconMouseTest.java b/jdk/test/java/awt/TrayIcon/TrayIconMouseTest/TrayIconMouseTest.java index 124ceba5399..b8f6a69d30c 100644 --- a/jdk/test/java/awt/TrayIcon/TrayIconMouseTest/TrayIconMouseTest.java +++ b/jdk/test/java/awt/TrayIcon/TrayIconMouseTest/TrayIconMouseTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 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 @@ -66,6 +66,10 @@ public static void main(String[] args) throws Exception { } else { if (System.getProperty("os.name").toLowerCase().startsWith("mac")) { isMacOS = true; + } else if (SystemTrayIconHelper.isOel7()) { + System.out.println("OEL 7 doesn't support double click in " + + "systray. Skipped"); + return; } new TrayIconMouseTest().doTest(); } @@ -108,7 +112,7 @@ private void doTest() throws Exception { for (int i = 0; i < buttonTypes.length; i++) { actionPerformed = false; robot.click(buttonTypes[i]); - robot.waitForIdle(2000); + robot.waitForIdle(6000); if (isMacOS && actionPerformed && i == 2) { @@ -155,7 +159,7 @@ private void doTest() throws Exception { if (! actionPerformed) { synchronized (actionLock) { try { - actionLock.wait(3000); + actionLock.wait(6000); } catch (Exception e) { } } diff --git a/jdk/test/java/awt/TrayIcon/TrayIconPopup/TrayIconPopupTest.java b/jdk/test/java/awt/TrayIcon/TrayIconPopup/TrayIconPopupTest.java index f866cc18aa0..a81e516bd0e 100644 --- a/jdk/test/java/awt/TrayIcon/TrayIconPopup/TrayIconPopupTest.java +++ b/jdk/test/java/awt/TrayIcon/TrayIconPopup/TrayIconPopupTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 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 @@ -127,7 +127,7 @@ void doTest() throws Exception { robot.mousePress(InputEvent.BUTTON3_MASK); robot.delay(50); robot.mouseRelease(InputEvent.BUTTON3_MASK); - robot.delay(1000); + robot.delay(6000); robot.mouseMove(window.getLocation().x + 10, window.getLocation().y + 10); robot.mousePress(InputEvent.BUTTON3_MASK); From afabb1b2c0e38ad2f124489faab54456d85b9c96 Mon Sep 17 00:00:00 2001 From: Semyon Sadetsky Date: Tue, 1 Dec 2015 19:07:45 +0300 Subject: [PATCH 06/62] 8068228: Test closed/java/awt/Mouse/MaximizedFrameTest/MaximizedFrameTest fails with GTKLookAndFeel Reviewed-by: ssadetsky, arapte --- .../MaximizedFrameTest.html | 42 ---- .../MaximizedFrameTest.java | 206 +++++++++++------- 2 files changed, 132 insertions(+), 116 deletions(-) delete mode 100644 jdk/test/java/awt/Mouse/MaximizedFrameTest/MaximizedFrameTest.html diff --git a/jdk/test/java/awt/Mouse/MaximizedFrameTest/MaximizedFrameTest.html b/jdk/test/java/awt/Mouse/MaximizedFrameTest/MaximizedFrameTest.html deleted file mode 100644 index 97781dc7643..00000000000 --- a/jdk/test/java/awt/Mouse/MaximizedFrameTest/MaximizedFrameTest.html +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2008, 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. - */ - - - - - - - -

bug 6176814
Bug ID: 6176814

- -

This is an AUTOMATIC test, simply wait for completion

- - - - diff --git a/jdk/test/java/awt/Mouse/MaximizedFrameTest/MaximizedFrameTest.java b/jdk/test/java/awt/Mouse/MaximizedFrameTest/MaximizedFrameTest.java index c2ee806bb19..e519d3dad19 100644 --- a/jdk/test/java/awt/Mouse/MaximizedFrameTest/MaximizedFrameTest.java +++ b/jdk/test/java/awt/Mouse/MaximizedFrameTest/MaximizedFrameTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 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 @@ -22,91 +22,149 @@ */ /* - test - @bug 6176814 - @summary Metalworks frame maximizes after the move - @author Andrei.Dmitriev area=Event - @run applet MaximizedFrameTest.html -*/ - -import java.applet.Applet; -import javax.swing.*; -import java.awt.event.*; -import java.awt.*; - -public class MaximizedFrameTest extends Applet -{ - final int ITERATIONS_COUNT = 20; - Robot robot; - Point framePosition; - Point newFrameLocation; - JFrame frame; - Rectangle gcBounds; - public static Object LOCK = new Object(); - - public void init() - { - String[] instructions = - { - "This is an AUTOMATIC test", - "simply wait until it is done" - }; + @test + @bug 6176814 8132766 + @summary Metalworks frame maximizes after the move + @run main MaximizedFrameTest + */ + +import java.awt.AWTException; +import java.awt.Component; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.InputEvent; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.JFrame; +import javax.swing.JLayeredPane; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; + +public class MaximizedFrameTest { + + final static int ITERATIONS_COUNT = 5; + private static JFrame frame; + private static Point tempMousePosition; + private static Component titleComponent; + + public void init() { JFrame.setDefaultLookAndFeelDecorated(true); + + try { + UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel"); + } catch (ClassNotFoundException | InstantiationException | + IllegalAccessException | UnsupportedLookAndFeelException ex) { + throw new RuntimeException("Test Failed. MetalLookAndFeel not set " + + "for frame"); + } + frame = new JFrame("JFrame Maximization Test"); frame.pack(); frame.setSize(450, 260); - }//End init() - - public void start () - { frame.setVisible(true); - validate(); - JLayeredPane lPane = frame.getLayeredPane(); - // System.out.println("JFrame's LayeredPane " + lPane ); - Component titleComponent = null; - boolean titleFound = false; - for (int j=0; j < lPane.getComponentsInLayer(JLayeredPane.FRAME_CONTENT_LAYER.intValue()).length; j++){ - titleComponent = lPane.getComponentsInLayer(JLayeredPane.FRAME_CONTENT_LAYER.intValue())[j]; - if (titleComponent.getClass().getName().equals("javax.swing.plaf.metal.MetalTitlePane")){ - titleFound = true; - break; + } + + public void getTitleComponent() throws Exception { + + SwingUtilities.invokeAndWait(new Runnable() { + + @Override + public void run() { + JLayeredPane lPane = frame.getLayeredPane(); + boolean titleFound = false; + + for (int j = 0; j < lPane.getComponentsInLayer( + JLayeredPane.FRAME_CONTENT_LAYER.intValue()).length; j++) { + + titleComponent = lPane.getComponentsInLayer( + JLayeredPane.FRAME_CONTENT_LAYER.intValue())[j]; + + if (titleComponent.getClass().getName().equals( + "javax.swing.plaf.metal.MetalTitlePane")) { + + titleFound = true; + break; + } + } + + if (!titleFound) { + try { + dispose(); + } catch (Exception ex) { + Logger.getLogger(MaximizedFrameTest.class.getName()) + .log(Level.SEVERE, null, ex); + } + throw new RuntimeException("Test Failed. Unable to " + + "determine title component"); + } } - } - if ( !titleFound ){ - throw new RuntimeException("Test Failed. Unable to determine title's size."); - } - //-------------------------------- - // it is sufficient to get maximized Frame only once. - Point tempMousePosition; - framePosition = frame.getLocationOnScreen(); + }); + } + + public void doMaximizeFrameTest() throws Exception { + + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + Point framePosition = frame.getLocationOnScreen(); + + tempMousePosition = new Point(framePosition.x + + frame.getWidth() / 2, framePosition.y + + titleComponent.getHeight() / 2); + } + }); + try { - robot = new Robot(); - tempMousePosition = new Point(framePosition.x + - frame.getWidth()/2, - framePosition.y + - titleComponent.getHeight()/2); + Robot robot = new Robot(); robot.mouseMove(tempMousePosition.x, tempMousePosition.y); - for (int iteration=0; iteration < ITERATIONS_COUNT; iteration++){ + robot.waitForIdle(); + + for (int iteration = 0; iteration < ITERATIONS_COUNT; iteration++) { robot.mousePress(InputEvent.BUTTON1_MASK); - gcBounds = - GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()[0].getConfigurations()[0].getBounds(); - //Moving a mouse pointer less than a few pixels - //leads to rising a double click event. - //We have to use exceeded the AWT_MULTICLICK_SMUDGE - //const value (which is 4 by default on GNOME) to test that. + robot.waitForIdle(); + + // Moving a mouse pointer less than a few pixels + // leads to rising a double click event. + // We have to use exceeded the AWT_MULTICLICK_SMUDGE + // const value (which is 4 by default on GNOME) to test that. tempMousePosition.x += 5; robot.mouseMove(tempMousePosition.x, tempMousePosition.y); - robot.delay(70); + robot.waitForIdle(); robot.mouseRelease(InputEvent.BUTTON1_MASK); - if ( frame.getExtendedState() != 0 ){ - throw new RuntimeException ("Test failed. JFrame was maximized. ExtendedState is : "+frame.getExtendedState()); - } - robot.delay(500); - } //for iteration + robot.waitForIdle(); - }catch(AWTException e) { - throw new RuntimeException("Test Failed. AWTException thrown."); + if (frame.getExtendedState() != 0) { + dispose(); + throw new RuntimeException("Test failed. JFrame was " + + "maximized. ExtendedState is : " + + frame.getExtendedState()); + } } + } catch (AWTException e) { + dispose(); + throw new RuntimeException("Test Failed. AWTException thrown."); + } System.out.println("Test passed."); - }// start() -}// class + } + + private void dispose() throws Exception { + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + if (null != frame) { + frame.dispose(); + } + } + }); + } + + public static void main(String[] args) throws Exception { + + MaximizedFrameTest maximizedFrameTest = new MaximizedFrameTest(); + maximizedFrameTest.init(); + maximizedFrameTest.getTitleComponent(); + maximizedFrameTest.doMaximizeFrameTest(); + maximizedFrameTest.dispose(); + } +} From 39b6389c6da36985bead96afefc1c68a5fcd2690 Mon Sep 17 00:00:00 2001 From: Semyon Sadetsky Date: Tue, 1 Dec 2015 19:21:40 +0300 Subject: [PATCH 07/62] 8030702: Deadlock between subclass of AbstractDocument and UndoManager Reviewed-by: alexsch, azvegint --- .../javax/swing/text/AbstractDocument.java | 88 +++++++++++++ .../classes/javax/swing/undo/UndoManager.java | 119 +++++++++++++---- .../swing/text/UndoableEditLockSupport.java | 43 ++++++ .../AbstractDocumentUndoConcurrentTest.java | 122 ++++++++++++++++++ 4 files changed, 346 insertions(+), 26 deletions(-) create mode 100644 jdk/src/java.desktop/share/classes/sun/swing/text/UndoableEditLockSupport.java create mode 100644 jdk/test/javax/swing/undo/UndoManager/AbstractDocumentUndoConcurrentTest.java diff --git a/jdk/src/java.desktop/share/classes/javax/swing/text/AbstractDocument.java b/jdk/src/java.desktop/share/classes/javax/swing/text/AbstractDocument.java index 2b87618abd9..b965ccfcbc0 100644 --- a/jdk/src/java.desktop/share/classes/javax/swing/text/AbstractDocument.java +++ b/jdk/src/java.desktop/share/classes/javax/swing/text/AbstractDocument.java @@ -36,6 +36,7 @@ import sun.font.BidiUtils; import sun.swing.SwingUtilities2; +import sun.swing.text.UndoableEditLockSupport; /** * An implementation of the document interface to serve as a @@ -275,6 +276,11 @@ protected void fireRemoveUpdate(DocumentEvent e) { * @see EventListenerList */ protected void fireUndoableEditUpdate(UndoableEditEvent e) { + if (e.getEdit() instanceof DefaultDocumentEvent) { + e = new UndoableEditEvent(e.getSource(), + new DefaultDocumentEventUndoableWrapper( + (DefaultDocumentEvent)e.getEdit())); + } // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); // Process the listeners last to first, notifying @@ -2952,6 +2958,88 @@ public DocumentEvent.ElementChange getChange(Element elem) { } + static class DefaultDocumentEventUndoableWrapper implements + UndoableEdit, UndoableEditLockSupport + { + final DefaultDocumentEvent dde; + public DefaultDocumentEventUndoableWrapper(DefaultDocumentEvent dde) { + this.dde = dde; + } + + @Override + public void undo() throws CannotUndoException { + dde.undo(); + } + + @Override + public boolean canUndo() { + return dde.canUndo(); + } + + @Override + public void redo() throws CannotRedoException { + dde.redo(); + } + + @Override + public boolean canRedo() { + return dde.canRedo(); + } + + @Override + public void die() { + dde.die(); + } + + @Override + public boolean addEdit(UndoableEdit anEdit) { + return dde.addEdit(anEdit); + } + + @Override + public boolean replaceEdit(UndoableEdit anEdit) { + return dde.replaceEdit(anEdit); + } + + @Override + public boolean isSignificant() { + return dde.isSignificant(); + } + + @Override + public String getPresentationName() { + return dde.getPresentationName(); + } + + @Override + public String getUndoPresentationName() { + return dde.getUndoPresentationName(); + } + + @Override + public String getRedoPresentationName() { + return dde.getRedoPresentationName(); + } + + /** + * {@inheritDoc} + * @since 1.9 + */ + @Override + public void lockEdit() { + ((AbstractDocument)dde.getDocument()).writeLock(); + } + + /** + * {@inheritDoc} + * @since 1.9 + */ + @Override + public void unlockEdit() { + ((AbstractDocument)dde.getDocument()).writeUnlock(); + } + } + /** * This event used when firing document changes while Undo/Redo * operations. It just wraps DefaultDocumentEvent and delegates diff --git a/jdk/src/java.desktop/share/classes/javax/swing/undo/UndoManager.java b/jdk/src/java.desktop/share/classes/javax/swing/undo/UndoManager.java index 782dd3b3356..cb85826e611 100644 --- a/jdk/src/java.desktop/share/classes/javax/swing/undo/UndoManager.java +++ b/jdk/src/java.desktop/share/classes/javax/swing/undo/UndoManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -28,6 +28,7 @@ import javax.swing.event.*; import javax.swing.UIManager; import java.util.*; +import sun.swing.text.UndoableEditLockSupport; /** * {@code UndoManager} manages a list of {@code UndoableEdits}, @@ -134,6 +135,11 @@ */ @SuppressWarnings("serial") // Same-version serialization only public class UndoManager extends CompoundEdit implements UndoableEditListener { + private enum Action { + UNDO, + REDO, + ANY + } int indexOfNextAdd; int limit; @@ -369,13 +375,8 @@ protected void redoTo(UndoableEdit edit) throws CannotRedoException { * @throws CannotRedoException if one of the edits throws * CannotRedoException */ - public synchronized void undoOrRedo() throws CannotRedoException, - CannotUndoException { - if (indexOfNextAdd == edits.size()) { - undo(); - } else { - redo(); - } + public void undoOrRedo() throws CannotRedoException, CannotUndoException { + tryUndoOrRedo(Action.ANY); } /** @@ -407,16 +408,8 @@ public synchronized boolean canUndoOrRedo() { * @see #canUndo * @see #editToBeUndone */ - public synchronized void undo() throws CannotUndoException { - if (inProgress) { - UndoableEdit edit = editToBeUndone(); - if (edit == null) { - throw new CannotUndoException(); - } - undoTo(edit); - } else { - super.undo(); - } + public void undo() throws CannotUndoException { + tryUndoOrRedo(Action.UNDO); } /** @@ -452,16 +445,90 @@ public synchronized boolean canUndo() { * @see #canRedo * @see #editToBeRedone */ - public synchronized void redo() throws CannotRedoException { - if (inProgress) { - UndoableEdit edit = editToBeRedone(); - if (edit == null) { - throw new CannotRedoException(); + public void redo() throws CannotRedoException { + tryUndoOrRedo(Action.REDO); + } + + private void tryUndoOrRedo(Action action) { + UndoableEditLockSupport lockSupport = null; + boolean undo; + synchronized (this) { + if (action == Action.ANY) { + undo = indexOfNextAdd == edits.size(); + } else { + undo = action == Action.UNDO; + } + if (inProgress) { + UndoableEdit edit = undo ? editToBeUndone() : editToBeRedone(); + if (edit == null) { + throw undo ? new CannotUndoException() : + new CannotRedoException(); + } + lockSupport = getEditLockSupport(edit); + if (lockSupport == null) { + if (undo) { + undoTo(edit); + } else { + redoTo(edit); + } + return; + } + } else { + if (undo) { + super.undo(); + } else { + super.redo(); + } + return; } - redoTo(edit); - } else { - super.redo(); } + // the edit synchronization is required + while (true) { + lockSupport.lockEdit(); + UndoableEditLockSupport editLockSupport = null; + try { + synchronized (this) { + if (action == Action.ANY) { + undo = indexOfNextAdd == edits.size(); + } + if (inProgress) { + UndoableEdit edit = undo ? editToBeUndone() : + editToBeRedone(); + if (edit == null) { + throw undo ? new CannotUndoException() : + new CannotRedoException(); + } + editLockSupport = getEditLockSupport(edit); + if (editLockSupport == null || + editLockSupport == lockSupport) { + if (undo) { + undoTo(edit); + } else { + redoTo(edit); + } + return; + } + } else { + if (undo) { + super.undo(); + } else { + super.redo(); + } + return; + } + } + } finally { + if (lockSupport != null) { + lockSupport.unlockEdit(); + } + lockSupport = editLockSupport; + } + } + } + + private UndoableEditLockSupport getEditLockSupport(UndoableEdit anEdit) { + return anEdit instanceof UndoableEditLockSupport ? + (UndoableEditLockSupport)anEdit : null; } /** diff --git a/jdk/src/java.desktop/share/classes/sun/swing/text/UndoableEditLockSupport.java b/jdk/src/java.desktop/share/classes/sun/swing/text/UndoableEditLockSupport.java new file mode 100644 index 00000000000..43440da0dc8 --- /dev/null +++ b/jdk/src/java.desktop/share/classes/sun/swing/text/UndoableEditLockSupport.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.swing.text; + +import javax.swing.undo.UndoableEdit; + +/** + * UndoableEdit support for undo/redo actions synchronization + * @since 1.9 + */ +public interface UndoableEditLockSupport extends UndoableEdit { + /** + * lock the UndoableEdit for threadsafe undo/redo + */ + void lockEdit(); + + /** + * unlock the UndoableEdit + */ + void unlockEdit(); +} diff --git a/jdk/test/javax/swing/undo/UndoManager/AbstractDocumentUndoConcurrentTest.java b/jdk/test/javax/swing/undo/UndoManager/AbstractDocumentUndoConcurrentTest.java new file mode 100644 index 00000000000..30c48df47f8 --- /dev/null +++ b/jdk/test/javax/swing/undo/UndoManager/AbstractDocumentUndoConcurrentTest.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + @bug 8030702 + @summary Deadlock between subclass of AbstractDocument and UndoManager + @author Semyon Sadetsky + */ + +import javax.swing.text.PlainDocument; +import javax.swing.text.StringContent; +import javax.swing.undo.UndoManager; +import java.text.DecimalFormat; +import java.text.Format; +import java.util.concurrent.CyclicBarrier; + +public class AbstractDocumentUndoConcurrentTest { + static CyclicBarrier barrier = new CyclicBarrier(3); + + private static PlainDocument doc1; + private static PlainDocument doc2; + private static Format format1 = new DecimalFormat(""); + private static Format format2 = new DecimalFormat(""); + + public static void main(String[] args) throws Exception { + test(); + System.out.println(doc1.getText(0, doc1.getLength())); + System.out.println(doc2.getText(0, doc2.getLength())); + System.out.println("ok"); + } + + private static void test() throws Exception { + doc1 = new PlainDocument(new StringContent()); + final UndoManager undoManager = new UndoManager(); + + doc1.addUndoableEditListener(undoManager); + doc1.insertString(0, "", null); + + doc2 = new PlainDocument(new StringContent()); + + doc2.addUndoableEditListener(undoManager); + doc2.insertString(0, "", null); + + Thread t1 = new Thread("Thread doc1") { + @Override + public void run() { + try { + barrier.await(); + for (int i = 0; i < 1000; i++) { + doc1.insertString(0, format1.format(i), null); + if(doc1.getLength() > 100) doc1.remove(0, 12); + } + + } catch (Exception e) { + throw new RuntimeException(e); + } + System.out.println("t1 done"); + } + }; + + Thread t2 = new Thread("Thread doc2") { + @Override + public void run() { + try { + barrier.await(); + for (int i = 0; i < 1000; i++) { + doc2.insertString(0, format2.format(i), null); + if(doc2.getLength() > 100) doc2.remove(0, 13); + } + + } catch (Exception e) { + throw new RuntimeException(e); + } + System.out.println("t2 done"); + } + }; + + Thread t3 = new Thread("Undo/Redo Thread") { + @Override + public void run() { + try { + barrier.await(); + } catch (Exception e) { + e.printStackTrace(); + } + for (int i = 0; i < 1000; i++) { + undoManager.undoOrRedo(); + undoManager.undo(); + } + System.out.println("t3 done"); + } + }; + + t1.start(); + t2.start(); + t3.start(); + + t1.join(); + t2.join(); + t3.join(); + } +} From e300345cc8e854f9ad7fe5347d4fe07ec93cfc9c Mon Sep 17 00:00:00 2001 From: Jayathirth D V Date: Wed, 2 Dec 2015 00:34:35 +0530 Subject: [PATCH 08/62] 8074967: [macosx] JPEGImageReader incorrectly identifies YCbCr JPEGs as RGB in standard metadata Reviewed-by: prr, psadhukhan --- .../imageio/plugins/jpeg/JPEGMetadata.java | 12 +-- .../jpeg/JpegMetadataColorSpaceTest.java | 69 ++++++++++++++++++ .../javax/imageio/plugins/jpeg/nomarkers.jpg | Bin 0 -> 548 bytes 3 files changed, 75 insertions(+), 6 deletions(-) create mode 100644 jdk/test/javax/imageio/plugins/jpeg/JpegMetadataColorSpaceTest.java create mode 100644 jdk/test/javax/imageio/plugins/jpeg/nomarkers.jpg diff --git a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/JPEGMetadata.java b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/JPEGMetadata.java index f25863955ec..7975efda580 100644 --- a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/JPEGMetadata.java +++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/JPEGMetadata.java @@ -874,13 +874,13 @@ protected IIOMetadataNode getStandardChromaNode() { return chroma; } - boolean idsAreJFIF = true; + boolean idsAreJFIF = false; - for (int i = 0; i < sof.componentSpecs.length; i++) { - int id = sof.componentSpecs[i].componentId; - if ((id < 1) || (id >= sof.componentSpecs.length)) { - idsAreJFIF = false; - } + int cid0 = sof.componentSpecs[0].componentId; + int cid1 = sof.componentSpecs[1].componentId; + int cid2 = sof.componentSpecs[2].componentId; + if ((cid0 == 1) && (cid1 == 2) && (cid2 == 3)) { + idsAreJFIF = true; } if (idsAreJFIF) { diff --git a/jdk/test/javax/imageio/plugins/jpeg/JpegMetadataColorSpaceTest.java b/jdk/test/javax/imageio/plugins/jpeg/JpegMetadataColorSpaceTest.java new file mode 100644 index 00000000000..dbe54f553e2 --- /dev/null +++ b/jdk/test/javax/imageio/plugins/jpeg/JpegMetadataColorSpaceTest.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8074967 + * @summary Test verifies if there is no JFIF & EXIF header + * and sampling factor is same of JPEG image, then + * JPEG colorspace should not be RGB. + * @run main JpegMetadataColorSpaceTest + */ + +import javax.imageio.ImageIO; +import javax.imageio.ImageReader; +import javax.imageio.metadata.IIOMetadata; +import javax.imageio.metadata.IIOMetadataFormatImpl; +import javax.imageio.metadata.IIOMetadataNode; +import javax.imageio.stream.ImageInputStream; +import java.io.File; +import java.io.IOException; +import java.util.Iterator; + +public class JpegMetadataColorSpaceTest { + public static void main(String[] args) throws IOException { + String fileName = "nomarkers.jpg"; + String sep = System.getProperty("file.separator"); + String dir = System.getProperty("test.src", "."); + String filePath = dir+sep+fileName; + System.out.println("Test file: " + filePath); + File file = new File(filePath); + ImageInputStream stream = ImageIO.createImageInputStream(file); + Iterator readers = ImageIO.getImageReaders(stream); + + if(readers.hasNext()) { + ImageReader reader = readers.next(); + reader.setInput(stream); + IIOMetadata metadata = reader.getImageMetadata(0); + + IIOMetadataNode standardTree = (IIOMetadataNode) + metadata.getAsTree + (IIOMetadataFormatImpl.standardMetadataFormatName); + IIOMetadataNode colorSpaceType = (IIOMetadataNode) + standardTree.getElementsByTagName("ColorSpaceType").item(0); + String colorSpaceName = colorSpaceType.getAttribute("name"); + if(colorSpaceName.equals("RGB")) + throw new RuntimeException("Identified incorrect ColorSpace"); + } + } +} diff --git a/jdk/test/javax/imageio/plugins/jpeg/nomarkers.jpg b/jdk/test/javax/imageio/plugins/jpeg/nomarkers.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3b08cdd3b02b159411cd54e30c2683be6d8ca6b0 GIT binary patch literal 548 zcmb79+YN#+6uq~lP-wvxTjbZoJs|PRE!@N%9K$h;L--h`wjq+BJmSgu>CJi7-Q5fJ zKnV%-h&&l*EQ(l`Br%tnlp+%%FG`gcx)4HDO0SKzR<1U6W$Myc6GD)BPsCEjQj?3^ z{0(;n&7p;sq6VUoYIHZSfCxiye>Oi?a8HmFfhei~@X`)V{p%Fm>O1r3-P~s)Bcavk saREhaa85va!DL0;bQO~VY_|CM3{XE@oe#TFE-Y0`OFO~L5HEPV0ZWiZasU7T literal 0 HcmV?d00001 From 3a2b32b7076804276dbe02c4f9d11ee20bcc981e Mon Sep 17 00:00:00 2001 From: Jayathirth D V Date: Wed, 2 Dec 2015 00:47:36 +0530 Subject: [PATCH 09/62] 6967419: IndexOutOfBoundsException when drawing PNGs Reviewed-by: prr, psadhukhan --- .../imageio/plugins/png/PNGImageWriter.java | 12 ++- .../plugins/png/PngForceStopWritingTest.java | 76 +++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 jdk/test/javax/imageio/plugins/png/PngForceStopWritingTest.java diff --git a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGImageWriter.java b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGImageWriter.java index 0004904aa99..278882e5385 100644 --- a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGImageWriter.java +++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGImageWriter.java @@ -193,7 +193,17 @@ private void finishChunk() throws IOException { // Return to end of chunk and flush to minimize buffering stream.seek(pos); - stream.flushBefore(pos); + try { + stream.flushBefore(pos); + } catch (IOException e) { + /* + * If flushBefore() fails we try to access startPos in finally + * block of write_IDAT(). We should update startPos to avoid + * IndexOutOfBoundException while seek() is happening. + */ + this.startPos = stream.getStreamPosition(); + throw e; + } } public int read() throws IOException { diff --git a/jdk/test/javax/imageio/plugins/png/PngForceStopWritingTest.java b/jdk/test/javax/imageio/plugins/png/PngForceStopWritingTest.java new file mode 100644 index 00000000000..b2da248787c --- /dev/null +++ b/jdk/test/javax/imageio/plugins/png/PngForceStopWritingTest.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 6967419 + * @summary Test verifies that when we force stop PNG writing to + * ImageOutputStream, it should not cause IndexOutOfBoundException. + * @run main PngForceStopWritingTest + */ + +import java.awt.Color; +import java.awt.GradientPaint; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.OutputStream; +import javax.imageio.ImageIO; +import javax.imageio.stream.ImageOutputStream; + +public class PngForceStopWritingTest { + + public static void main(String[] args) throws IOException { + + OutputStream outputStream = new NullOutputStream(); + ImageOutputStream imageOutputStream = + ImageIO.createImageOutputStream(outputStream); + try { + ImageIO.write(createImage(2048),"PNG", imageOutputStream); + } catch (IOException e) { + imageOutputStream.close(); + } + } + + private static BufferedImage createImage(int size) { + + BufferedImage image = new + BufferedImage(size, size, BufferedImage.TYPE_3BYTE_BGR); + Graphics2D g = image.createGraphics(); + g.setPaint(new GradientPaint(0, 0, Color.blue, size, size, Color.red)); + g.fillRect(0, 0, size, size); + g.dispose(); + return image; + } + + static class NullOutputStream extends OutputStream { + long count = 0; + @Override + public void write(int b) throws IOException { + count++; + if (count > 30000L) { + throw new IOException("Force stop image writing"); + } + } + } +} From 2b89c8529ddb210492786b533435521fa9eef559 Mon Sep 17 00:00:00 2001 From: Jayathirth D V Date: Wed, 2 Dec 2015 00:52:49 +0530 Subject: [PATCH 10/62] 8041501: ImageIO reader is not capable of reading JPEGs without JFIF header Reviewed-by: prr, psadhukhan --- .../share/native/libjavajpeg/imageioJPEG.c | 30 ++++---- .../plugins/jpeg/JpegImageColorSpaceTest.java | 69 +++++++++++++++++++ 2 files changed, 86 insertions(+), 13 deletions(-) create mode 100644 jdk/test/javax/imageio/plugins/jpeg/JpegImageColorSpaceTest.java diff --git a/jdk/src/java.desktop/share/native/libjavajpeg/imageioJPEG.c b/jdk/src/java.desktop/share/native/libjavajpeg/imageioJPEG.c index f0d5c019a8d..81fe9117d04 100644 --- a/jdk/src/java.desktop/share/native/libjavajpeg/imageioJPEG.c +++ b/jdk/src/java.desktop/share/native/libjavajpeg/imageioJPEG.c @@ -1610,6 +1610,7 @@ Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_readImageHeader int ret; int h_samp0, h_samp1, h_samp2; int v_samp0, v_samp1, v_samp2; + int cid0, cid1, cid2; jboolean retval = JNI_FALSE; imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr); j_decompress_ptr cinfo; @@ -1711,17 +1712,15 @@ Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_readImageHeader } } else if (!cinfo->saw_JFIF_marker && !IS_EXIF(cinfo)) { /* - * IJG assumes all unidentified 3-channels are YCbCr. - * We assume that only if the second two channels are - * subsampled (either horizontally or vertically). If not, - * we assume RGB. - * - * 4776576: Some digital cameras output YCbCr JPEG images - * that do not contain a JFIF APP0 marker but are only - * vertically subsampled (no horizontal subsampling). - * We should only assume this is RGB data if the subsampling - * factors for the second two channels are the same as the - * first (check both horizontal and vertical factors). + * In the absence of certain markers, IJG has interpreted + * component id's of [1,2,3] as meaning YCbCr.We follow that + * interpretation, which is additionally described in the Image + * I/O JPEG metadata spec.If that condition is not met here the + * next step will be to examine the subsampling factors, if + * there is any difference in subsampling factors we also assume + * YCbCr, only if both horizontal and vertical subsampling + * is same we assume JPEG color space as RGB. + * This is also described in the Image I/O JPEG metadata spec. */ h_samp0 = cinfo->comp_info[0].h_samp_factor; h_samp1 = cinfo->comp_info[1].h_samp_factor; @@ -1731,8 +1730,13 @@ Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_readImageHeader v_samp1 = cinfo->comp_info[1].v_samp_factor; v_samp2 = cinfo->comp_info[2].v_samp_factor; - if ((h_samp1 == h_samp0) && (h_samp2 == h_samp0) && - (v_samp1 == v_samp0) && (v_samp2 == v_samp0)) + cid0 = cinfo->comp_info[0].component_id; + cid1 = cinfo->comp_info[1].component_id; + cid2 = cinfo->comp_info[2].component_id; + + if ((!(cid0 == 1 && cid1 == 2 && cid2 == 3)) && + ((h_samp1 == h_samp0) && (h_samp2 == h_samp0) && + (v_samp1 == v_samp0) && (v_samp2 == v_samp0))) { cinfo->jpeg_color_space = JCS_RGB; /* output is already RGB, so it stays the same */ diff --git a/jdk/test/javax/imageio/plugins/jpeg/JpegImageColorSpaceTest.java b/jdk/test/javax/imageio/plugins/jpeg/JpegImageColorSpaceTest.java new file mode 100644 index 00000000000..c18b8216120 --- /dev/null +++ b/jdk/test/javax/imageio/plugins/jpeg/JpegImageColorSpaceTest.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8041501 + * @summary Test verifies if there is no JFIF & EXIF header + * and sampling factor is same of JPEG image, then + * imageIO should not override colorspace determined + * in IJG library. + * @run main JpegImageColorSpaceTest + */ + +import java.awt.Color; +import java.awt.image.BufferedImage; +import java.io.File; +import javax.imageio.ImageIO; + +public class JpegImageColorSpaceTest { + + public static void main(String args[]) throws Exception { + + String fileName = "nomarkers.jpg"; + String sep = System.getProperty("file.separator"); + String dir = System.getProperty("test.src", "."); + String filePath = dir+sep+fileName; + System.out.println("Test file: " + filePath); + File imageFile = new File(filePath); + + BufferedImage bufferedImage = ImageIO.read(imageFile); + int imageWidth = bufferedImage.getWidth(); + int imageHeight = bufferedImage.getHeight(); + + for (int i = 0; i < imageWidth; i++) { + for(int j = 0; j < imageHeight; j++) { + /* + * Since image is white we check individual pixel values from + * BufferedImage to verify if ImageIO.read() is done with proper + * color space or not. + */ + if (bufferedImage.getRGB(i, j) != Color.white.getRGB()) { + // color space is not proper + throw new RuntimeException("ColorSpace is not determined " + + "properly by ImageIO"); + } + } + } + } +} From 854c76518636eacf0496f971fc4fd75a7eed8e9f Mon Sep 17 00:00:00 2001 From: Jiri Vanek Date: Wed, 2 Dec 2015 21:23:59 +0000 Subject: [PATCH 11/62] 8144071: ImageIO does not reset stream if an exception is thrown Reset the I/O stream in a finally block Reviewed-by: andrew --- .../share/classes/javax/imageio/ImageIO.java | 9 +- .../imageio/spi/MarkTryFinallyReproducer.java | 367 ++++++++++++++++++ 2 files changed, 373 insertions(+), 3 deletions(-) create mode 100644 jdk/test/javax/imageio/spi/MarkTryFinallyReproducer.java diff --git a/jdk/src/java.desktop/share/classes/javax/imageio/ImageIO.java b/jdk/src/java.desktop/share/classes/javax/imageio/ImageIO.java index 7beea7b77a6..b56975ba15b 100644 --- a/jdk/src/java.desktop/share/classes/javax/imageio/ImageIO.java +++ b/jdk/src/java.desktop/share/classes/javax/imageio/ImageIO.java @@ -564,9 +564,12 @@ public boolean filter(Object elt) { if (stream != null) { stream.mark(); } - canDecode = spi.canDecodeInput(input); - if (stream != null) { - stream.reset(); + try { + canDecode = spi.canDecodeInput(input); + } finally { + if (stream != null) { + stream.reset(); + } } return canDecode; diff --git a/jdk/test/javax/imageio/spi/MarkTryFinallyReproducer.java b/jdk/test/javax/imageio/spi/MarkTryFinallyReproducer.java new file mode 100644 index 00000000000..7efd06a62f4 --- /dev/null +++ b/jdk/test/javax/imageio/spi/MarkTryFinallyReproducer.java @@ -0,0 +1,367 @@ +/* + * Copyright 2015 Red Hat, Inc. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8144071 + * @run main/othervm MarkTryFinallyReproducer + * @summary Test that call to canDecodeInput in ImageIO don't corrupt + * mark/reset stack in ImageInputStream + * @author Jiri Vanek + */ + +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.ByteOrder; +import java.util.Locale; +import javax.imageio.ImageIO; +import javax.imageio.ImageReader; +import javax.imageio.spi.IIORegistry; +import javax.imageio.spi.ImageReaderSpi; +import javax.imageio.stream.IIOByteBuffer; +import javax.imageio.stream.ImageInputStream; + + +public class MarkTryFinallyReproducer { + + private static final byte[] bmp = new byte[]{ + 127,127, 66, 77, -86, 0, 0, 0, 0, 0, 0, 0, + 122, 0, 0, 0, 108, 0, 0, 0, 4, 0, 0, 0, 4, + 0, 0, 0, 1, 0, 24, 0, 0, 0, 0, 0, 48, 0, 0, + 0, 19, 11, 0, 0, 19, 11, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 66, 71, 82, 115, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, -1, -1, -1, + -1, -1, -1, 0, -1, 0, -1, -1, -1, -1, 0, 0, 0, -17, + 0, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, -1, -1, -1, + -1, 0, 0, 0, -1, -1, -1, -1, -1, -1, 0, 0, -1 + }; + //first two are evil, we are skipping them later. Others are normal BMP + + private static class NotClosingImageInputStream implements ImageInputStream { + + private final ImageInputStream src; + + private NotClosingImageInputStream(ImageInputStream createImageInputStream) { + this.src = createImageInputStream; + } + + @Override + public void setByteOrder(ByteOrder byteOrder) { + src.setByteOrder(byteOrder); + } + + @Override + public ByteOrder getByteOrder() { + return src.getByteOrder(); + } + + @Override + public int read() throws IOException { + return src.read(); + } + + @Override + public int read(byte[] b) throws IOException { + return src.read(b); + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + return src.read(b, off, len); + } + + @Override + public void readBytes(IIOByteBuffer buf, int len) throws IOException { + src.readBytes(buf, len); + } + + @Override + public boolean readBoolean() throws IOException { + return src.readBoolean(); + } + + @Override + public byte readByte() throws IOException { + return src.readByte(); + } + + @Override + public int readUnsignedByte() throws IOException { + return src.readUnsignedByte(); + } + + @Override + public short readShort() throws IOException { + return src.readShort(); + } + + @Override + public int readUnsignedShort() throws IOException { + return src.readUnsignedShort(); + } + + @Override + public char readChar() throws IOException { + return src.readChar(); + } + + @Override + public int readInt() throws IOException { + return src.readInt(); + } + + @Override + public long readUnsignedInt() throws IOException { + return src.readUnsignedInt(); + } + + @Override + public long readLong() throws IOException { + return src.readLong(); + } + + @Override + public float readFloat() throws IOException { + return src.readFloat(); + } + + @Override + public double readDouble() throws IOException { + return src.readDouble(); + } + + @Override + public String readLine() throws IOException { + return src.readLine(); + } + + @Override + public String readUTF() throws IOException { + return src.readUTF(); + } + + @Override + public void readFully(byte[] b, int off, int len) throws IOException { + src.readFully(b, off, len); + } + + @Override + public void readFully(byte[] b) throws IOException { + src.readFully(b); + } + + @Override + public void readFully(short[] s, int off, int len) throws IOException { + src.readFully(s, off, len); + } + + @Override + public void readFully(char[] c, int off, int len) throws IOException { + src.readFully(c, off, len); + } + + @Override + public void readFully(int[] i, int off, int len) throws IOException { + src.readFully(i, off, len); + } + + @Override + public void readFully(long[] l, int off, int len) throws IOException { + src.readFully(l, off, len); + } + + @Override + public void readFully(float[] f, int off, int len) throws IOException { + src.readFully(f, off, len); + } + + @Override + public void readFully(double[] d, int off, int len) throws IOException { + src.readFully(d, off, len); + } + + @Override + public long getStreamPosition() throws IOException { + return src.getStreamPosition(); + } + + @Override + public int getBitOffset() throws IOException { + return src.getBitOffset(); + } + + @Override + public void setBitOffset(int bitOffset) throws IOException { + src.setBitOffset(bitOffset); + } + + @Override + public int readBit() throws IOException { + return src.readBit(); + } + + @Override + public long readBits(int numBits) throws IOException { + return src.readBits(numBits); + } + + @Override + public long length() throws IOException { + return src.length(); + } + + @Override + public int skipBytes(int n) throws IOException { + return src.skipBytes(n); + } + + @Override + public long skipBytes(long n) throws IOException { + return src.skipBytes(n); + } + + @Override + public void seek(long pos) throws IOException { + src.seek(pos); + } + + @Override + public void mark() { + src.mark(); + } + + @Override + public void reset() throws IOException { + src.reset(); + } + + @Override + public void flushBefore(long pos) throws IOException { + src.flushBefore(pos); + } + + @Override + public void flush() throws IOException { + src.flush(); + } + + @Override + public long getFlushedPosition() { + return src.getFlushedPosition(); + } + + @Override + public boolean isCached() { + return src.isCached(); + } + + @Override + public boolean isCachedMemory() { + return src.isCachedMemory(); + } + + @Override + public boolean isCachedFile() { + return src.isCachedFile(); + } + + @Override + public void close() throws IOException { + //the only important one. nothing + } + } + + static final String readerClassName + = MarkTryFinallyReproducerSpi.class.getName(); + static final String[] localNames = {"myNames"}; + static final String[] localSuffixes = {"mySuffixes"}; + static final String[] localMIMETypes = {"myMimes"}; + + public static class MarkTryFinallyReproducerSpi extends ImageReaderSpi { + + public MarkTryFinallyReproducerSpi() { + super("MarkTryFinallyReproducerSpi", + "1.0", + localNames, + localSuffixes, + localMIMETypes, + readerClassName, + new Class[]{ImageInputStream.class}, + new String[0], + false, + null, + null, + new String[0], + new String[0], + false, + null, + null, + new String[0], + new String[0]); + } + + @Override + public String getDescription(Locale locale) { + return ""; + } + + @Override + public boolean canDecodeInput(Object input) throws IOException { + throw new IOException("Bad luck"); + } + + @Override + public ImageReader createReaderInstance(Object extension) { + return null; + } + } + + public static void main(String[] args) throws IOException { + MarkTryFinallyReproducerSpi spi = new MarkTryFinallyReproducerSpi(); + IIORegistry.getDefaultInstance().registerServiceProvider(spi); + + ImageInputStream iis1 = + new NotClosingImageInputStream(ImageIO.createImageInputStream(new ByteArrayInputStream(bmp))); + iis1.readByte(); + iis1.mark(); + long p1 = iis1.getStreamPosition(); + iis1.readByte(); + iis1.mark(); + long p2 = iis1.getStreamPosition(); + BufferedImage bi1 = ImageIO.read(iis1); + iis1.reset(); + long pn2 = iis1.getStreamPosition(); + iis1.reset(); + long pn1 = iis1.getStreamPosition(); + if (p1 != pn1 || p2!= pn2) { + throw new RuntimeException("Exception from call to canDecodeInput in ImageIO. " + + "Corrupted stack in ImageInputStream"); + } + + } + +} From 59accc606b6d79a8dba0a86d11fa92fa7e52cb23 Mon Sep 17 00:00:00 2001 From: Sergey Bylokhov Date: Thu, 3 Dec 2015 12:27:02 +0530 Subject: [PATCH 12/62] 8131754: AquaTreeUI.getCollapsedIcon() issue reported in java beans tests with a modular build Reviewed-by: malenkov, alexsch --- .../share/classes/javax/swing/JComponent.java | 1 + .../XMLEncoder/javax_swing_JComponent.java | 77 +++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 jdk/test/java/beans/XMLEncoder/javax_swing_JComponent.java diff --git a/jdk/src/java.desktop/share/classes/javax/swing/JComponent.java b/jdk/src/java.desktop/share/classes/javax/swing/JComponent.java index 3276d534a48..b6fa1b7bd81 100644 --- a/jdk/src/java.desktop/share/classes/javax/swing/JComponent.java +++ b/jdk/src/java.desktop/share/classes/javax/swing/JComponent.java @@ -618,6 +618,7 @@ public void updateUI() {} * @return the {@code ComponentUI} object that renders this component * @since 1.9 */ + @Transient public ComponentUI getUI() { return ui; } diff --git a/jdk/test/java/beans/XMLEncoder/javax_swing_JComponent.java b/jdk/test/java/beans/XMLEncoder/javax_swing_JComponent.java new file mode 100644 index 00000000000..9ca11f9954a --- /dev/null +++ b/jdk/test/java/beans/XMLEncoder/javax_swing_JComponent.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.swing.JComponent; +import javax.swing.plaf.ComponentUI; + +/* + * @test + * @bug 8131754 + * @summary Tests JComponent encoding + */ +public final class javax_swing_JComponent extends AbstractTest { + + public static void main(final String[] args) { + new javax_swing_JComponent().test(true); + } + + protected JComponent getObject() { + return new SimpleJComponent(); + } + + protected JComponent getAnotherObject() { + return new CustomJComponent(); + } + + public static final class SimpleJComponent extends JComponent { + + } + + public static final class CustomJComponent extends JComponent { + + public CustomJComponent() { + ui = new CustomUI(); + } + + @Override + public ComponentUI getUI() { + return ui; + } + + @Override + public void setUI(final ComponentUI newUI) { + ui = newUI; + } + } + + public static final class CustomUI extends ComponentUI { + + public boolean getFlag() { + throw new Error(); + } + + public void setFlag(final boolean flag) { + throw new Error(); + } + } +} From 6ffba3b398f03d29ed0778e5ca6534540c1d89a3 Mon Sep 17 00:00:00 2001 From: Alexey Ivanov Date: Thu, 3 Dec 2015 15:22:31 +0300 Subject: [PATCH 13/62] 8134152: Public API for java 8 DataFlavor fields do not have @since tag Reviewed-by: ssadetsky, alexsch --- .../share/classes/java/awt/datatransfer/DataFlavor.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/jdk/src/java.datatransfer/share/classes/java/awt/datatransfer/DataFlavor.java b/jdk/src/java.datatransfer/share/classes/java/awt/datatransfer/DataFlavor.java index 5f05177be39..becfd0563ba 100644 --- a/jdk/src/java.datatransfer/share/classes/java/awt/datatransfer/DataFlavor.java +++ b/jdk/src/java.datatransfer/share/classes/java/awt/datatransfer/DataFlavor.java @@ -289,6 +289,8 @@ private static DataFlavor initHtmlDataFlavor(String htmlFlavorType) { * representationClass = String * mimeType = "text/html" * + * + * @since 1.8 */ public static DataFlavor selectionHtmlFlavor = initHtmlDataFlavor("selection"); @@ -301,6 +303,8 @@ private static DataFlavor initHtmlDataFlavor(String htmlFlavorType) { * representationClass = String * mimeType = "text/html" * + * + * @since 1.8 */ public static DataFlavor fragmentHtmlFlavor = initHtmlDataFlavor("fragment"); @@ -314,6 +318,8 @@ private static DataFlavor initHtmlDataFlavor(String htmlFlavorType) { * representationClass = String * mimeType = "text/html" * + * + * @since 1.8 */ public static DataFlavor allHtmlFlavor = initHtmlDataFlavor("all"); From 3096b2cc2f538b50b73971d8996c2c90a60da380 Mon Sep 17 00:00:00 2001 From: Rajeev Chamyal Date: Fri, 4 Dec 2015 09:56:50 +0400 Subject: [PATCH 14/62] 8067660: JFileChooser create new folder fails silently Reviewed-by: alexsch, psadhukhan --- .../swing/filechooser/FileSystemView.java | 13 +- .../JFileChooser/8067660/FileChooserTest.java | 250 ++++++++++++++++++ 2 files changed, 259 insertions(+), 4 deletions(-) create mode 100644 jdk/test/javax/swing/JFileChooser/8067660/FileChooserTest.java diff --git a/jdk/src/java.desktop/share/classes/javax/swing/filechooser/FileSystemView.java b/jdk/src/java.desktop/share/classes/javax/swing/filechooser/FileSystemView.java index 54cd7c037d4..bbc35bf9c1d 100644 --- a/jdk/src/java.desktop/share/classes/javax/swing/filechooser/FileSystemView.java +++ b/jdk/src/java.desktop/share/classes/javax/swing/filechooser/FileSystemView.java @@ -663,7 +663,9 @@ public File createNewFolder(File containingDir) throws IOException { if(newFolder.exists()) { throw new IOException("Directory already exists:" + newFolder.getAbsolutePath()); } else { - newFolder.mkdirs(); + if(!newFolder.mkdirs()) { + throw new IOException(newFolder.getAbsolutePath()); + } } return newFolder; @@ -773,7 +775,9 @@ public File createNewFolder(File containingDir) throws IOException { if(newFolder.exists()) { throw new IOException("Directory already exists:" + newFolder.getAbsolutePath()); } else { - newFolder.mkdirs(); + if(!newFolder.mkdirs()) { + throw new IOException(newFolder.getAbsolutePath()); + } } return newFolder; @@ -842,9 +846,10 @@ public File createNewFolder(File containingDir) throws IOException { if(newFolder.exists()) { throw new IOException("Directory already exists:" + newFolder.getAbsolutePath()); } else { - newFolder.mkdirs(); + if(!newFolder.mkdirs()) { + throw new IOException(newFolder.getAbsolutePath()); + } } - return newFolder; } diff --git a/jdk/test/javax/swing/JFileChooser/8067660/FileChooserTest.java b/jdk/test/javax/swing/JFileChooser/8067660/FileChooserTest.java new file mode 100644 index 00000000000..acd49add15f --- /dev/null +++ b/jdk/test/javax/swing/JFileChooser/8067660/FileChooserTest.java @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8067660 + * @summary JFileChooser create new folder fails silently + * @requires (os.family == "windows") + * @run main/manual FileChooserTest + */ +import java.awt.Panel; +import java.awt.TextArea; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; + +public class FileChooserTest { + + private static boolean theTestPassed; + private static boolean testGeneratedInterrupt; + private static Thread mainThread; + private static int sleepTime = 30000; + public static JFileChooser fileChooser; + + private static void init() throws Exception { + + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + String[] instructions + = { + "1) Create a folder with read only permissions", + "2) Click on run test button.It will open a open dialog" + + " Navigate to the newly created read only folder", + "3) Click on the create new folder button in open dialog", + "4) If an error message does not pops up" + + "test failed otherwise passed.", + "5) Pressing Pass/Fail button will mark test as " + + "pass/fail and will shutdown JVM"}; + + Sysout.createDialogWithInstructions(instructions); + Sysout.printInstructions(instructions); + } + }); + } + + /** + * *************************************************** + * Standard Test Machinery Section DO NOT modify anything in this section -- + * it's a standard chunk of code which has all of the synchronisation + * necessary for the test harness. By keeping it the same in all tests, it + * is easier to read and understand someone else's test, as well as insuring + * that all tests behave correctly with the test harness. There is a section + * following this for test-defined classes + */ + public static void main(String args[]) throws Exception { + + mainThread = Thread.currentThread(); + try { + init(); + } catch (Exception ex) { + return; + } + try { + mainThread.sleep(sleepTime); + } catch (InterruptedException ex) { + Sysout.dispose(); + if (!theTestPassed && testGeneratedInterrupt) { + throw new RuntimeException("Test Failed"); + } + } + if (!testGeneratedInterrupt) { + Sysout.dispose(); + throw new RuntimeException("Test Failed"); + } + } + + public static synchronized void pass() { + theTestPassed = true; + testGeneratedInterrupt = true; + mainThread.interrupt(); + } + + public static synchronized void fail() { + theTestPassed = false; + testGeneratedInterrupt = true; + mainThread.interrupt(); + } +} + +/** + * This is part of the standard test machinery. It creates a dialog (with the + * instructions), and is the interface for sending text messages to the user. To + * print the instructions, send an array of strings to Sysout.createDialog + * WithInstructions method. Put one line of instructions per array entry. To + * display a message for the tester to see, simply call Sysout.println with the + * string to be displayed. This mimics System.out.println but works within the + * test harness as well as standalone. + */ +class Sysout { + + private static TestDialog dialog; + private static JFrame frame; + + public static void createDialogWithInstructions(String[] instructions) { + frame = new JFrame(); + dialog = new TestDialog(frame, "Instructions"); + dialog.printInstructions(instructions); + dialog.setVisible(true); + println("Any messages for the tester will display here."); + } + + public static void printInstructions(String[] instructions) { + dialog.printInstructions(instructions); + } + + public static void println(String messageIn) { + dialog.displayMessage(messageIn); + } + + public static void dispose() { + Sysout.println("Shutting down the Java process.."); + if(FileChooserTest.fileChooser != null) { + FileChooserTest.fileChooser.cancelSelection(); + } + frame.dispose(); + dialog.dispose(); + } +} + +/** + * This is part of the standard test machinery. It provides a place for the test + * instructions to be displayed, and a place for interactive messages to the + * user to be displayed. To have the test instructions displayed, see Sysout. To + * have a message to the user be displayed, see Sysout. Do not call anything in + * this dialog directly. + */ +class TestDialog extends JDialog { + + private TextArea instructionsText; + private TextArea messageText; + private int maxStringLength = 80; + private Panel buttonP = new Panel(); + private JButton run = new JButton("Run"); + private JButton passB = new JButton("Pass"); + private JButton failB = new JButton("Fail"); + + public TestDialog(JFrame frame, String name) { + super(frame, name); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + int scrollBoth = TextArea.SCROLLBARS_BOTH; + instructionsText = new TextArea("", 15, maxStringLength, scrollBoth); + add("North", instructionsText); + + messageText = new TextArea("", 5, maxStringLength, scrollBoth); + add("Center", messageText); + + buttonP.add("East", run); + buttonP.add("East", passB); + buttonP.add("West", failB); + passB.setEnabled(false); + failB.setEnabled(false); + add("South", buttonP); + + run.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent ae) { + FileChooserTest.fileChooser = new JFileChooser(); + FileChooserTest.fileChooser.showOpenDialog(null); + passB.setEnabled(true); + failB.setEnabled(true); + } + }); + + passB.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent ae) { + FileChooserTest.pass(); + } + }); + + failB.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent ae) { + FileChooserTest.fail(); + } + }); + pack(); + + setVisible(true); + } + + public void printInstructions(String[] instructions) { + instructionsText.setText(""); + + String printStr, remainingStr; + for (String instruction : instructions) { + remainingStr = instruction; + while (remainingStr.length() > 0) { + if (remainingStr.length() >= maxStringLength) { + int posOfSpace = remainingStr. + lastIndexOf(' ', maxStringLength - 1); + + if (posOfSpace <= 0) { + posOfSpace = maxStringLength - 1; + } + + printStr = remainingStr.substring(0, posOfSpace + 1); + remainingStr = remainingStr.substring(posOfSpace + 1); + } else { + printStr = remainingStr; + remainingStr = ""; + } + instructionsText.append(printStr + "\n"); + } + } + + } + + public void displayMessage(String messageIn) { + messageText.append(messageIn + "\n"); + } +} From 9c2dc1edf517c9e025972c167453594127db9a01 Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Fri, 4 Dec 2015 13:52:21 +0300 Subject: [PATCH 15/62] 8140530: Creating a VolatileImage with size 0, 0 results in no longer working g2d.drawStri Reviewed-by: flar, serb --- .../sun/awt/image/SunVolatileImage.java | 4 ++ .../native/common/java2d/x11/X11SurfaceData.c | 9 ++++ .../image/VolatileImage/VolatileImageBug.java | 52 +++++++++++++++++++ 3 files changed, 65 insertions(+) create mode 100644 jdk/test/java/awt/image/VolatileImage/VolatileImageBug.java diff --git a/jdk/src/java.desktop/share/classes/sun/awt/image/SunVolatileImage.java b/jdk/src/java.desktop/share/classes/sun/awt/image/SunVolatileImage.java index 00c8911f696..1ee5c4019f1 100644 --- a/jdk/src/java.desktop/share/classes/sun/awt/image/SunVolatileImage.java +++ b/jdk/src/java.desktop/share/classes/sun/awt/image/SunVolatileImage.java @@ -70,6 +70,10 @@ protected SunVolatileImage(Component comp, { this.comp = comp; this.graphicsConfig = graphicsConfig; + if (width <= 0 || height <= 0) { + throw new IllegalArgumentException("Width (" + width + ")" + + " and height (" + height + ") cannot be <= 0"); + } this.width = width; this.height = height; this.forcedAccelSurfaceType = accType; diff --git a/jdk/src/java.desktop/unix/native/common/java2d/x11/X11SurfaceData.c b/jdk/src/java.desktop/unix/native/common/java2d/x11/X11SurfaceData.c index d9af8ddf6e1..e6597a4723c 100644 --- a/jdk/src/java.desktop/unix/native/common/java2d/x11/X11SurfaceData.c +++ b/jdk/src/java.desktop/unix/native/common/java2d/x11/X11SurfaceData.c @@ -438,6 +438,15 @@ jboolean XShared_initSurface(JNIEnv *env, X11SDOps *xsdo, jint depth, jint width xsdo->drawable = drawable; xsdo->isPixmap = JNI_FALSE; } else { + /* + * width , height must be nonzero otherwise XCreatePixmap + * generates BadValue in error_handler + */ + if (width <= 0 || height <= 0) { + JNU_ThrowOutOfMemoryError(env, + "Can't create offscreen surface"); + return JNI_FALSE; + } xsdo->isPixmap = JNI_TRUE; /* REMIND: workaround for bug 4420220 on pgx32 boards: don't use DGA with pixmaps unless USE_DGA_PIXMAPS is set. diff --git a/jdk/test/java/awt/image/VolatileImage/VolatileImageBug.java b/jdk/test/java/awt/image/VolatileImage/VolatileImageBug.java new file mode 100644 index 00000000000..70ca2b00840 --- /dev/null +++ b/jdk/test/java/awt/image/VolatileImage/VolatileImageBug.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsEnvironment; +import java.awt.image.VolatileImage; + +/** + * @test + * @bug 8140530 + * @run main VolatileImageBug + * @summary Creating volatileimage(0,0) should throw IAE + */ +public class VolatileImageBug { + public static void main(String[] args) { + + boolean iaeThrown = false; + GraphicsEnvironment ge = GraphicsEnvironment. + getLocalGraphicsEnvironment(); + GraphicsConfiguration gc = ge.getDefaultScreenDevice(). + getDefaultConfiguration(); + try { + VolatileImage volatileImage = gc.createCompatibleVolatileImage(0, 0); + } catch (IllegalArgumentException iae) { + iaeThrown = true; + } + if (!iaeThrown) { + throw new RuntimeException ("IllegalArgumentException not thrown " + + "for createCompatibleVolatileImage(0,0)"); + } + } +} + From cdb4a75a00102ecf64d363b495b28499f7c3588c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laurent=20Bourg=C3=A8s?= Date: Sat, 5 Dec 2015 09:48:43 -0800 Subject: [PATCH 16/62] 8144630: Use PrivilegedAction to create Thread in Marlin RendererStats Reviewed-by: prr, flar --- .../sun/java2d/marlin/RendererStats.java | 42 ++++++++++++------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/RendererStats.java b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/RendererStats.java index 6ddb5253372..4588bf270b3 100644 --- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/RendererStats.java +++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/RendererStats.java @@ -25,6 +25,8 @@ package sun.java2d.marlin; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ConcurrentLinkedQueue; @@ -32,6 +34,7 @@ import sun.java2d.marlin.stats.Histogram; import sun.java2d.marlin.stats.Monitor; import sun.java2d.marlin.stats.StatLong; +import sun.awt.util.ThreadGroupUtils; /** * This class gathers global rendering statistics for debugging purposes only @@ -237,22 +240,33 @@ public static void dumpStats() { private RendererStats() { super(); - Runtime.getRuntime().addShutdownHook(new Thread() { - @Override - public void run() { - dump(); - } - }); + AccessController.doPrivileged( + (PrivilegedAction) () -> { + final Thread hook = new Thread( + ThreadGroupUtils.getRootThreadGroup(), + new Runnable() { + @Override + public void run() { + dump(); + } + }, + "MarlinStatsHook" + ); + hook.setContextClassLoader(null); + Runtime.getRuntime().addShutdownHook(hook); - if (useDumpThread) { - final Timer statTimer = new Timer("RendererStats"); - statTimer.scheduleAtFixedRate(new TimerTask() { - @Override - public void run() { - dump(); + if (useDumpThread) { + final Timer statTimer = new Timer("RendererStats"); + statTimer.scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + dump(); + } + }, statDump, statDump); } - }, statDump, statDump); - } + return null; + } + ); } void dump() { From 4faa0ee15c1a16d6cbe6cef90c89640013510050 Mon Sep 17 00:00:00 2001 From: Joe Darcy Date: Mon, 7 Dec 2015 14:12:58 -0800 Subject: [PATCH 17/62] 8144880: Instrument intermittently failing test ConfigChanges.java Reviewed-by: lancea, martin --- jdk/test/TEST.groups | 2 ++ .../util/concurrent/ThreadPoolExecutor/ConfigChanges.java | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/jdk/test/TEST.groups b/jdk/test/TEST.groups index 0600a4c8345..769d3f91e30 100644 --- a/jdk/test/TEST.groups +++ b/jdk/test/TEST.groups @@ -32,6 +32,7 @@ tier1 = \ :jdk_util \ -java/util/WeakHashMap/GCDuringIteration.java \ -java/util/concurrent/Phaser/Basic.java \ + -java/util/concurrent/ThreadPoolExecutor/ConfigChanges.java sun/nio/cs/ISO8859x.java \ java/nio/Buffer \ com/sun/crypto/provider/Cipher \ @@ -42,6 +43,7 @@ tier2 = \ java/util/zip/TestLocalTime.java \ java/util/concurrent/Phaser/Basic.java \ java/util/WeakHashMap/GCDuringIteration.java \ + java/util/concurrent/ThreadPoolExecutor/ConfigChanges.java \ :jdk_io \ :jdk_nio \ -sun/nio/cs/ISO8859x.java \ diff --git a/jdk/test/java/util/concurrent/ThreadPoolExecutor/ConfigChanges.java b/jdk/test/java/util/concurrent/ThreadPoolExecutor/ConfigChanges.java index 2418acc18c5..a7ff015bc77 100644 --- a/jdk/test/java/util/concurrent/ThreadPoolExecutor/ConfigChanges.java +++ b/jdk/test/java/util/concurrent/ThreadPoolExecutor/ConfigChanges.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 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 @@ -25,7 +25,10 @@ * @test * @bug 6450200 * @summary Test proper handling of pool state changes + * @library /lib/testlibrary/ + * @build jdk.testlibrary.RandomFactory * @run main/othervm ConfigChanges + * @key randomness intermittent * @author Martin Buchholz */ @@ -42,11 +45,12 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.atomic.AtomicInteger; +import jdk.testlibrary.RandomFactory; public class ConfigChanges { static final ThreadGroup tg = new ThreadGroup("pool"); - static final Random rnd = new Random(); + static final Random rnd = RandomFactory.getRandom(); static void report(ThreadPoolExecutor tpe) { try { From 307b9775e8e3ec16f085d3f37466584aa43780e9 Mon Sep 17 00:00:00 2001 From: Xue-Lei Andrew Fan Date: Tue, 8 Dec 2015 03:49:12 +0000 Subject: [PATCH 18/62] 8144890: Add the intermittent keyword test B6216082.java Reviewed-by: mullan --- .../protocol/https/HttpsURLConnection/B6216082.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/jdk/test/sun/net/www/protocol/https/HttpsURLConnection/B6216082.java b/jdk/test/sun/net/www/protocol/https/HttpsURLConnection/B6216082.java index 33af848f466..72bae7e4ed5 100644 --- a/jdk/test/sun/net/www/protocol/https/HttpsURLConnection/B6216082.java +++ b/jdk/test/sun/net/www/protocol/https/HttpsURLConnection/B6216082.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -21,15 +21,20 @@ * questions. */ +// +// SunJSSE does not support dynamic system properties, no way to re-use +// system properties in samevm/agentvm mode. +// + /* * @test * @bug 6216082 * @summary Redirect problem with HttpsURLConnection using a proxy - * SunJSSE does not support dynamic system properties, no way to re-use - * system properties in samevm/agentvm mode. * @modules java.base/sun.net.www * @library .. - * @build HttpCallback TestHttpsServer ClosedChannelList HttpTransaction TunnelProxy + * @build HttpCallback TestHttpsServer ClosedChannelList + * HttpTransaction TunnelProxy + * @key intermittent * @run main/othervm B6216082 */ From 4d646ce63350d2c0e29069159adc93c95736aa69 Mon Sep 17 00:00:00 2001 From: Athijegannathan Sundararajan Date: Tue, 8 Dec 2015 10:13:57 +0530 Subject: [PATCH 19/62] 8143404: Remove apple script engine code in jdk repository Reviewed-by: alanb, mchung --- jdk/make/lib/Lib-jdk.deploy.osx.gmk | 26 - .../apple/applescript/AppleScriptEngine.java | 389 --------- .../applescript/AppleScriptEngineFactory.java | 244 ------ .../AS_NS_ConversionUtils.h | 38 - .../AS_NS_ConversionUtils.m | 793 ------------------ .../libapplescriptengine/AppleScriptEngine.m | 199 ----- .../AppleScriptExecutionContext.h | 46 - .../AppleScriptExecutionContext.m | 165 ---- .../NS_Java_ConversionUtils.h | 33 - .../NS_Java_ConversionUtils.m | 145 ---- 10 files changed, 2078 deletions(-) delete mode 100644 jdk/src/jdk.deploy.osx/macosx/classes/apple/applescript/AppleScriptEngine.java delete mode 100644 jdk/src/jdk.deploy.osx/macosx/classes/apple/applescript/AppleScriptEngineFactory.java delete mode 100644 jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AS_NS_ConversionUtils.h delete mode 100644 jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AS_NS_ConversionUtils.m delete mode 100644 jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AppleScriptEngine.m delete mode 100644 jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AppleScriptExecutionContext.h delete mode 100644 jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AppleScriptExecutionContext.m delete mode 100644 jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/NS_Java_ConversionUtils.h delete mode 100644 jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/NS_Java_ConversionUtils.m diff --git a/jdk/make/lib/Lib-jdk.deploy.osx.gmk b/jdk/make/lib/Lib-jdk.deploy.osx.gmk index a5c0eb1d41f..08125ce9d23 100644 --- a/jdk/make/lib/Lib-jdk.deploy.osx.gmk +++ b/jdk/make/lib/Lib-jdk.deploy.osx.gmk @@ -29,32 +29,6 @@ ifeq ($(OPENJDK_TARGET_OS), macosx) ################################################################################ - LIBAPPLESCRIPTENGINE_SRC := $(JDK_TOPDIR)/src/jdk.deploy.osx/macosx/native/libapplescriptengine - - $(eval $(call SetupNativeCompilation,BUILD_LIBAPPLESCRIPTENGINE, \ - LIBRARY := AppleScriptEngine, \ - OUTPUT_DIR := $(INSTALL_LIBRARIES_HERE), \ - SRC := $(LIBAPPLESCRIPTENGINE_SRC), \ - OPTIMIZATION := LOW, \ - CFLAGS := $(CFLAGS_JDKLIB) \ - -I$(LIBAPPLESCRIPTENGINE_SRC) \ - -I$(SUPPORT_OUTPUTDIR)/headers/jdk.deploy.osx, \ - DISABLED_WARNINGS_clang := implicit-function-declaration format, \ - LDFLAGS := $(LDFLAGS_JDKLIB) \ - $(call SET_SHARED_LIBRARY_ORIGIN), \ - LIBS := -framework Cocoa \ - -framework Carbon \ - -framework JavaNativeFoundation \ - $(JDKLIB_LIBS), \ - OBJECT_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libAppleScriptEngine, \ - DEBUG_SYMBOLS := $(DEBUG_ALL_BINARIES))) - - $(BUILD_LIBAPPLESCRIPTENGINE): $(call FindLib, java.base, java) - - TARGETS += $(BUILD_LIBAPPLESCRIPTENGINE) - - ################################################################################ - LIBOSX_DIRS := $(JDK_TOPDIR)/src/jdk.deploy.osx/macosx/native/libosx LIBOSX_CFLAGS := -I$(LIBOSX_DIRS) \ -I$(JDK_TOPDIR)/src/java.desktop/macosx/native/libosxapp \ diff --git a/jdk/src/jdk.deploy.osx/macosx/classes/apple/applescript/AppleScriptEngine.java b/jdk/src/jdk.deploy.osx/macosx/classes/apple/applescript/AppleScriptEngine.java deleted file mode 100644 index 8ef8116e744..00000000000 --- a/jdk/src/jdk.deploy.osx/macosx/classes/apple/applescript/AppleScriptEngine.java +++ /dev/null @@ -1,389 +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 apple.applescript; - -import java.io.*; -import java.nio.file.Files; -import java.util.*; -import java.util.Map.Entry; - -import javax.script.*; - -/** - * AppleScriptEngine implements JSR 223 for AppleScript on Mac OS X - */ -public class AppleScriptEngine implements ScriptEngine { - private static native void initNative(); - - private static native long createContextFrom(final Object object); - private static native Object createObjectFrom(final long context); - private static native void disposeContext(final long context); - - private static native long evalScript(final String script, long contextptr); - private static native long evalScriptFromURL(final String filename, long contextptr); - - static { - System.loadLibrary("AppleScriptEngine"); - initNative(); - TRACE(""); - } - - static void checkSecurity() { - final SecurityManager securityManager = System.getSecurityManager(); - if (securityManager != null) securityManager.checkExec("/usr/bin/osascript"); - } - - static void TRACE(final String str) { -// System.out.println(AppleScriptEngine.class.getName() + "." + str); - } - - /** - * Accessor for the ScriptEngine's long name variable - * @return the long name of the ScriptEngine - */ - protected static String getEngine() { - TRACE("getEngine()"); - return AppleScriptEngineFactory.ENGINE_NAME; - } - - /** - * Accessor for the ScriptEngine's version - * @return the version of the ScriptEngine - */ - protected static String getEngineVersion() { - TRACE("getEngineVersion()"); - return AppleScriptEngineFactory.ENGINE_VERSION; - } - - /** - * Accessor for the ScriptEngine's short name - * @return the short name of the ScriptEngine - */ - protected static String getName() { - TRACE("getName()"); - return AppleScriptEngineFactory.ENGINE_SHORT_NAME; - } - - /** - * Accessor for the ScriptEngine's supported language name - * @return the language the ScriptEngine supports - */ - protected static String getLanguage() { - TRACE("getLanguage()"); - return AppleScriptEngineFactory.LANGUAGE; - } - - /** - * The no argument constructor sets up the object with default members, - * a factory for the engine and a fresh context. - * @see com.apple.applescript.AppleScriptEngine#init() - */ - public AppleScriptEngine() { - TRACE("()"); - // set our parent factory to be a new factory - factory = AppleScriptEngineFactory.getFactory(); - - // set up our noarg bindings - setContext(new SimpleScriptContext()); - put(ARGV, ""); - - init(); - } - - /** - * All AppleScriptEngines share the same ScriptEngineFactory - */ - private final ScriptEngineFactory factory; - - /** - * The local context for the AppleScriptEngine - */ - private ScriptContext context; - - /** - * The constructor taking a factory as an argument sets the parent factory for - * this engine to be the passed factory, and sets up the engine with a fresh context - * @param factory - * @see com.apple.applescript.AppleScriptEngine#init() - */ - public AppleScriptEngine(final ScriptEngineFactory factory) { - // inherit the factory passed to us - this.factory = factory; - - // set up our noarg bindings - setContext(new SimpleScriptContext()); - put(ARGV, ""); - - init(); - } - - /** - * The initializer populates the local context with some useful predefined variables: - *
  • javax_script_language_version - the version of AppleScript that the AppleScriptEngine supports.
  • - *
  • javax_script_language - "AppleScript" -- the language supported by the AppleScriptEngine.
  • - *
  • javax_script_engine - "AppleScriptEngine" -- the name of the ScriptEngine.
  • - *
  • javax_script_engine_version - the version of the AppleScriptEngine
  • - *
  • javax_script_argv - "" -- AppleScript does not take arguments from the command line
  • - *
  • javax_script_filename - "" -- the currently executing filename
  • - *
  • javax_script_name - "AppleScriptEngine" -- the short name of the AppleScriptEngine
  • - *
  • THREADING - null -- the AppleScriptEngine does not support concurrency, you will have to implement thread-safeness yourself.
- */ - private void init() { - TRACE("init()"); - // set up our context -/* TODO -- name of current executable? bad java documentation at: - * http://docs.oracle.com/javase/6/docs/api/javax/script/ScriptEngine.html#FILENAME */ - put(ScriptEngine.FILENAME, ""); - put(ScriptEngine.ENGINE, getEngine()); - put(ScriptEngine.ENGINE_VERSION, getEngineVersion()); - put(ScriptEngine.NAME, getName()); - put(ScriptEngine.LANGUAGE, getLanguage()); - put(ScriptEngine.LANGUAGE_VERSION, getLanguageVersion()); - - // TODO -- for now, err on the side of caution and say that we are NOT thread-safe - put("THREADING", null); - } - - /** - * Uses the AppleScriptEngine to get the local AppleScript version - * @return the version of AppleScript running on the system - */ - protected String getLanguageVersion() { - TRACE("AppleScriptEngine.getLanguageVersion()"); - try { - final Object result = eval("get the version of AppleScript"); - if (result instanceof String) return (String)result; - } catch (final ScriptException e) { e.printStackTrace(); } - return "unknown"; - } - - /** - * Implementation required by ScriptEngine parent
- * Returns the factory parent of this AppleScriptEngine - */ - public ScriptEngineFactory getFactory() { - return factory; - } - - /** - * Implementation required by ScriptEngine parent
- * Return the engine's context - * @return this ScriptEngine's context - */ - public ScriptContext getContext() { - return context; - } - - /** - * Implementation required by ScriptEngine parent
- * Set a new context for the engine - * @param context the new context to install in the engine - */ - public void setContext(final ScriptContext context) { - this.context = context; - } - - /** - * Implementation required by ScriptEngine parent
- * Create and return a new set of simple bindings. - * @return a new and empty set of bindings - */ - public Bindings createBindings() { - return new SimpleBindings(); - } - - /** - * Implementation required by ScriptEngine parent
- * Return the engines bindings for the context indicated by the argument. - * @param scope contextual scope to return. - * @return the bindings in the engine for the scope indicated by the parameter - */ - public Bindings getBindings(final int scope) { - return context.getBindings(scope); - } - - /** - * Implementation required by ScriptEngine parent
- * Sets the bindings for the indicated scope - * @param bindings a set of bindings to assign to the engine - * @param scope the scope that the passed bindings should be assigned to - */ - public void setBindings(final Bindings bindings, final int scope) { - context.setBindings(bindings, scope); - } - - /** - * Implementation required by ScriptEngine parent
- * Insert a key and value into the engine's bindings (scope: engine) - * @param key the key of the pair - * @param value the value of the pair - */ - public void put(final String key, final Object value) { - getBindings(ScriptContext.ENGINE_SCOPE).put(key, value); - } - - /** - * Implementation required by ScriptEngine parent
- * Get a value from the engine's bindings using a key (scope: engine) - * @param key the key of the pair - * @return the value of the pair - */ - public Object get(final String key) { - return getBindings(ScriptContext.ENGINE_SCOPE).get(key); - } - - /** - * Implementation required by ScriptEngine parent
- * Passes the Reader argument, as well as the engine's context to a lower evaluation function.
- * Prefers FileReader or BufferedReader wrapping FileReader as argument. - * @param reader a Reader to AppleScript source or compiled AppleScript - * @return an Object corresponding to the return value of the script - * @see com.apple.applescript.AppleScriptEngine#eval(Reader, ScriptContext) - */ - public Object eval(final Reader reader) throws ScriptException { - return eval(reader, getContext()); - } - - /** - * Implementation required by ScriptEngine parent
- * Uses the passed bindings as the context for executing the passed script. - * @param reader a stream to AppleScript source or compiled AppleScript - * @param bindings a Bindings object representing the contexts to execute inside - * @return the return value of the script - * @see com.apple.applescript.AppleScriptEngine#eval(Reader, ScriptContext) - */ - public Object eval(final Reader reader, final Bindings bindings) throws ScriptException { - final Bindings tmp = getContext().getBindings(ScriptContext.ENGINE_SCOPE); - getContext().setBindings(bindings, ScriptContext.ENGINE_SCOPE); - final Object retval = eval(reader); - getContext().setBindings(tmp, ScriptContext.ENGINE_SCOPE); - return retval; - } - - /** - * Implementation required by ScriptEngine parent
- * This function can execute either AppleScript source or compiled AppleScript and functions by writing the - * contents of the Reader to a temporary file and then executing it with the engine's context. - * @param reader - * @param scriptContext - * @return an Object corresponding to the return value of the script - */ - public Object eval(final Reader reader, final ScriptContext context) throws ScriptException { - checkSecurity(); - - // write our passed reader to a temporary file - File tmpfile; - FileWriter tmpwrite; - try { - tmpfile = Files.createTempFile("AppleScriptEngine.", ".scpt").toFile(); - tmpwrite = new FileWriter(tmpfile); - - // read in our input and write directly to tmpfile - /* TODO -- this may or may not be avoidable for certain Readers, - * if a filename can be grabbed, it would be faster to get that and - * use the underlying file than writing a temp file. - */ - int data; - while ((data = reader.read()) != -1) { - tmpwrite.write(data); - } - tmpwrite.close(); - - // set up our context business - final long contextptr = scriptContextToNSDictionary(context); - try { - final long retCtx = evalScriptFromURL("file://" + tmpfile.getCanonicalPath(), contextptr); - Object retVal = (retCtx == 0) ? null : createObjectFrom(retCtx); - disposeContext(retCtx); - return retVal; - } finally { - disposeContext(contextptr); - tmpfile.delete(); - } - } catch (final IOException e) { - throw new ScriptException(e); - } - } - - /** - * Implementation required by ScriptEngine parent
- * Evaluate an AppleScript script passed as a source string. Using the engine's built in context. - * @param script the string to execute. - * @return an Object representing the return value of the script - * @see com.apple.applescript.AppleScriptEngine#eval(String, ScriptContext) - */ - public Object eval(final String script) throws ScriptException { - return eval(script, getContext()); - } - - /** - * Implementation required by ScriptEngine parent
- * Evaluate an AppleScript script passed as a source string with a custom ScriptContext. - * @param script the AppleScript source to compile and execute. - * @param scriptContext the context to execute the script under - * @see com.apple.applescript.AppleScriptEngine#eval(String, ScriptContext) - */ - public Object eval(final String script, final Bindings bindings) throws ScriptException { - final Bindings tmp = getContext().getBindings(ScriptContext.ENGINE_SCOPE); - getContext().setBindings(bindings, ScriptContext.ENGINE_SCOPE); - - final Object retval = eval(script); - getContext().setBindings(tmp, ScriptContext.ENGINE_SCOPE); - - return retval; - } - - /** - * Implementation required by ScriptEngine parent - * @param script - * @param scriptContext - */ - public Object eval(final String script, final ScriptContext context) throws ScriptException { - checkSecurity(); - final long ctxPtr = scriptContextToNSDictionary(context); - try { - final long retCtx = evalScript(script, ctxPtr); - Object retVal = (retCtx == 0) ? null : createObjectFrom(retCtx); - disposeContext(retCtx); - return retVal; - } finally { - disposeContext(ctxPtr); - } - } - - /** - * Converts a ScriptContext into an NSDictionary - * @param context ScriptContext for the engine - * @return a pointer to an NSDictionary - */ - private long scriptContextToNSDictionary(final ScriptContext context) throws ScriptException { - final Map contextAsMap = new HashMap(); - for (final Entry e : context.getBindings(ScriptContext.ENGINE_SCOPE).entrySet()) { - contextAsMap.put(e.getKey().replaceAll("\\.", "_"), e.getValue()); - } - return createContextFrom(contextAsMap); - } -} diff --git a/jdk/src/jdk.deploy.osx/macosx/classes/apple/applescript/AppleScriptEngineFactory.java b/jdk/src/jdk.deploy.osx/macosx/classes/apple/applescript/AppleScriptEngineFactory.java deleted file mode 100644 index 8c50b9d9301..00000000000 --- a/jdk/src/jdk.deploy.osx/macosx/classes/apple/applescript/AppleScriptEngineFactory.java +++ /dev/null @@ -1,244 +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 apple.applescript; - -import java.security.*; -import java.util.*; -import javax.script.*; - -public class AppleScriptEngineFactory implements ScriptEngineFactory { - private static volatile boolean initialized = false; - - private static native void initNative(); - - static void TRACE(final String str) { -// System.out.println(AppleScriptEngineFactory.class.getName() + "." + str); - } - - /** - * The name of this ScriptEngine - */ - static final String ENGINE_NAME = "AppleScriptEngine"; - - /** - * The version of this ScriptEngine - */ - static final String ENGINE_VERSION = "1.1"; - - /** - * The name of this ScriptEngine (yes, again) - */ - static final String ENGINE_SHORT_NAME = ENGINE_NAME; - - /** - * The name of the language supported by this ScriptEngine - */ - static final String LANGUAGE = "AppleScript"; - - static ScriptEngineFactory getFactory() { - TRACE("getFactory()"); - return new AppleScriptEngineFactory(); - } - - /** - * Initialize a new AppleScriptEngineFactory, replete with a member AppleScriptEngine - */ - public AppleScriptEngineFactory() { - TRACE("()"); - } - - /** - * Returns the full name of the ScriptEngine. - * - * @return full name of the ScriptEngine - */ - @Override - public String getEngineName() { - TRACE("getEngineName()"); - return ENGINE_NAME; - } - - /** - * Returns the version of the ScriptEngine. - * - * @return version of the ScriptEngine - */ - @Override - public String getEngineVersion() { - TRACE("getEngineVersion()"); - return ENGINE_VERSION; - } - - /** - * Returns the name of the scripting language supported by this ScriptEngine. - * - * @return name of the language supported by the ScriptEngine(Factory) - */ - @Override - public String getLanguageName() { - TRACE("getLanguageName()"); - return LANGUAGE; - } - - /** - * Returns the version of the scripting language supported by this ScriptEngine(Factory). - * - * @return language version supported by the ScriptEngine(Factory) - */ - @Override - public String getLanguageVersion() { - TRACE("getLanguageVersion()"); - return AccessController.doPrivileged(new PrivilegedAction() { - public String run() { - final AppleScriptEngine engine = getScriptEngine(); - return engine.getLanguageVersion(); - } - }); - } - - /** - * Returns an immutable list of filename extensions, which generally identify - * scripts written in the language supported by this ScriptEngine. - * - * @return ArrayList of file extensions AppleScript associates with - */ - @Override - public List getExtensions() { - TRACE("getExtensions()"); - return Arrays.asList("scpt", "applescript", "app"); - } - - /** - * Returns an immutable list of mimetypes, associated with scripts - * that can be executed by the engine. - * - * @return ArrayList of mimetypes that AppleScript associates with - */ - @Override - public List getMimeTypes() { - TRACE("getMimeTypes()"); - return Arrays.asList("application/x-applescript", "text/plain", "text/applescript"); - } - - /** - * Returns an immutable list of short names for the ScriptEngine, - * which may be used to identify the ScriptEngine by the ScriptEngineManager. - * - * @return - */ - @Override - public List getNames() { - TRACE("getNames()"); - return Arrays.asList("AppleScriptEngine", "AppleScript", "OSA"); - } - - /** - * Returns a String which can be used to invoke a method of a Java - * object using the syntax of the supported scripting language. - * - * @param obj - * unused -- AppleScript does not support objects - * @param m - * function name - * @param args - * arguments to the function - * @return the AppleScript string calling the method - */ - @Override - public String getMethodCallSyntax(final String obj, final String fname, final String ... args) { -// StringBuilder builder = new StringBuilder(); -// builder.append("my " + fname + "("); -// // TODO -- do -// builder.append(")\n"); -// return builder.toString(); - - return null; - } - - /** - * Returns a String that can be used as a statement to display the specified String using the syntax of the supported scripting language. - * - * @param toDisplay - * @return - */ - @Override - public String getOutputStatement(final String toDisplay) { - // TODO -- this might even be good enough? XD - return getMethodCallSyntax(null, "print", toDisplay); - } - - /** - * Returns the value of an attribute whose meaning may be implementation-specific. - * - * @param key - * the key to look up - * @return the static preseeded value for the key in the ScriptEngine, if it exists, otherwise null - */ - @Override - public Object getParameter(final String key) { - final AppleScriptEngine engine = getScriptEngine(); - if (!engine.getBindings(ScriptContext.ENGINE_SCOPE).containsKey(key)) return null; - return engine.getBindings(ScriptContext.ENGINE_SCOPE).get(key); - } - - /** - * Returns A valid scripting language executable program with given statements. - * - * @param statements - * @return - */ - @Override - public String getProgram(final String ... statements) { - final StringBuilder program = new StringBuilder(); - for (final String statement : statements) { - program.append(statement + "\n"); - } - return program.toString(); - } - - /** - * Returns an instance of the ScriptEngine associated with this ScriptEngineFactory. - * - * @return new AppleScriptEngine with this factory as it's parent - */ - @Override - public AppleScriptEngine getScriptEngine() { - AppleScriptEngine.checkSecurity(); - ensureInitialized(); - - return new AppleScriptEngine(this); - } - - private static synchronized void ensureInitialized() { - if (!initialized) { - initialized = true; - - java.awt.Toolkit.getDefaultToolkit(); - System.loadLibrary("AppleScriptEngine"); - initNative(); - } - } -} diff --git a/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AS_NS_ConversionUtils.h b/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AS_NS_ConversionUtils.h deleted file mode 100644 index 3b428d222d7..00000000000 --- a/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AS_NS_ConversionUtils.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#import - - -// A 'collection' (responds to -objectEnumerator) is translated to an AS list. -// For any other object obj, this returns [[obj description] aeDescriptorValue], mainly -// intended for debugging purposes. -@interface NSObject (JavaAppleScriptEngineAdditions) -- (NSAppleEventDescriptor *) aeDescriptorValue; -@end - -@interface NSAppleEventDescriptor (JavaAppleScriptEngineAdditions) -- (id) objCObjectValue; -@end diff --git a/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AS_NS_ConversionUtils.m b/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AS_NS_ConversionUtils.m deleted file mode 100644 index 42c0da59d5e..00000000000 --- a/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AS_NS_ConversionUtils.m +++ /dev/null @@ -1,793 +0,0 @@ -/* - * Copyright (c) 2011, 2012, 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. - */ - -// -// Most of this is adapted from Ken Ferry's KFAppleScript Additions, contributed with permission -// http://homepage.mac.com/kenferry/software.html -// - -#import "AS_NS_ConversionUtils.h" - -#import -#import - - -@interface NSAppleEventDescriptor (JavaAppleScriptEngineAdditionsPrivate) - -// just returns self. This means that you can pass custom descriptors -// to -[NSAppleScript executeHandler:error:withParameters:]. -- (NSAppleEventDescriptor *)aeDescriptorValue; - -// working with primitive descriptor types -+ (id)descriptorWithInt16:(SInt16)val; -- (SInt16)int16Value; -+ (id)descriptorWithUnsignedInt32:(UInt32)val; -- (UInt32)unsignedInt32Value; -+ (id)descriptorWithFloat32:(Float32)val; -- (Float32)float32Value; -+ (id)descriptorWithFloat64:(Float64)val; -- (Float64)float64Value; -+ (id)descriptorWithLongDateTime:(LongDateTime)val; -- (LongDateTime)longDateTimeValue; - - -// These are the methods for converting AS objects to objective-C objects. -// -[NSAppleEventDescriptor objCObjectValue] is the general method for converting -// AS objects to ObjC objects, and is called by -[NSAppleScript executeHandler:error:withParameters:]. -// It does no work itself. It finds a handler based on the type of the descriptor and lets that -// handler object do the work. If there is no handler type registered for a the type of a descriptor, -// the raw descriptor is returned. -// -// You can designate a handlers for descriptor types with -// +[NSAppleEventDescriptor registerConversionHandler:selector:forDescriptorTypes:]. Please note -// that this method does _not_ retain the handler object (for now anyway). The selector should -// take a single argument, a descriptor to translate, and should return an object. An example such -// selector is @selector(dictionaryWithAEDesc:), for which the handler object would be [NSDictionary class]. -// -// A number of handlers are designated by default. The methods and objects can be easily inferred (or check -// the implementation), but the automatically handled types are -// typeUnicodeText, -// typeText, -// typeUTF8Text, -// typeCString, -// typeChar, -// typeBoolean, -// typeTrue, -// typeFalse, -// typeSInt16, -// typeSInt32, -// typeUInt32, -// typeSInt64, -// typeIEEE32BitFloatingPoint, -// typeIEEE64BitFloatingPoint, -// type128BitFloatingPoint, -// typeAEList, -// typeAERecord, -// typeLongDateTime, -// typeNull. -+ (void)registerConversionHandler:(id)anObject selector:(SEL)aSelector forDescriptorTypes:(DescType)firstType, ...; -+ (void) jaseSetUpHandlerDict; -@end - -// wrap the NSAppleEventDescriptor string methods -@interface NSString (JavaAppleScriptEngineAdditions) -- (NSAppleEventDescriptor *)aeDescriptorValue; -+ (NSString *)stringWithAEDesc:(NSAppleEventDescriptor *)desc; -@end - -// wrap the NSAppleEventDescriptor longDateTime methods -@interface NSDate (JavaAppleScriptEngineAdditions) -- (NSAppleEventDescriptor *)aeDescriptorValue; -+ (NSDate *)dateWithAEDesc:(NSAppleEventDescriptor *)desc; -@end - -// these are fairly complicated methods, due to having to try to match up the various -// AS number types (see NSAppleEventDescriptor for the primitive number methods) -// with NSNumber variants. For complete behavior it's best to look at the implementation. -// Some notes: -// NSNumbers created with numberWithBool should be correctly translated to AS booleans and vice versa. -// NSNumbers created with large integer types may have to be translated to AS doubles, -// so be careful if checking equality (you may have to check equality within epsilon). -// Since NSNumbers can't remember if they were created with an unsigned value, -// [[NSNumber numberWithUnsignedChar:255] aeDescriptorValue] is going to get you an AS integer -// with value -1. If you really need a descriptor with an unsigned value, you'll need to do it -// manually using the primitive methods on NSAppleEventDescriptor. The resulting descriptor -// can still be passed to AS with -[NSAppleScript executeHandler:error:withParameters:]. -@interface NSNumber (JavaAppleScriptEngineAdditions) -- (NSAppleEventDescriptor *)aeDescriptorValue; -+ (id)numberWithAEDesc:(NSAppleEventDescriptor *)desc; -@end - -// Here we're following the behavior described in the CocoaScripting release note. -// -// NSPoint -> list of two numbers: {x, y} -// NSRange -> list of two numbers: {begin offset, end offset} -// NSRect -> list of four numbers: {left, bottom, right, top} -// NSSize -> list of two numbers: {width, height} -@interface NSValue (JavaAppleScriptEngineAdditions) -- (NSAppleEventDescriptor *)aeDescriptorValue; -@end - -// No need for ObjC -> AS conversion here, we fall through to NSObject as a collection. -// For AS -> ObjC conversion, we build an array using the primitive list methods on -// NSAppleEventDescriptor. -@interface NSArray (JavaAppleScriptEngineAdditions) -+ (NSArray *)arrayWithAEDesc:(NSAppleEventDescriptor *)desc; -@end - - -// Please see the CocoaScripting release note for behavior. It's kind of complicated. -// -// methods wrap the primitive record methods on NSAppleEventDescriptor. -@interface NSDictionary (JavaAppleScriptEngineAdditions) -- (NSAppleEventDescriptor *)aeDescriptorValue; -+ (NSDictionary *)dictionaryWithAEDesc:(NSAppleEventDescriptor *)desc; -@end - -// be aware that a null descriptor does not correspond to the 'null' keyword in -// AppleScript - it's more like nothing at all. For example, the return -// from an empty handler. -@interface NSNull (JavaAppleScriptEngineAdditions) -- (NSAppleEventDescriptor *)aeDescriptorValue; -+ (NSNull *)nullWithAEDesc:(NSAppleEventDescriptor *)desc; -@end - - -@interface NSNumber (JavaAppleScriptEngineAdditionsPrivate) -+ (id) jaseNumberWithSignedIntP:(void *)int_p byteCount:(int)bytes; -+ (id) jaseNumberWithUnsignedIntP:(void *)int_p byteCount:(int)bytes; -+ (id) jaseNumberWithFloatP:(void *)float_p byteCount:(int)bytes; -@end - - -@implementation NSObject (JavaAppleScriptEngineAdditions) - -- (NSAppleEventDescriptor *)aeDescriptorValue { - // collections go to lists - if (![self respondsToSelector:@selector(objectEnumerator)]) { - // encode the description as a fallback - this is pretty useless, only helpful for debugging - return [[self description] aeDescriptorValue]; - } - - NSAppleEventDescriptor *resultDesc = [NSAppleEventDescriptor listDescriptor]; - NSEnumerator *objectEnumerator = [(id)self objectEnumerator]; - - unsigned int i = 1; // apple event descriptors are 1-indexed - id currentObject; - while((currentObject = [objectEnumerator nextObject]) != nil) { - [resultDesc insertDescriptor:[currentObject aeDescriptorValue] atIndex:i++]; - } - - return resultDesc; -} - -@end - - -@implementation NSArray (JavaAppleScriptEngineAdditions) - -// don't need to override aeDescriptorValue, the NSObject will treat the array as a collection -+ (NSArray *)arrayWithAEDesc:(NSAppleEventDescriptor *)desc { - NSAppleEventDescriptor *listDesc = [desc coerceToDescriptorType:typeAEList]; - NSMutableArray *resultArray = [NSMutableArray array]; - - // apple event descriptors are 1-indexed - unsigned int listCount = [listDesc numberOfItems]; - unsigned int i; - for (i = 1; i <= listCount; i++) { - [resultArray addObject:[[listDesc descriptorAtIndex:i] objCObjectValue]]; - } - - return resultArray; -} - -@end - - -@implementation NSDictionary (JavaAppleScriptEngineAdditions) - -- (NSAppleEventDescriptor *)aeDescriptorValue { - NSAppleEventDescriptor *resultDesc = [NSAppleEventDescriptor recordDescriptor]; - NSMutableArray *userFields = [NSMutableArray array]; - NSArray *keys = [self allKeys]; - - unsigned int keyCount = [keys count]; - unsigned int i; - for (i = 0; i < keyCount; i++) { - id key = [keys objectAtIndex:i]; - - if ([key isKindOfClass:[NSNumber class]]) { - [resultDesc setDescriptor:[[self objectForKey:key] aeDescriptorValue] forKeyword:[(NSNumber *)key intValue]]; - } else if ([key isKindOfClass:[NSString class]]) { - [userFields addObject:key]; - [userFields addObject:[self objectForKey:key]]; - } - } - - if ([userFields count] > 0) { - [resultDesc setDescriptor:[userFields aeDescriptorValue] forKeyword:keyASUserRecordFields]; - } - - return resultDesc; -} - -+ (NSDictionary *)dictionaryWithAEDesc:(NSAppleEventDescriptor *)desc { - NSAppleEventDescriptor *recDescriptor = [desc coerceToDescriptorType:typeAERecord]; - NSMutableDictionary *resultDict = [NSMutableDictionary dictionary]; - - // NSAppleEventDescriptor uses 1 indexing - unsigned int recordCount = [recDescriptor numberOfItems]; - unsigned int recordIndex; - for (recordIndex = 1; recordIndex <= recordCount; recordIndex++) { - AEKeyword keyword = [recDescriptor keywordForDescriptorAtIndex:recordIndex]; - - if(keyword == keyASUserRecordFields) { - NSAppleEventDescriptor *listDescriptor = [recDescriptor descriptorAtIndex:recordIndex]; - - // NSAppleEventDescriptor uses 1 indexing - unsigned int listCount = [listDescriptor numberOfItems]; - unsigned int listIndex; - for (listIndex = 1; listIndex <= listCount; listIndex += 2) { - id keyObj = [[listDescriptor descriptorAtIndex:listIndex] objCObjectValue]; - id valObj = [[listDescriptor descriptorAtIndex:listIndex+1] objCObjectValue]; - - [resultDict setObject:valObj forKey:keyObj]; - } - } else { - id keyObj = [NSNumber numberWithInt:keyword]; - id valObj = [[recDescriptor descriptorAtIndex:recordIndex] objCObjectValue]; - - [resultDict setObject:valObj forKey:keyObj]; - } - } - - return resultDict; -} - -@end - - -@implementation NSString (JavaAppleScriptEngineAdditions) - -- (NSAppleEventDescriptor *)aeDescriptorValue { - return [NSAppleEventDescriptor descriptorWithString:self]; -} - -+ (NSString *)stringWithAEDesc:(NSAppleEventDescriptor *)desc { - return [desc stringValue]; -} - -+ (NSString *)versionWithAEDesc:(NSAppleEventDescriptor *)desc { - const AEDesc *aeDesc = [desc aeDesc]; - VersRec v; - AEGetDescData(aeDesc, &v, sizeof(v)); - return [[[NSString alloc] initWithBytes:&v.shortVersion[1] length:StrLength(v.shortVersion) encoding:NSUTF8StringEncoding] autorelease]; -} - -@end - - -@implementation NSNull (JavaAppleScriptEngineAdditions) - -- (NSAppleEventDescriptor *)aeDescriptorValue { - return [NSAppleEventDescriptor nullDescriptor]; -} - -+ (NSNull *)nullWithAEDesc:(NSAppleEventDescriptor *)desc { - return [NSNull null]; -} - -@end - - -@implementation NSDate (JavaAppleScriptEngineAdditions) - -- (NSAppleEventDescriptor *)aeDescriptorValue { - LongDateTime ldt; - UCConvertCFAbsoluteTimeToLongDateTime(CFDateGetAbsoluteTime((CFDateRef)self), &ldt); - return [NSAppleEventDescriptor descriptorWithLongDateTime:ldt]; -} - -+ (NSDate *)dateWithAEDesc:(NSAppleEventDescriptor *)desc { - CFAbsoluteTime absTime; - UCConvertLongDateTimeToCFAbsoluteTime([desc longDateTimeValue], &absTime); - NSDate *resultDate = (NSDate *)CFDateCreate(NULL, absTime); - return [resultDate autorelease]; -} - -@end - - - -static inline int areEqualEncodings(const char *enc1, const char *enc2) { - return (strcmp(enc1, enc2) == 0); -} - -@implementation NSNumber (JavaAppleScriptEngineAdditions) - --(id)jaseDescriptorValueWithFloatP:(void *)float_p byteCount:(int)bytes { - float floatVal; - if (bytes < sizeof(Float32)) { - floatVal = [self floatValue]; - float_p = &floatVal; - bytes = sizeof(floatVal); - } - - double doubleVal; - if (bytes > sizeof(Float64)) { - doubleVal = [self doubleValue]; - float_p = &doubleVal; - bytes = sizeof(doubleVal); - } - - if (bytes == sizeof(Float32)) { - return [NSAppleEventDescriptor descriptorWithFloat32:*(Float32 *)float_p]; - } - - if (bytes == sizeof(Float64)) { - return [NSAppleEventDescriptor descriptorWithFloat64:*(Float64 *)float_p]; - } - - [NSException raise:NSInvalidArgumentException - format:@"Cannot create an NSAppleEventDescriptor for float with %d bytes of data.", bytes]; - - return nil; -} - --(id)jaseDescriptorValueWithSignedIntP:(void *)int_p byteCount:(int)bytes { - int intVal; - - if (bytes < sizeof(SInt16)) { - intVal = [self intValue]; - int_p = &intVal; - bytes = sizeof(intVal); - } - - if (bytes == sizeof(SInt16)) { - return [NSAppleEventDescriptor descriptorWithInt16:*(SInt16 *)int_p]; - } - - if (bytes == sizeof(SInt32)) { - return [NSAppleEventDescriptor descriptorWithInt32:*(SInt32 *)int_p]; - } - - double val = [self doubleValue]; - return [self jaseDescriptorValueWithFloatP:&val byteCount:sizeof(val)]; -} - --(id)jaseDescriptorValueWithUnsignedIntP:(void *)int_p byteCount:(int)bytes { - unsigned int uIntVal; - - if (bytes < sizeof(UInt32)) { - uIntVal = [self unsignedIntValue]; - int_p = &uIntVal; - bytes = sizeof(uIntVal); - } - - if (bytes == sizeof(UInt32)) { - return [NSAppleEventDescriptor descriptorWithUnsignedInt32:*(UInt32 *)int_p]; - } - - double val = (double)[self unsignedLongLongValue]; - return [self jaseDescriptorValueWithFloatP:&val byteCount:sizeof(val)]; -} - -- (NSAppleEventDescriptor *)aeDescriptorValue { - // NSNumber is unfortunately complicated, because the applescript - // type we should use depends on the c type that our NSNumber corresponds to - - const char *type = [self objCType]; - - // convert - if (areEqualEncodings(type, @encode(BOOL))) { - return [NSAppleEventDescriptor descriptorWithBoolean:[self boolValue]]; - } - - if (areEqualEncodings(type, @encode(char))) { - char val = [self charValue]; - return [self jaseDescriptorValueWithSignedIntP:&val byteCount:sizeof(val)]; - } - - if (areEqualEncodings(type, @encode(short))) { - short val = [self shortValue]; - return [self jaseDescriptorValueWithSignedIntP:&val byteCount:sizeof(val)]; - } - - if (areEqualEncodings(type, @encode(int))) { - int val = [self intValue]; - return [self jaseDescriptorValueWithSignedIntP:&val byteCount:sizeof(val)]; - } - - if (areEqualEncodings(type, @encode(long))) { - long val = [self longValue]; - return [self jaseDescriptorValueWithSignedIntP:&val byteCount:sizeof(val)]; - } - - if (areEqualEncodings(type, @encode(long long))) { - long long val = [self longLongValue]; - return [self jaseDescriptorValueWithSignedIntP:&val byteCount:sizeof(val)]; - } - - if (areEqualEncodings(type, @encode(unsigned char))) { - unsigned char val = [self unsignedCharValue]; - return [self jaseDescriptorValueWithUnsignedIntP:&val byteCount:sizeof(val)]; - } - - if (areEqualEncodings(type, @encode(unsigned short))) { - unsigned short val = [self unsignedShortValue]; - return [self jaseDescriptorValueWithUnsignedIntP:&val byteCount:sizeof(val)]; - } - - if (areEqualEncodings(type, @encode(unsigned int))) { - unsigned int val = [self unsignedIntValue]; - return [self jaseDescriptorValueWithUnsignedIntP:&val byteCount:sizeof(val)]; - } - - if (areEqualEncodings(type, @encode(unsigned long))) { - unsigned long val = [self unsignedLongValue]; - return [self jaseDescriptorValueWithUnsignedIntP:&val byteCount:sizeof(val)]; - } - - if (areEqualEncodings(type, @encode(unsigned long long))) { - unsigned long long val = [self unsignedLongLongValue]; - return [self jaseDescriptorValueWithUnsignedIntP:&val byteCount:sizeof(val)]; - } - - if (areEqualEncodings(type, @encode(float))) { - float val = [self floatValue]; - return [self jaseDescriptorValueWithFloatP:&val byteCount:sizeof(val)]; - } - - if (areEqualEncodings(type, @encode(double))) { - double val = [self doubleValue]; - return [self jaseDescriptorValueWithFloatP:&val byteCount:sizeof(val)]; - } - - [NSException raise:@"jaseUnsupportedAEDescriptorConversion" - format:@"JavaAppleScriptEngineAdditions: conversion of an NSNumber with objCType '%s' to an aeDescriptor is not supported.", type]; - - return nil; -} - -+ (id)numberWithAEDesc:(NSAppleEventDescriptor *)desc { - DescType type = [desc descriptorType]; - - if ((type == typeTrue) || (type == typeFalse) || (type == typeBoolean)) { - return [NSNumber numberWithBool:[desc booleanValue]]; - } - - if (type == typeSInt16) { - SInt16 val = [desc int16Value]; - return [NSNumber jaseNumberWithSignedIntP:&val byteCount:sizeof(val)]; - } - - if (type == typeSInt32) { - SInt32 val = [desc int32Value]; - return [NSNumber jaseNumberWithSignedIntP:&val byteCount:sizeof(val)]; - } - - if (type == typeUInt32) { - UInt32 val = [desc unsignedInt32Value]; - return [NSNumber jaseNumberWithUnsignedIntP:&val byteCount:sizeof(val)]; - } - - if (type == typeIEEE32BitFloatingPoint) { - Float32 val = [desc float32Value]; - return [NSNumber jaseNumberWithFloatP:&val byteCount:sizeof(val)]; - } - - if (type == typeIEEE64BitFloatingPoint) { - Float64 val = [desc float64Value]; - return [NSNumber jaseNumberWithFloatP:&val byteCount:sizeof(val)]; - } - - // try to coerce to 64bit floating point - desc = [desc coerceToDescriptorType:typeIEEE64BitFloatingPoint]; - if (desc != nil) { - Float64 val = [desc float64Value]; - return [NSNumber jaseNumberWithFloatP:&val byteCount:sizeof(val)]; - } - - [NSException raise:@"jaseUnsupportedAEDescriptorConversion" - format:@"JavaAppleScriptEngineAdditions: conversion of an NSAppleEventDescriptor with objCType '%s' to an aeDescriptor is not supported.", type]; - - return nil; -} - -+ (id) jaseNumberWithSignedIntP:(void *)int_p byteCount:(int)bytes { - if (bytes == sizeof(char)) { - return [NSNumber numberWithChar:*(char *)int_p]; - } - - if (bytes == sizeof(short)) { - return [NSNumber numberWithShort:*(short *)int_p]; - } - - if (bytes == sizeof(int)) { - return [NSNumber numberWithInt:*(int *)int_p]; - } - - if (bytes == sizeof(long)) { - return [NSNumber numberWithLong:*(long *)int_p]; - } - - if (bytes == sizeof(long long)) { - return [NSNumber numberWithLongLong:*(long long *)int_p]; - } - - [NSException raise:NSInvalidArgumentException - format:@"NSNumber jaseNumberWithSignedIntP:byteCount: number with %i bytes not supported.", bytes]; - - return nil; -} - -+ (id) jaseNumberWithUnsignedIntP:(void *)int_p byteCount:(int)bytes { - if (bytes == sizeof(unsigned char)) { - return [NSNumber numberWithUnsignedChar:*(unsigned char *)int_p]; - } - - if (bytes == sizeof(unsigned short)) { - return [NSNumber numberWithUnsignedShort:*(unsigned short *)int_p]; - } - - if (bytes == sizeof(unsigned int)) { - return [NSNumber numberWithUnsignedInt:*(unsigned int *)int_p]; - } - - if (bytes == sizeof(unsigned long)) { - return [NSNumber numberWithUnsignedLong:*(unsigned long *)int_p]; - } - - if (bytes == sizeof(unsigned long long)) { - return [NSNumber numberWithUnsignedLongLong:*(unsigned long long *)int_p]; - } - - [NSException raise:NSInvalidArgumentException - format:@"NSNumber numberWithUnsignedInt:byteCount: number with %i bytes not supported.", bytes]; - - return nil; -} - -+ (id) jaseNumberWithFloatP:(void *)float_p byteCount:(int)bytes { - if (bytes == sizeof(float)) { - return [NSNumber numberWithFloat:*(float *)float_p]; - } - - if (bytes == sizeof(double)) { - return [NSNumber numberWithFloat:*(double *)float_p]; - } - - [NSException raise:NSInvalidArgumentException - format:@"NSNumber numberWithFloat:byteCount: floating point number with %i bytes not supported.", bytes]; - - return nil; -} - -@end - -@implementation NSValue (JavaAppleScriptEngineAdditions) - -- (NSAppleEventDescriptor *)aeDescriptorValue { - const char *type = [self objCType]; - - if (areEqualEncodings(type, @encode(NSSize))) { - NSSize size = [self sizeValue]; - return [[NSArray arrayWithObjects: - [NSNumber numberWithFloat:size.width], - [NSNumber numberWithFloat:size.height], nil] aeDescriptorValue]; - } - - if (areEqualEncodings(type, @encode(NSPoint))) { - NSPoint point = [self pointValue]; - return [[NSArray arrayWithObjects: - [NSNumber numberWithFloat:point.x], - [NSNumber numberWithFloat:point.y], nil] aeDescriptorValue]; - } - - if (areEqualEncodings(type, @encode(NSRange))) { - NSRange range = [self rangeValue]; - return [[NSArray arrayWithObjects: - [NSNumber numberWithUnsignedInt:range.location], - [NSNumber numberWithUnsignedInt:range.location + range.length], nil] aeDescriptorValue]; - } - - if (areEqualEncodings(type, @encode(NSRect))) { - NSRect rect = [self rectValue]; - return [[NSArray arrayWithObjects: - [NSNumber numberWithFloat:rect.origin.x], - [NSNumber numberWithFloat:rect.origin.y], - [NSNumber numberWithFloat:rect.origin.x + rect.size.width], - [NSNumber numberWithFloat:rect.origin.y + rect.size.height], nil] aeDescriptorValue]; - } - - [NSException raise:@"jaseUnsupportedAEDescriptorConversion" - format:@"JavaAppleScriptEngineAdditions: conversion of an NSNumber with objCType '%s' to an aeDescriptor is not supported.", type]; - - return nil; -} - -@end - - -@implementation NSImage (JavaAppleScriptEngineAdditions) - -- (NSAppleEventDescriptor *)aeDescriptorValue { - NSData *data = [self TIFFRepresentation]; - return [NSAppleEventDescriptor descriptorWithDescriptorType:typeTIFF data:data]; -} - -+ (NSImage *)imageWithAEDesc:(NSAppleEventDescriptor *)desc { - const AEDesc *d = [desc aeDesc]; - NSMutableData *data = [NSMutableData dataWithLength:AEGetDescDataSize(d)]; - AEGetDescData(d, [data mutableBytes], [data length]); - return [[[NSImage alloc] initWithData:data] autorelease]; -} - -@end - - - -@implementation NSAppleEventDescriptor (JavaAppleScriptEngineAdditions) - -// we're going to leak this. It doesn't matter much for running apps, but -// for developers it might be nice to try to dispose of it (so it would not clutter the -// output when testing for leaks) -static NSMutableDictionary *handlerDict = nil; - -- (id)objCObjectValue { - if (handlerDict == nil) [NSAppleEventDescriptor jaseSetUpHandlerDict]; - - id returnObj; - DescType type = [self descriptorType]; - NSInvocation *handlerInvocation = [handlerDict objectForKey:[NSValue valueWithBytes:&type objCType:@encode(DescType)]]; - if (handlerInvocation == nil) { - if (type == typeType) { - DescType subType; - AEGetDescData([self aeDesc], &subType, sizeof(subType)); - if (subType == typeNull) return [NSNull null]; - } - // return raw apple event descriptor if no handler is registered - returnObj = self; - } else { - [handlerInvocation setArgument:&self atIndex:2]; - [handlerInvocation invoke]; - [handlerInvocation getReturnValue:&returnObj]; - } - - return returnObj; -} - -// FIXME - error checking, non nil handler -+ (void)registerConversionHandler:(id)anObject selector:(SEL)aSelector forDescriptorTypes:(DescType)firstType, ... { - if (handlerDict == nil) [NSAppleEventDescriptor jaseSetUpHandlerDict]; - - NSInvocation *handlerInvocation = [NSInvocation invocationWithMethodSignature:[anObject methodSignatureForSelector:aSelector]]; - [handlerInvocation setTarget:anObject]; - [handlerInvocation setSelector:aSelector]; - - DescType aType = firstType; - va_list typesList; - va_start(typesList, firstType); - do { - NSValue *type = [NSValue valueWithBytes:&aType objCType:@encode(DescType)]; - [handlerDict setObject:handlerInvocation forKey:type]; - } while((aType = va_arg(typesList, DescType)) != 0); - va_end(typesList); -} - - -- (NSAppleEventDescriptor *)aeDescriptorValue { - return self; -} - -+ (id)descriptorWithInt16:(SInt16)val { - return [NSAppleEventDescriptor descriptorWithDescriptorType:typeSInt16 bytes:&val length:sizeof(val)]; -} - -- (SInt16)int16Value { - SInt16 retValue; - [[[self coerceToDescriptorType:typeSInt16] data] getBytes:&retValue]; - return retValue; -} - -+ (id)descriptorWithUnsignedInt32:(UInt32)val { - return [NSAppleEventDescriptor descriptorWithDescriptorType:typeUInt32 bytes:&val length:sizeof(val)]; -} - -- (UInt32)unsignedInt32Value { - UInt32 retValue; - [[[self coerceToDescriptorType:typeUInt32] data] getBytes:&retValue]; - return retValue; -} - - -+ (id)descriptorWithFloat32:(Float32)val { - return [NSAppleEventDescriptor descriptorWithDescriptorType:typeIEEE32BitFloatingPoint bytes:&val length:sizeof(val)]; -} - -- (Float32)float32Value { - Float32 retValue; - [[[self coerceToDescriptorType:typeIEEE32BitFloatingPoint] data] getBytes:&retValue]; - return retValue; -} - - -+ (id)descriptorWithFloat64:(Float64)val { - return [NSAppleEventDescriptor descriptorWithDescriptorType:typeIEEE64BitFloatingPoint bytes:&val length:sizeof(val)]; -} - -- (Float64)float64Value { - Float64 retValue; - [[[self coerceToDescriptorType:typeIEEE64BitFloatingPoint] data] getBytes:&retValue]; - return retValue; -} - -+ (id)descriptorWithLongDateTime:(LongDateTime)val { - return [NSAppleEventDescriptor descriptorWithDescriptorType:typeLongDateTime bytes:&val length:sizeof(val)]; -} - -- (LongDateTime)longDateTimeValue { - LongDateTime retValue; - [[[self coerceToDescriptorType:typeLongDateTime] data] getBytes:&retValue]; - return retValue; -} - -+ (void)jaseSetUpHandlerDict { - handlerDict = [[NSMutableDictionary alloc] init]; - - // register default handlers - // types are culled from AEDataModel.h and AERegistry.h - - // string -> NSStrings - [NSAppleEventDescriptor registerConversionHandler:[NSString class] selector:@selector(stringWithAEDesc:) forDescriptorTypes: - typeUnicodeText, typeText, typeUTF8Text, typeCString, typeChar, nil]; - - // number/bool -> NSNumber - [NSAppleEventDescriptor registerConversionHandler:[NSNumber class] selector:@selector(numberWithAEDesc:) forDescriptorTypes: - typeBoolean, typeTrue, typeFalse, - typeSInt16, typeSInt32, typeUInt32, typeSInt64, - typeIEEE32BitFloatingPoint, typeIEEE64BitFloatingPoint, type128BitFloatingPoint, nil]; - - // list -> NSArray - [NSAppleEventDescriptor registerConversionHandler:[NSArray class] selector:@selector(arrayWithAEDesc:) forDescriptorTypes:typeAEList, nil]; - - // record -> NSDictionary - [NSAppleEventDescriptor registerConversionHandler:[NSDictionary class] selector:@selector(dictionaryWithAEDesc:) forDescriptorTypes:typeAERecord, nil]; - - // date -> NSDate - [NSAppleEventDescriptor registerConversionHandler:[NSDate class] selector:@selector(dateWithAEDesc:) forDescriptorTypes:typeLongDateTime, nil]; - - // images -> NSImage - [NSAppleEventDescriptor registerConversionHandler:[NSImage class] selector:@selector(imageWithAEDesc:) forDescriptorTypes: - typeTIFF, typeJPEG, typeGIF, typePict, typeIconFamily, typeIconAndMask, nil]; - - // vers -> NSString - [NSAppleEventDescriptor registerConversionHandler:[NSString class] selector:@selector(versionWithAEDesc:) forDescriptorTypes:typeVersion, nil]; - - // null -> NSNull - [NSAppleEventDescriptor registerConversionHandler:[NSNull class] selector:@selector(nullWithAEDesc:) forDescriptorTypes:typeNull, nil]; -} - -@end diff --git a/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AppleScriptEngine.m b/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AppleScriptEngine.m deleted file mode 100644 index 33c8cdc379b..00000000000 --- a/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AppleScriptEngine.m +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright (c) 2011, 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. - */ - -#import "apple_applescript_AppleScriptEngine.h" -#import "apple_applescript_AppleScriptEngineFactory.h" - -// Must include this before JavaNativeFoundation.h to get jni.h from build -#include "jni.h" -#include "jni_util.h" - -#import - -#import "NS_Java_ConversionUtils.h" -#import "AppleScriptExecutionContext.h" - -//#define DEBUG 1 - -/* - * Declare library specific JNI_Onload entry if static build - */ -DEF_STATIC_JNI_OnLoad - -/* - * Class: apple_applescript_AppleScriptEngineFactory - * Method: initNative - * Signature: ()V - */ -JNIEXPORT void JNICALL Java_apple_applescript_AppleScriptEngineFactory_initNative -(JNIEnv *env, jclass clazz) -{ - return; -} - - -/* - * Class: apple_applescript_AppleScriptEngine - * Method: initNative - * Signature: ()V - */ -JNIEXPORT void JNICALL Java_apple_applescript_AppleScriptEngine_initNative -(JNIEnv *env, jclass clazz) -{ - return; -} - - -/* - * Class: apple_applescript_AppleScriptEngine - * Method: createContextFrom - * Signature: (Ljava/lang/Object;)J - */ -JNIEXPORT jlong JNICALL Java_apple_applescript_AppleScriptEngine_createContextFrom -(JNIEnv *env, jclass clazz, jobject javaContext) -{ - NSObject *obj = nil; - -JNF_COCOA_ENTER(env); - - obj = [[JavaAppleScriptEngineCoercion coercer] coerceJavaObject:javaContext withEnv:env]; - -#ifdef DEBUG - NSLog(@"converted context: %@", obj); -#endif - - CFRetain(obj); - -JNF_COCOA_EXIT(env); - - return ptr_to_jlong(obj); -} - - -/* - * Class: apple_applescript_AppleScriptEngine - * Method: createObjectFrom - * Signature: (J)Ljava/lang/Object; - */ -JNIEXPORT jobject JNICALL Java_apple_applescript_AppleScriptEngine_createObjectFrom -(JNIEnv *env, jclass clazz, jlong nativeContext) -{ - jobject obj = NULL; - -JNF_COCOA_ENTER(env); - - obj = [[JavaAppleScriptEngineCoercion coercer] coerceNSObject:(id)jlong_to_ptr(nativeContext) withEnv:env]; - -JNF_COCOA_EXIT(env); - - return obj; -} - - -/* - * Class: apple_applescript_AppleScriptEngine - * Method: disposeContext - * Signature: (J)V - */ -JNIEXPORT void JNICALL Java_apple_applescript_AppleScriptEngine_disposeContext -(JNIEnv *env, jclass clazz, jlong nativeContext) -{ - -JNF_COCOA_ENTER(env); - - id obj = (id)jlong_to_ptr(nativeContext); - if (obj != nil) CFRelease(obj); - -JNF_COCOA_EXIT(env); - -} - - -/* - * Class: apple_applescript_AppleScriptEngine - * Method: evalScript - * Signature: (Ljava/lang/String;J)J - */ -JNIEXPORT jlong JNICALL Java_apple_applescript_AppleScriptEngine_evalScript -(JNIEnv *env, jclass clazz, jstring ascript, jlong contextptr) -{ - id retval = nil; - -JNF_COCOA_ENTER(env); - - NSDictionary *ncontext = jlong_to_ptr(contextptr); - NSString *source = JNFJavaToNSString(env, ascript); - -#ifdef DEBUG - NSLog(@"evalScript(source:\"%@\" context: %@)", source, ncontext); -#endif - - AppleScriptExecutionContext *scriptInvocationCtx = [[[AppleScriptExecutionContext alloc] initWithSource:source context:ncontext] autorelease]; - retval = [scriptInvocationCtx invokeWithEnv:env]; - -#ifdef DEBUG - NSLog(@"returning: %@", retval); -#endif - - if (retval) CFRetain(retval); - -JNF_COCOA_EXIT(env); - - return ptr_to_jlong(retval); -} - - -/* - * Class: apple_applescript_AppleScriptEngine - * Method: evalScriptFromURL - * Signature: (Ljava/lang/String;J)J - */ -JNIEXPORT jlong JNICALL Java_apple_applescript_AppleScriptEngine_evalScriptFromURL -(JNIEnv *env, jclass clazz, jstring afilename, jlong contextptr) -{ - id retval = nil; - -JNF_COCOA_ENTER(env); - - NSDictionary *ncontext = jlong_to_ptr(contextptr); - NSString *filename = JNFJavaToNSString(env, afilename); - -#ifdef DEBUG - NSLog(@"evalScript(filename:\"%@\" context: %@)", filename, ncontext); -#endif - - AppleScriptExecutionContext *scriptInvocationCtx = [[[AppleScriptExecutionContext alloc] initWithFile:filename context:ncontext] autorelease]; - retval = [scriptInvocationCtx invokeWithEnv:env]; - -#ifdef DEBUG - NSLog(@"returning: %@", retval); -#endif - - if (retval) CFRetain(retval); - -JNF_COCOA_EXIT(env); - - return ptr_to_jlong(retval); -} diff --git a/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AppleScriptExecutionContext.h b/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AppleScriptExecutionContext.h deleted file mode 100644 index 863d0a711a0..00000000000 --- a/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AppleScriptExecutionContext.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#import - - -@interface AppleScriptExecutionContext : NSObject { - NSString *source; - BOOL isFile; - NSDictionary *context; - NSDictionary *error; - id returnValue; -} - -@property (nonatomic, retain) NSString *source; -@property (nonatomic, retain) NSDictionary *context; -@property (nonatomic, retain) NSDictionary *error; -@property (nonatomic, retain) id returnValue; - -- (id) initWithSource:(NSString *)source context:(NSDictionary *)context; -- (id) initWithFile:(NSString *)filename context:(NSDictionary *)context; -- (id) invokeWithEnv:(JNIEnv *)env; - -@end diff --git a/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AppleScriptExecutionContext.m b/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AppleScriptExecutionContext.m deleted file mode 100644 index 9b4d4d705e1..00000000000 --- a/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/AppleScriptExecutionContext.m +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#import "AppleScriptExecutionContext.h" - -#import - -#import "AS_NS_ConversionUtils.h" - - -@implementation AppleScriptExecutionContext - -@synthesize source; -@synthesize context; -@synthesize error; -@synthesize returnValue; - -- (id) init:(NSString *)sourceIn context:(id)contextIn { - self = [super init]; - if (!self) return self; - - self.source = sourceIn; - self.context = contextIn; - self.returnValue = nil; - self.error = nil; - - return self; -} - -- (id) initWithSource:(NSString *)sourceIn context:(NSDictionary *)contextIn { - self = [self init:sourceIn context:contextIn]; - isFile = NO; - return self; -} - -- (id) initWithFile:(NSString *)filenameIn context:(NSDictionary *)contextIn { - self = [self init:filenameIn context:contextIn]; - isFile = YES; - return self; -} - -- (void) dealloc { - self.source = nil; - self.context = nil; - self.returnValue = nil; - self.error = nil; - - [super dealloc]; -} - -- (NSAppleScript *) scriptFromURL { - NSURL *url = [NSURL URLWithString:source]; - NSDictionary *err = nil; - NSAppleScript *script = [[[NSAppleScript alloc] initWithContentsOfURL:url error:(&err)] autorelease]; - if (err != nil) self.error = err; - return script; -} - -- (NSAppleScript *) scriptFromSource { - return [[[NSAppleScript alloc] initWithSource:source] autorelease]; -} - -- (NSAppleEventDescriptor *) functionInvocationEvent { - NSString *function = [[context objectForKey:@"javax_script_function"] description]; - if (function == nil) return nil; - - // wrap the arg in an array if it is not already a list - id args = [context objectForKey:@"javax_script_argv"]; - if (![args isKindOfClass:[NSArray class]]) { - args = [NSArray arrayWithObjects:args, nil]; - } - - // triangulate our target - int pid = [[NSProcessInfo processInfo] processIdentifier]; - NSAppleEventDescriptor* targetAddress = [NSAppleEventDescriptor descriptorWithDescriptorType:typeKernelProcessID - bytes:&pid - length:sizeof(pid)]; - - // create the event to call a subroutine in the script - NSAppleEventDescriptor* event = [[NSAppleEventDescriptor alloc] initWithEventClass:kASAppleScriptSuite - eventID:kASSubroutineEvent - targetDescriptor:targetAddress - returnID:kAutoGenerateReturnID - transactionID:kAnyTransactionID]; - - // set up the handler - NSAppleEventDescriptor* subroutineDescriptor = [NSAppleEventDescriptor descriptorWithString:[function lowercaseString]]; - [event setParamDescriptor:subroutineDescriptor forKeyword:keyASSubroutineName]; - - // set up the arguments - [event setParamDescriptor:[args aeDescriptorValue] forKeyword:keyDirectObject]; - - return [event autorelease]; -} - -- (void) invoke { - // create our script - NSAppleScript *script = isFile ? [self scriptFromURL] : [self scriptFromSource]; - if (self.error != nil) return; - - // find out if we have a subroutine to call - NSAppleEventDescriptor *fxnInvkEvt = [self functionInvocationEvent]; - - // exec! - NSAppleEventDescriptor *desc = nil; - NSDictionary *err = nil; - if (fxnInvkEvt == nil) { - desc = [script executeAndReturnError:(&err)]; - } else { - desc = [script executeAppleEvent:fxnInvkEvt error:(&err)]; - } - - // if we encountered an exception, stash and bail - if (err != nil) { - self.error = err; - return; - } - - // convert to NSObjects, and return in ivar - self.returnValue = [desc objCObjectValue]; -} - -- (id) invokeWithEnv:(JNIEnv *)env { - BOOL useAnyThread = [@"any-thread" isEqual:[context valueForKey:@"javax_script_threading"]]; - - // check if we are already on the AppKit thread, if desired - if(pthread_main_np() || useAnyThread) { - [self invoke]; - } else { - [JNFRunLoop performOnMainThread:@selector(invoke) on:self withObject:nil waitUntilDone:YES]; - } - - // if we have an exception parked in our ivar, snarf the message (if there is one), and toss a ScriptException - if (self.error != nil) { - NSString *asErrString = [self.error objectForKey:NSAppleScriptErrorMessage]; - if (!asErrString) asErrString = @"AppleScriptEngine failed to execute script."; // usually when we fail to load a file - [JNFException raise:env as:"javax/script/ScriptException" reason:[asErrString UTF8String]]; - } - - return self.returnValue; -} - -@end diff --git a/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/NS_Java_ConversionUtils.h b/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/NS_Java_ConversionUtils.h deleted file mode 100644 index 8e5bd24b766..00000000000 --- a/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/NS_Java_ConversionUtils.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#import - - -@interface JavaAppleScriptEngineCoercion : NSObject - -+ (JNFTypeCoercer *) coercer; - -@end diff --git a/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/NS_Java_ConversionUtils.m b/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/NS_Java_ConversionUtils.m deleted file mode 100644 index 6114ce7a3bb..00000000000 --- a/jdk/src/jdk.deploy.osx/macosx/native/libapplescriptengine/NS_Java_ConversionUtils.m +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#import "NS_Java_ConversionUtils.h" - -#import - - -@interface JavaAppleScriptBaseConverter : NSObject -@end - -@interface JavaAppleScriptImageConverter : NSObject -@end - -@interface JavaAppleScriptVersionConverter : NSObject -@end - -@interface JavaAppleScriptNullConverter : NSObject -@end - - -@implementation JavaAppleScriptEngineCoercion - -static JNFTypeCoercer *appleScriptCoercer = nil; - -+ (JNFTypeCoercer *) coercer { - if (appleScriptCoercer) return appleScriptCoercer; - - id asSpecificCoercions = [[JNFDefaultCoercions defaultCoercer] deriveCoercer]; - [asSpecificCoercions addCoercion:[[[JavaAppleScriptImageConverter alloc] init] autorelease] forNSClass:[NSImage class] javaClass:@"java/awt/Image"]; - [asSpecificCoercions addCoercion:[[[JavaAppleScriptVersionConverter alloc] init] autorelease] forNSClass:[NSAppleEventDescriptor class] javaClass:nil]; - [asSpecificCoercions addCoercion:[[[JavaAppleScriptNullConverter alloc] init] autorelease] forNSClass:[NSNull class] javaClass:nil]; - - return appleScriptCoercer = [asSpecificCoercions retain]; -} - -@end - - -// [NSObject description] <-> java.lang.Object.toString() -@implementation JavaAppleScriptBaseConverter - -// by default, bizzare NSObjects will have -description called on them, and passed back to Java like that -- (jobject) coerceNSObject:(id)obj withEnv:(JNIEnv *)env usingCoercer:(JNFTypeCoercion *)coercer { - return JNFNSToJavaString(env, [obj description]); -} - -// by default, bizzare Java objects will be toString()'d and passed to AppleScript like that -- (id) coerceJavaObject:(jobject)obj withEnv:(JNIEnv *)env usingCoercer:(JNFTypeCoercion *)coercer { - return JNFObjectToString(env, obj); -} - -@end - - -// NSImage <-> apple.awt.CImage -@implementation JavaAppleScriptImageConverter - -static JNF_CLASS_CACHE(jc_CImage, "apple/awt/CImage"); -static JNF_STATIC_MEMBER_CACHE(jm_CImage_getCreator, jc_CImage, "getCreator", "()Lapple/awt/CImage$Creator;"); -static JNF_MEMBER_CACHE(jm_CImage_getNSImage, jc_CImage, "getNSImage", "()J"); - -static JNF_CLASS_CACHE(jc_CImage_Generator, "apple/awt/CImage$Creator"); -static JNF_MEMBER_CACHE(jm_CImage_Generator_createImageFromPtr, jc_CImage_Generator, "createImage", "(J)Ljava/awt/image/BufferedImage;"); -static JNF_MEMBER_CACHE(jm_CImage_Generator_createImageFromImg, jc_CImage_Generator, "createImage", "(Ljava/awt/Image;)Lapple/awt/CImage;"); - -- (jobject) coerceNSObject:(id)obj withEnv:(JNIEnv *)env usingCoercer:(JNFTypeCoercion *)coercer { - NSImage *img = (NSImage *)obj; - CFRetain(img); - jobject creator = JNFCallStaticObjectMethod(env, jm_CImage_getCreator); - jobject jobj = JNFCallObjectMethod(env, creator, jm_CImage_Generator_createImageFromPtr, ptr_to_jlong(img)); - return jobj; -} - -- (id) coerceJavaObject:(jobject)obj withEnv:(JNIEnv *)env usingCoercer:(JNFTypeCoercion *)coercer { - jobject cimage = obj; - if (!JNFIsInstanceOf(env, obj, &jc_CImage)) { - jobject creator = JNFCallStaticObjectMethod(env, jm_CImage_getCreator); - cimage = JNFCallObjectMethod(env, creator, jm_CImage_Generator_createImageFromImg, obj); - } - - jlong nsImagePtr = JNFCallLongMethod(env, cimage, jm_CImage_getNSImage); - - NSImage *img = (NSImage *)jlong_to_ptr(nsImagePtr); - return [[img retain] autorelease]; -} - -@end - - -// NSAppleEventDescriptor('vers') -> java.lang.String -@implementation JavaAppleScriptVersionConverter - -- (jobject) coerceNSObject:(id)obj withEnv:(JNIEnv *)env usingCoercer:(JNFTypeCoercion *)coercer { - NSAppleEventDescriptor *desc = (NSAppleEventDescriptor *)obj; - - const AEDesc *aeDesc = [desc aeDesc]; - if (aeDesc->descriptorType == typeNull) { - return NULL; - } - - return JNFNSToJavaString(env, [obj description]); -} - -- (id) coerceJavaObject:(jobject)obj withEnv:(JNIEnv *)env usingCoercer:(JNFTypeCoercion *)coercer { - return nil; // there is no Java object that represents a "version" -} - -@end - - -// NSNull <-> null -@implementation JavaAppleScriptNullConverter - -- (jobject) coerceNSObject:(id)obj withEnv:(JNIEnv *)env usingCoercer:(JNFTypeCoercion *)coercer { - return NULL; -} - -- (id) coerceJavaObject:(jobject)obj withEnv:(JNIEnv *)env usingCoercer:(JNFTypeCoercion *)coercer { - return nil; -} - -@end From 5bfc44dca23a7c9c29b280595df85cbb0417ec16 Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Tue, 8 Dec 2015 11:25:47 +0300 Subject: [PATCH 20/62] 7160052: GlyphVector.setGlyphPosition can throw an exception on valid input Reviewed-by: jdv, serb --- .../classes/sun/font/StandardGlyphVector.java | 8 ++- .../TestStandardGlyphVectorBug.java | 58 +++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 jdk/test/java/awt/font/GlyphVector/TestStandardGlyphVectorBug.java diff --git a/jdk/src/java.desktop/share/classes/sun/font/StandardGlyphVector.java b/jdk/src/java.desktop/share/classes/sun/font/StandardGlyphVector.java index 06abf9de163..69f1d4babc2 100644 --- a/jdk/src/java.desktop/share/classes/sun/font/StandardGlyphVector.java +++ b/jdk/src/java.desktop/share/classes/sun/font/StandardGlyphVector.java @@ -445,13 +445,19 @@ public Point2D getGlyphPosition(int ix) { } public void setGlyphPosition(int ix, Point2D pos) { + if (ix < 0 || ix > glyphs.length) { + throw new IndexOutOfBoundsException("ix = " + ix); + } + initPositions(); int ix2 = ix << 1; positions[ix2] = (float)pos.getX(); positions[ix2 + 1] = (float)pos.getY(); - clearCaches(ix); + if (ix < glyphs.length) { + clearCaches(ix); + } addFlags(FLAG_HAS_POSITION_ADJUSTMENTS); } diff --git a/jdk/test/java/awt/font/GlyphVector/TestStandardGlyphVectorBug.java b/jdk/test/java/awt/font/GlyphVector/TestStandardGlyphVectorBug.java new file mode 100644 index 00000000000..c4dbda40837 --- /dev/null +++ b/jdk/test/java/awt/font/GlyphVector/TestStandardGlyphVectorBug.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +import java.awt.Font; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; + +/** + * @test + * @bug 7160052 + * @run main TestStandardGlyphVectorBug + * @summary GlyphVector.setGlyphPosition should not throw an exception on valid input + */ +public class TestStandardGlyphVectorBug +{ + public static void main(String[] args) + { + Font defaultFont = new Font(null); + FontRenderContext defaultFrc = new FontRenderContext(new AffineTransform(), + true, true); + GlyphVector gv = defaultFont.createGlyphVector(defaultFrc, "test"); + + //this causes the bounds to be cached + //which is necessary to trigger the bug + gv.getGlyphLogicalBounds(0); + + //this correctly gets the position of the overall advance + Point2D glyphPosition = gv.getGlyphPosition(gv.getNumGlyphs()); + + // this sets the position of the overall advance, + // but also incorrectly tries to clear the bounds cache + // of a specific glyph indexed by the glyphIndex parameter + // even if the glyphIndex represents the overall advance + // (i.e. if glyphIndex == getNumGlyphs()) + gv.setGlyphPosition(gv.getNumGlyphs(), glyphPosition); + } +} From bb6d5ef30c8b1c8b5ccc6366e3efd8318227abba Mon Sep 17 00:00:00 2001 From: Erik Joelsson Date: Tue, 8 Dec 2015 12:05:51 +0100 Subject: [PATCH 21/62] 8144857: Intermittent build error building jdk/src/demo/solaris/jni/Poller/Poller.c Reviewed-by: dholmes --- jdk/make/CompileDemos.gmk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jdk/make/CompileDemos.gmk b/jdk/make/CompileDemos.gmk index 5bb6ed39d2a..423378389cc 100644 --- a/jdk/make/CompileDemos.gmk +++ b/jdk/make/CompileDemos.gmk @@ -459,7 +459,7 @@ ifeq ($(OPENJDK_TARGET_OS), solaris) # We can only compile native code after java has been compiled (since we # depend on generated .h files) $(SUPPORT_OUTPUTDIR)/demos/native/jni/Poller/Poller.o: \ - $(BUILD_DEMO_JAVA_POLLER_COMPILE_TARGETS) + $(BUILD_DEMO_JAVA_Poller_COMPILE_TARGET) # Copy to image $(SUPPORT_OUTPUTDIR)/demos/image/jni/Poller/README.txt: \ From 7c018b1a000ff5f480b7c5acfd2770a23bb6e583 Mon Sep 17 00:00:00 2001 From: Ambarish Rapte Date: Wed, 9 Dec 2015 02:41:51 +0400 Subject: [PATCH 22/62] 8060137: Removing Text from TextField / TextArea is not possible after typing Reviewed-by: ssadetsky, psadhukhan --- .../share/classes/java/awt/TextComponent.java | 22 ++-- .../TextAreaEditing/TextAreaEditing.java | 28 ++++- .../TextFieldEditing/TextFieldEditing.java | 113 ++++++++++++++++++ 3 files changed, 154 insertions(+), 9 deletions(-) create mode 100644 jdk/test/java/awt/TextField/TextFieldEditing/TextFieldEditing.java diff --git a/jdk/src/java.desktop/share/classes/java/awt/TextComponent.java b/jdk/src/java.desktop/share/classes/java/awt/TextComponent.java index 9e7c22a49bb..191412a30b7 100644 --- a/jdk/src/java.desktop/share/classes/java/awt/TextComponent.java +++ b/jdk/src/java.desktop/share/classes/java/awt/TextComponent.java @@ -229,15 +229,21 @@ public void removeNotify() { * @see java.awt.TextComponent#getText */ public synchronized void setText(String t) { - boolean skipTextEvent = (text == null || text.isEmpty()) - && (t == null || t.isEmpty()); - text = (t != null) ? t : ""; + if (t == null) { + t = ""; + } TextComponentPeer peer = (TextComponentPeer)this.peer; - // Please note that we do not want to post an event - // if TextArea.setText() or TextField.setText() replaces an empty text - // by an empty text, that is, if component's text remains unchanged. - if (peer != null && !skipTextEvent) { - peer.setText(text); + if (peer != null) { + text = peer.getText(); + // Please note that we do not want to post an event + // if TextArea.setText() or TextField.setText() replaces text + // by same text, that is, if component's text remains unchanged. + if (!t.equals(text)) { + text = t; + peer.setText(text); + } + } else { + text = t; } } diff --git a/jdk/test/java/awt/TextArea/TextAreaEditing/TextAreaEditing.java b/jdk/test/java/awt/TextArea/TextAreaEditing/TextAreaEditing.java index f3306784ef8..6b7dcfaf2c0 100644 --- a/jdk/test/java/awt/TextArea/TextAreaEditing/TextAreaEditing.java +++ b/jdk/test/java/awt/TextArea/TextAreaEditing/TextAreaEditing.java @@ -23,16 +23,23 @@ /* @test - @bug 8040322 + @bug 8040322 8060137 + @library ../../regtesthelpers + @build Util @summary Test TextArea APIs replaceRange, insert, append & setText @run main TextAreaEditing */ import java.awt.Frame; +import java.awt.Robot; import java.awt.TextArea; +import java.awt.AWTException; +import java.awt.event.KeyEvent; +import test.java.awt.regtesthelpers.Util; public class TextAreaEditing { + final static Robot robot = Util.createRobot(); private int testFailCount; private boolean isTestFail; private StringBuilder testFailMessage; @@ -61,6 +68,7 @@ public static void main(String[] s) { textArea.testReplaceRange(); textArea.testInsert(); textArea.testAppend(); + textArea.testSetText(); textArea.checkFailures(); textArea.dispose(); } @@ -119,6 +127,24 @@ private void testAppend() { checkTest(""); } + private void testSetText() { + textArea.setText(null); + textArea.requestFocus(); + Util.clickOnComp(textArea, robot); + Util.waitForIdle(robot); + robot.keyPress(KeyEvent.VK_A); + robot.delay(5); + robot.keyRelease(KeyEvent.VK_A); + Util.waitForIdle(robot); + textArea.setText(null); + checkTest(""); + textArea.setText("CaseSensitive"); + checkTest("CaseSensitive"); + textArea.setText("caseSensitive"); + checkTest("caseSensitive"); + + } + private void checkTest(String str) { if (str != null && !str.equals(textArea.getText())) { testFailMessage.append("TestFail line : "); diff --git a/jdk/test/java/awt/TextField/TextFieldEditing/TextFieldEditing.java b/jdk/test/java/awt/TextField/TextFieldEditing/TextFieldEditing.java new file mode 100644 index 00000000000..503b6e35129 --- /dev/null +++ b/jdk/test/java/awt/TextField/TextFieldEditing/TextFieldEditing.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + @test + @bug 8060137 + @library ../../regtesthelpers + @build Util + @summary Test TextField setText API + @run main TextFieldEditing + */ + +import java.awt.Frame; +import java.awt.Robot; +import java.awt.TextField; +import java.awt.AWTException; +import java.awt.event.KeyEvent; +import test.java.awt.regtesthelpers.Util; + +public class TextFieldEditing { + + final static Robot robot = Util.createRobot(); + private int testFailCount; + private boolean isTestFail; + private StringBuilder testFailMessage; + + private Frame mainFrame; + private TextField textField; + + private TextFieldEditing() { + testFailMessage = new StringBuilder(); + mainFrame = new Frame(); + mainFrame.setSize(200, 200); + + textField = new TextField(); + mainFrame.add(textField); + mainFrame.setVisible(true); + } + + private void dispose() { + if (mainFrame != null) { + mainFrame.dispose(); + } + } + + public static void main(String[] s) { + TextFieldEditing textField = new TextFieldEditing(); + textField.testSetText(); + textField.checkFailures(); + textField.dispose(); + } + + private void testSetText() { + textField.setText(null); + textField.requestFocus(); + Util.clickOnComp(textField, robot); + Util.waitForIdle(robot); + robot.keyPress(KeyEvent.VK_A); + robot.delay(5); + robot.keyRelease(KeyEvent.VK_A); + Util.waitForIdle(robot); + textField.setText(null); + checkTest(""); + textField.setText("CaseSensitive"); + checkTest("CaseSensitive"); + textField.setText("caseSensitive"); + checkTest("caseSensitive"); + } + + private void checkTest(String str) { + if (str != null && !str.equals(textField.getText())) { + testFailMessage.append("TestFail line : "); + testFailMessage.append(Thread.currentThread().getStackTrace()[2]. + getLineNumber()); + testFailMessage.append(" TextField string : \""); + testFailMessage.append(textField.getText()); + testFailMessage.append("\" does not match expected string : \""); + testFailMessage.append(str).append("\""); + testFailMessage.append(System.getProperty("line.separator")); + testFailCount++; + isTestFail = true; + } + } + + private void checkFailures() { + if (isTestFail) { + testFailMessage.insert(0, "Test Fail count : " + testFailCount + + System.getProperty("line.separator")); + dispose(); + throw new RuntimeException(testFailMessage.toString()); + } + } +} From 18ace0f732d8acfd7bfaf8f18a50549d8822bf20 Mon Sep 17 00:00:00 2001 From: Manajit Halder Date: Tue, 8 Dec 2015 19:50:14 +0300 Subject: [PATCH 23/62] 7159591: [macosx] In SetFontTest there's no space for the second button Reviewed-by: arapte, serb --- jdk/test/java/awt/List/SetFontTest/SetFontTest.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jdk/test/java/awt/List/SetFontTest/SetFontTest.html b/jdk/test/java/awt/List/SetFontTest/SetFontTest.html index db33b8c83a8..3ac0d99fc51 100644 --- a/jdk/test/java/awt/List/SetFontTest/SetFontTest.html +++ b/jdk/test/java/awt/List/SetFontTest/SetFontTest.html @@ -38,6 +38,6 @@

SetFontTest
Bug ID: 5010944

See the dialog box (usually in upper left corner) for instructions

- + From 44a8a73eb6585a158e95946673d8ff6a943daca6 Mon Sep 17 00:00:00 2001 From: Xueming Shen Date: Tue, 8 Dec 2015 09:25:01 -0800 Subject: [PATCH 24/62] 8142508: To bring j.u.z.ZipFile's native implementation to Java to remove the expensive jni cost and mmap crash risk Reviewed-by: coffeys --- jdk/make/mapfiles/libzip/mapfile-vers | 22 +- jdk/make/mapfiles/libzip/reorder-sparc | 16 - jdk/make/mapfiles/libzip/reorder-sparcv9 | 15 - jdk/make/mapfiles/libzip/reorder-x86 | 18 - .../share/classes/java/util/jar/JarFile.java | 5 +- .../share/classes/java/util/zip/ZipCoder.java | 21 +- .../share/classes/java/util/zip/ZipFile.java | 847 ++++++++++++++---- .../share/classes/java/util/zip/ZipUtils.java | 79 +- .../internal/misc/JavaUtilZipFileAccess.java | 1 + .../java.base/share/classes/sun/misc/VM.java | 3 - .../java.base/share/native/libzip/ZipFile.c | 406 --------- jdk/test/java/util/zip/ZipFile/ReadZip.java | 3 +- .../java/util/zip/ZipFile/TestZipFile.java | 361 ++++++++ 13 files changed, 1120 insertions(+), 677 deletions(-) delete mode 100644 jdk/src/java.base/share/native/libzip/ZipFile.c create mode 100644 jdk/test/java/util/zip/ZipFile/TestZipFile.java diff --git a/jdk/make/mapfiles/libzip/mapfile-vers b/jdk/make/mapfiles/libzip/mapfile-vers index ceace23f26d..c1ab48c10cf 100644 --- a/jdk/make/mapfiles/libzip/mapfile-vers +++ b/jdk/make/mapfiles/libzip/mapfile-vers @@ -1,5 +1,5 @@ # -# Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 1997, 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 @@ -27,7 +27,6 @@ SUNWprivate_1.1 { global: - Java_java_util_jar_JarFile_getMetaInfEntryNames; Java_java_util_zip_Adler32_update; Java_java_util_zip_Adler32_updateBytes; Java_java_util_zip_Adler32_updateByteBuffer; @@ -48,25 +47,6 @@ SUNWprivate_1.1 { Java_java_util_zip_Inflater_initIDs; Java_java_util_zip_Inflater_reset; Java_java_util_zip_Inflater_setDictionary; - Java_java_util_zip_ZipFile_close; - Java_java_util_zip_ZipFile_getCommentBytes; - Java_java_util_zip_ZipFile_freeEntry; - Java_java_util_zip_ZipFile_getEntry; - Java_java_util_zip_ZipFile_getEntryBytes; - Java_java_util_zip_ZipFile_getEntryCrc; - Java_java_util_zip_ZipFile_getEntryCSize; - Java_java_util_zip_ZipFile_getEntryFlag; - Java_java_util_zip_ZipFile_getEntryMethod; - Java_java_util_zip_ZipFile_getEntrySize; - Java_java_util_zip_ZipFile_getEntryTime; - Java_java_util_zip_ZipFile_getNextEntry; - Java_java_util_zip_ZipFile_getZipMessage; - Java_java_util_zip_ZipFile_getTotal; - Java_java_util_zip_ZipFile_initIDs; - Java_java_util_zip_ZipFile_open; - Java_java_util_zip_ZipFile_read; - Java_java_util_zip_ZipFile_startsWithLOC; - ZIP_Close; ZIP_CRC32; ZIP_FindEntry; diff --git a/jdk/make/mapfiles/libzip/reorder-sparc b/jdk/make/mapfiles/libzip/reorder-sparc index 154e7998a53..63b2ad6fc28 100644 --- a/jdk/make/mapfiles/libzip/reorder-sparc +++ b/jdk/make/mapfiles/libzip/reorder-sparc @@ -16,30 +16,14 @@ text: .text%ZIP_InflateFully; text: .text%ZIP_Lock; text: .text%ZIP_Unlock; text: .text%ZIP_FreeEntry; -text: .text%Java_java_util_zip_ZipFile_initIDs; -text: .text%Java_java_util_zip_ZipFile_open; -text: .text%Java_java_util_zip_ZipFile_getTotal; -text: .text%Java_java_util_zip_ZipFile_startsWithLOC; -text: .text%Java_java_util_zip_ZipFile_getEntry; -text: .text%Java_java_util_zip_ZipFile_freeEntry; -text: .text%Java_java_util_zip_ZipFile_getEntryTime; -text: .text%Java_java_util_zip_ZipFile_getEntryCrc; -text: .text%Java_java_util_zip_ZipFile_getEntryCSize; -text: .text%Java_java_util_zip_ZipFile_getEntrySize; -text: .text%Java_java_util_zip_ZipFile_getEntryFlag; -text: .text%Java_java_util_zip_ZipFile_getEntryMethod; -text: .text%Java_java_util_zip_ZipFile_getEntryBytes; text: .text%Java_java_util_zip_Inflater_initIDs; text: .text%Java_java_util_zip_Inflater_init; text: .text%inflateInit2_; text: .text%zcalloc; text: .text%Java_java_util_zip_Inflater_inflateBytes; -text: .text%Java_java_util_zip_ZipFile_read; text: .text%ZIP_Read; text: .text%zcfree; -text: .text%Java_java_util_jar_JarFile_getMetaInfEntryNames; text: .text%Java_java_util_zip_Inflater_reset; text: .text%Java_java_util_zip_Inflater_end; text: .text%inflateEnd; -text: .text%Java_java_util_zip_ZipFile_close; text: .text%ZIP_Close; diff --git a/jdk/make/mapfiles/libzip/reorder-sparcv9 b/jdk/make/mapfiles/libzip/reorder-sparcv9 index 89690b8dabb..caca118de98 100644 --- a/jdk/make/mapfiles/libzip/reorder-sparcv9 +++ b/jdk/make/mapfiles/libzip/reorder-sparcv9 @@ -15,19 +15,6 @@ text: .text%ZIP_InflateFully; text: .text%ZIP_Lock; text: .text%ZIP_Unlock; text: .text%ZIP_FreeEntry; -text: .text%Java_java_util_zip_ZipFile_initIDs; -text: .text%Java_java_util_zip_ZipFile_open; -text: .text%Java_java_util_zip_ZipFile_getTotal; -text: .text%Java_java_util_zip_ZipFile_startsWithLOC; -text: .text%Java_java_util_zip_ZipFile_getEntry; -text: .text%Java_java_util_zip_ZipFile_freeEntry; -text: .text%Java_java_util_zip_ZipFile_getEntryTime; -text: .text%Java_java_util_zip_ZipFile_getEntryCrc; -text: .text%Java_java_util_zip_ZipFile_getEntryCSize; -text: .text%Java_java_util_zip_ZipFile_getEntrySize; -text: .text%Java_java_util_zip_ZipFile_getEntryFlag; -text: .text%Java_java_util_zip_ZipFile_getEntryMethod; -text: .text%Java_java_util_zip_ZipFile_getEntryBytes; text: .text%Java_java_util_zip_Inflater_initIDs; text: .text%Java_java_util_zip_Inflater_init; text: .text%inflateInit2_; @@ -35,7 +22,6 @@ text: .text%zcalloc; text: .text%inflateReset; text: .text%Java_java_util_zip_Inflater_inflateBytes; text: .text%inflate; -text: .text%Java_java_util_zip_ZipFile_read; text: .text%ZIP_Read; text: .text%zcfree; text: .text%Java_java_util_jar_JarFile_getMetaInfEntryNames; @@ -43,6 +29,5 @@ text: .text%ZIP_ReadEntry; text: .text%InflateFully; text: .text%inflateEnd; text: .text%Java_java_util_zip_Inflater_reset; -text: .text%Java_java_util_zip_ZipFile_close; text: .text%ZIP_Close; text: .text%Java_java_util_zip_Inflater_end; diff --git a/jdk/make/mapfiles/libzip/reorder-x86 b/jdk/make/mapfiles/libzip/reorder-x86 index 746948315eb..dfd57c7752e 100644 --- a/jdk/make/mapfiles/libzip/reorder-x86 +++ b/jdk/make/mapfiles/libzip/reorder-x86 @@ -16,34 +16,16 @@ text: .text%ZIP_InflateFully; text: .text%ZIP_Lock; text: .text%ZIP_Unlock; text: .text%ZIP_FreeEntry; -text: .text%Java_java_util_zip_ZipFile_initIDs; -text: .text%Java_java_util_zip_ZipFile_open; -text: .text%Java_java_util_zip_ZipFile_getTotal; -text: .text%Java_java_util_zip_ZipFile_startsWithLOC; -text: .text%Java_java_util_zip_ZipFile_getEntry; -text: .text%Java_java_util_zip_ZipFile_freeEntry; -text: .text%Java_java_util_zip_ZipFile_getEntryTime; -text: .text%Java_java_util_zip_ZipFile_getEntryCrc; -text: .text%Java_java_util_zip_ZipFile_getEntryCSize; -text: .text%Java_java_util_zip_ZipFile_getEntrySize; -text: .text%Java_java_util_zip_ZipFile_getEntryFlag; -text: .text%Java_java_util_zip_ZipFile_getEntryMethod; -text: .text%Java_java_util_zip_ZipFile_getEntryBytes; -text: .text%Java_java_util_zip_Inflater_initIDs; -text: .text%Java_java_util_zip_Inflater_init; text: .text%inflateInit2_; text: .text%zcalloc; text: .text%inflateReset; text: .text%Java_java_util_zip_Inflater_inflateBytes; text: .text%inflate; -text: .text%Java_java_util_zip_ZipFile_read; text: .text%ZIP_Read; text: .text%zcfree; -text: .text%Java_java_util_jar_JarFile_getMetaInfEntryNames; text: .text%ZIP_ReadEntry; text: .text%InflateFully; text: .text%inflateEnd; text: .text%Java_java_util_zip_Inflater_reset; -text: .text%Java_java_util_zip_ZipFile_close; text: .text%ZIP_Close; text: .text%Java_java_util_zip_Inflater_end; diff --git a/jdk/src/java.base/share/classes/java/util/jar/JarFile.java b/jdk/src/java.base/share/classes/java/util/jar/JarFile.java index 6d16a517ac9..62734ceefbd 100644 --- a/jdk/src/java.base/share/classes/java/util/jar/JarFile.java +++ b/jdk/src/java.base/share/classes/java/util/jar/JarFile.java @@ -203,7 +203,10 @@ private Manifest getManifestFromReference() throws IOException { return man; } - private native String[] getMetaInfEntryNames(); + private String[] getMetaInfEntryNames() { + return jdk.internal.misc.SharedSecrets.getJavaUtilZipFileAccess() + .getMetaInfEntryNames((ZipFile)this); + } /** * Returns the {@code JarEntry} for the given entry name or diff --git a/jdk/src/java.base/share/classes/java/util/zip/ZipCoder.java b/jdk/src/java.base/share/classes/java/util/zip/ZipCoder.java index b920b820e03..243d6e8c065 100644 --- a/jdk/src/java.base/share/classes/java/util/zip/ZipCoder.java +++ b/jdk/src/java.base/share/classes/java/util/zip/ZipCoder.java @@ -43,7 +43,7 @@ final class ZipCoder { - String toString(byte[] ba, int length) { + String toString(byte[] ba, int off, int length) { CharsetDecoder cd = decoder().reset(); int len = (int)(length * cd.maxCharsPerByte()); char[] ca = new char[len]; @@ -53,12 +53,12 @@ String toString(byte[] ba, int length) { // CodingErrorAction.REPLACE mode. ZipCoder uses // REPORT mode. if (isUTF8 && cd instanceof ArrayDecoder) { - int clen = ((ArrayDecoder)cd).decode(ba, 0, length, ca); + int clen = ((ArrayDecoder)cd).decode(ba, off, length, ca); if (clen == -1) // malformed throw new IllegalArgumentException("MALFORMED"); return new String(ca, 0, clen); } - ByteBuffer bb = ByteBuffer.wrap(ba, 0, length); + ByteBuffer bb = ByteBuffer.wrap(ba, off, length); CharBuffer cb = CharBuffer.wrap(ca); CoderResult cr = cd.decode(bb, cb, true); if (!cr.isUnderflow()) @@ -69,8 +69,12 @@ String toString(byte[] ba, int length) { return new String(ca, 0, cb.position()); } + String toString(byte[] ba, int length) { + return toString(ba, 0, length); + } + String toString(byte[] ba) { - return toString(ba, ba.length); + return toString(ba, 0, ba.length); } byte[] getBytes(String s) { @@ -111,13 +115,16 @@ byte[] getBytesUTF8(String s) { return utf8.getBytes(s); } - String toStringUTF8(byte[] ba, int len) { + return toStringUTF8(ba, 0, len); + } + + String toStringUTF8(byte[] ba, int off, int len) { if (isUTF8) - return toString(ba, len); + return toString(ba, off, len); if (utf8 == null) utf8 = new ZipCoder(StandardCharsets.UTF_8); - return utf8.toString(ba, len); + return utf8.toString(ba, off, len); } boolean isUTF8() { diff --git a/jdk/src/java.base/share/classes/java/util/zip/ZipFile.java b/jdk/src/java.base/share/classes/java/util/zip/ZipFile.java index 4e3a6d20417..561023c30e7 100644 --- a/jdk/src/java.base/share/classes/java/util/zip/ZipFile.java +++ b/jdk/src/java.base/share/classes/java/util/zip/ZipFile.java @@ -30,14 +30,22 @@ import java.io.IOException; import java.io.EOFException; import java.io.File; +import java.io.RandomAccessFile; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.Path; +import java.nio.file.Files; + import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Deque; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.Map; +import java.util.Objects; import java.util.NoSuchElementException; import java.util.Spliterator; import java.util.Spliterators; @@ -47,7 +55,9 @@ import jdk.internal.misc.JavaUtilZipFileAccess; import jdk.internal.misc.SharedSecrets; +import static java.util.zip.ZipConstants.*; import static java.util.zip.ZipConstants64.*; +import static java.util.zip.ZipUtils.*; /** * This class is used to read entries from a zip file. @@ -60,11 +70,11 @@ */ public class ZipFile implements ZipConstants, Closeable { - private long jzfile; // address of jzfile data + private final String name; // zip file name - private final int total; // total number of entries - private final boolean locsig; // if zip file starts with LOCSIG (usually true) private volatile boolean closeRequested = false; + private Source zsrc; + private ZipCoder zc; private static final int STORED = ZipEntry.STORED; private static final int DEFLATED = ZipEntry.DEFLATED; @@ -83,23 +93,6 @@ class ZipFile implements ZipConstants, Closeable { */ public static final int OPEN_DELETE = 0x4; - static { - /* Zip library is loaded from System.initializeSystemClass */ - initIDs(); - } - - private static native void initIDs(); - - private static final boolean usemmap; - - static { - // A system prpperty to disable mmap use to avoid vm crash when - // in-use zip file is accidently overwritten by others. - String prop = sun.misc.VM.getSavedProperty("sun.zip.disableMemoryMapping"); - usemmap = (prop == null || - !(prop.length() == 0 || prop.equalsIgnoreCase("true"))); - } - /** * Opens a zip file for reading. * @@ -165,8 +158,6 @@ public ZipFile(File file) throws ZipException, IOException { this(file, OPEN_READ); } - private ZipCoder zc; - /** * Opens a new {@code ZipFile} to read from the specified * {@code File} object in the specified mode. The mode argument @@ -214,16 +205,13 @@ public ZipFile(File file, int mode, Charset charset) throws IOException sm.checkDelete(name); } } - if (charset == null) - throw new NullPointerException("charset is null"); + Objects.requireNonNull(charset, "charset"); this.zc = ZipCoder.get(charset); + this.name = name; long t0 = System.nanoTime(); - jzfile = open(name, mode, file.lastModified(), usemmap); + this.zsrc = Source.get(file, (mode & OPEN_DELETE) != 0); sun.misc.PerfCounter.getZipFileOpenTime().addElapsedTimeFrom(t0); sun.misc.PerfCounter.getZipFileCount().increment(); - this.name = name; - this.total = getTotal(jzfile); - this.locsig = startsWithLOC(jzfile); } /** @@ -257,6 +245,7 @@ public ZipFile(String name, Charset charset) throws IOException /** * Opens a ZIP file for reading given the specified File object. + * * @param file the ZIP file to be opened for reading * @param charset * The {@linkplain java.nio.charset.Charset charset} to be @@ -287,10 +276,10 @@ public ZipFile(File file, Charset charset) throws IOException public String getComment() { synchronized (this) { ensureOpen(); - byte[] bcomm = getCommentBytes(jzfile); - if (bcomm == null) + if (zsrc.comment == null) { return null; - return zc.toString(bcomm, bcomm.length); + } + return zc.toString(zsrc.comment); } } @@ -303,38 +292,27 @@ public String getComment() { * @throws IllegalStateException if the zip file has been closed */ public ZipEntry getEntry(String name) { - if (name == null) { - throw new NullPointerException("name"); - } - long jzentry = 0; + Objects.requireNonNull(name, "name"); synchronized (this) { ensureOpen(); - jzentry = getEntry(jzfile, zc.getBytes(name), true); - if (jzentry != 0) { - ZipEntry ze = getZipEntry(name, jzentry); - freeEntry(jzfile, jzentry); - return ze; + int pos = zsrc.getEntryPos(zc.getBytes(name), true); + if (pos != -1) { + return getZipEntry(name, pos); } } return null; } - private static native long getEntry(long jzfile, byte[] name, - boolean addSlash); - - // freeEntry releases the C jzentry struct. - private static native void freeEntry(long jzfile, long jzentry); - - // the outstanding inputstreams that need to be closed, + // The outstanding inputstreams that need to be closed, // mapped to the inflater objects they use. private final Map streams = new WeakHashMap<>(); /** * Returns an input stream for reading the contents of the specified * zip file entry. - * - *

Closing this ZIP file will, in turn, close all input - * streams that have been returned by invocations of this method. + *

+ * Closing this ZIP file will, in turn, close all input streams that + * have been returned by invocations of this method. * * @param entry the zip file entry * @return the input stream for reading the contents of the specified @@ -344,37 +322,38 @@ private static native long getEntry(long jzfile, byte[] name, * @throws IllegalStateException if the zip file has been closed */ public InputStream getInputStream(ZipEntry entry) throws IOException { - if (entry == null) { - throw new NullPointerException("entry"); - } - long jzentry = 0; + Objects.requireNonNull(entry, "entry"); + int pos = -1; ZipFileInputStream in = null; synchronized (this) { ensureOpen(); if (!zc.isUTF8() && (entry.flag & EFS) != 0) { - jzentry = getEntry(jzfile, zc.getBytesUTF8(entry.name), false); + pos = zsrc.getEntryPos(zc.getBytesUTF8(entry.name), false); } else { - jzentry = getEntry(jzfile, zc.getBytes(entry.name), false); + pos = zsrc.getEntryPos(zc.getBytes(entry.name), false); } - if (jzentry == 0) { + if (pos == -1) { return null; } - in = new ZipFileInputStream(jzentry); - - switch (getEntryMethod(jzentry)) { + in = new ZipFileInputStream(zsrc.cen, pos); + switch (CENHOW(zsrc.cen, pos)) { case STORED: synchronized (streams) { streams.put(in, null); } return in; case DEFLATED: + // Inflater likes a bit of slack // MORE: Compute good size for inflater stream: - long size = getEntrySize(jzentry) + 2; // Inflater likes a bit of slack - if (size > 65536) size = 8192; - if (size <= 0) size = 4096; + long size = CENLEN(zsrc.cen, pos) + 2; + if (size > 65536) { + size = 8192; + } + if (size <= 0) { + size = 4096; + } Inflater inf = getInflater(); - InputStream is = - new ZipFileInflaterInputStream(in, inf, (int)size); + InputStream is = new ZipFileInflaterInputStream(in, inf, (int)size); synchronized (streams) { streams.put(is, inf); } @@ -447,8 +426,8 @@ protected void finalize() throws Throwable { private Inflater getInflater() { Inflater inf; synchronized (inflaterCache) { - while (null != (inf = inflaterCache.poll())) { - if (false == inf.ended()) { + while ((inf = inflaterCache.poll()) != null) { + if (!inf.ended()) { return inf; } } @@ -460,7 +439,7 @@ private Inflater getInflater() { * Releases the specified inflater to the list of available inflaters. */ private void releaseInflater(Inflater inf) { - if (false == inf.ended()) { + if (!inf.ended()) { inf.reset(); synchronized (inflaterCache) { inflaterCache.add(inf); @@ -469,7 +448,7 @@ private void releaseInflater(Inflater inf) { } // List of available Inflater objects for decompression - private Deque inflaterCache = new ArrayDeque<>(); + private final Deque inflaterCache = new ArrayDeque<>(); /** * Returns the path name of the ZIP file. @@ -493,7 +472,7 @@ public boolean hasMoreElements() { public boolean hasNext() { synchronized (ZipFile.this) { ensureOpen(); - return i < total; + return i < zsrc.total; } } @@ -504,28 +483,11 @@ public ZipEntry nextElement() { public ZipEntry next() { synchronized (ZipFile.this) { ensureOpen(); - if (i >= total) { + if (i >= zsrc.total) { throw new NoSuchElementException(); } - long jzentry = getNextEntry(jzfile, i++); - if (jzentry == 0) { - String message; - if (closeRequested) { - message = "ZipFile concurrently closed"; - } else { - message = getZipMessage(ZipFile.this.jzfile); - } - throw new ZipError("jzentry == 0" + - ",\n jzfile = " + ZipFile.this.jzfile + - ",\n total = " + ZipFile.this.total + - ",\n name = " + ZipFile.this.name + - ",\n i = " + i + - ",\n message = " + message - ); - } - ZipEntry ze = getZipEntry(null, jzentry); - freeEntry(jzfile, jzentry); - return ze; + // each "entry" has 3 ints in table entries + return getZipEntry(null, zsrc.getEntryPos(i++ * 3)); } } @@ -559,48 +521,53 @@ public Stream stream() { Spliterator.IMMUTABLE | Spliterator.NONNULL), false); } - private ZipEntry getZipEntry(String name, long jzentry) { + /* Checks ensureOpen() before invoke this method */ + private ZipEntry getZipEntry(String name, int pos) { + byte[] cen = zsrc.cen; ZipEntry e = new ZipEntry(); - e.flag = getEntryFlag(jzentry); // get the flag first + int nlen = CENNAM(cen, pos); + int elen = CENEXT(cen, pos); + int clen = CENCOM(cen, pos); + e.flag = CENFLG(cen, pos); // get the flag first if (name != null) { e.name = name; } else { - byte[] bname = getEntryBytes(jzentry, JZENTRY_NAME); if (!zc.isUTF8() && (e.flag & EFS) != 0) { - e.name = zc.toStringUTF8(bname, bname.length); + e.name = zc.toStringUTF8(cen, pos + CENHDR, nlen); } else { - e.name = zc.toString(bname, bname.length); - } - } - e.xdostime = getEntryTime(jzentry); - e.crc = getEntryCrc(jzentry); - e.size = getEntrySize(jzentry); - e.csize = getEntryCSize(jzentry); - e.method = getEntryMethod(jzentry); - e.setExtra0(getEntryBytes(jzentry, JZENTRY_EXTRA), false); - byte[] bcomm = getEntryBytes(jzentry, JZENTRY_COMMENT); - if (bcomm == null) { - e.comment = null; - } else { + e.name = zc.toString(cen, pos + CENHDR, nlen); + } + } + e.xdostime = CENTIM(cen, pos); + e.crc = CENCRC(cen, pos); + e.size = CENLEN(cen, pos); + e.csize = CENSIZ(cen, pos); + e.method = CENHOW(cen, pos); + if (elen != 0) { + e.setExtra0(Arrays.copyOfRange(cen, pos + CENHDR + nlen, + pos + CENHDR + nlen + elen), true); + } + if (clen != 0) { if (!zc.isUTF8() && (e.flag & EFS) != 0) { - e.comment = zc.toStringUTF8(bcomm, bcomm.length); + e.comment = zc.toStringUTF8(cen, pos + CENHDR + nlen + elen, clen); } else { - e.comment = zc.toString(bcomm, bcomm.length); + e.comment = zc.toString(cen, pos + CENHDR + nlen + elen, clen); } } return e; } - private static native long getNextEntry(long jzfile, int i); - /** * Returns the number of entries in the ZIP file. + * * @return the number of entries in the ZIP file * @throws IllegalStateException if the zip file has been closed */ public int size() { - ensureOpen(); - return total; + synchronized (this) { + ensureOpen(); + return zsrc.total; + } } /** @@ -612,14 +579,15 @@ public int size() { * @throws IOException if an I/O error has occurred */ public void close() throws IOException { - if (closeRequested) + if (closeRequested) { return; + } closeRequested = true; synchronized (this) { // Close streams, release their inflaters synchronized (streams) { - if (false == streams.isEmpty()) { + if (!streams.isEmpty()) { Map copy = new HashMap<>(streams); streams.clear(); for (Map.Entry e : copy.entrySet()) { @@ -631,21 +599,17 @@ public void close() throws IOException { } } } - // Release cached inflaters - Inflater inf; synchronized (inflaterCache) { - while (null != (inf = inflaterCache.poll())) { + Inflater inf; + while ((inf = inflaterCache.poll()) != null) { inf.end(); } } - - if (jzfile != 0) { - // Close the zip file - long zf = this.jzfile; - jzfile = 0; - - close(zf); + // Release zip src + if (zsrc != null) { + Source.close(zsrc); + zsrc = null; } } } @@ -668,14 +632,11 @@ protected void finalize() throws IOException { close(); } - private static native void close(long jzfile); - private void ensureOpen() { if (closeRequested) { throw new IllegalStateException("zip file closed"); } - - if (jzfile == 0) { + if (zsrc == null) { throw new IllegalStateException("The object is not initialized."); } } @@ -691,23 +652,86 @@ private void ensureOpenOrZipException() throws IOException { * (possibly compressed) zip file entry. */ private class ZipFileInputStream extends InputStream { - private volatile boolean zfisCloseRequested = false; - protected long jzentry; // address of jzentry data + private volatile boolean closeRequested = false; private long pos; // current position within entry data protected long rem; // number of remaining bytes within entry protected long size; // uncompressed size of this entry - ZipFileInputStream(long jzentry) { - pos = 0; - rem = getEntryCSize(jzentry); - size = getEntrySize(jzentry); - this.jzentry = jzentry; + ZipFileInputStream(byte[] cen, int cenpos) throws IOException { + rem = CENSIZ(cen, cenpos); + size = CENLEN(cen, cenpos); + pos = CENOFF(cen, cenpos); + // zip64 + if (rem == ZIP64_MAGICVAL || size == ZIP64_MAGICVAL || + pos == ZIP64_MAGICVAL) { + checkZIP64(cen, cenpos); + } + // negative for lazy initialization, see getDataOffset(); + pos = - (pos + ZipFile.this.zsrc.locpos); + } + + private void checkZIP64(byte[] cen, int cenpos) throws IOException { + int off = cenpos + CENHDR + CENNAM(cen, cenpos); + int end = off + CENEXT(cen, cenpos); + while (off + 4 < end) { + int tag = get16(cen, off); + int sz = get16(cen, off + 2); + off += 4; + if (off + sz > end) // invalid data + break; + if (tag == EXTID_ZIP64) { + if (size == ZIP64_MAGICVAL) { + if (sz < 8 || (off + 8) > end) + break; + size = get64(cen, off); + sz -= 8; + off += 8; + } + if (rem == ZIP64_MAGICVAL) { + if (sz < 8 || (off + 8) > end) + break; + rem = get64(cen, off); + sz -= 8; + off += 8; + } + if (pos == ZIP64_MAGICVAL) { + if (sz < 8 || (off + 8) > end) + break; + pos = get64(cen, off); + sz -= 8; + off += 8; + } + break; + } + off += sz; + } + } + + /* The Zip file spec explicitly allows the LOC extra data size to + * be different from the CEN extra data size. Since we cannot trust + * the CEN extra data size, we need to read the LOC to determine + * the entry data offset. + */ + private long initDataOffset() throws IOException { + if (pos <= 0) { + byte[] loc = new byte[LOCHDR]; + pos = -pos; + int len = ZipFile.this.zsrc.readFullyAt(loc, 0, loc.length, pos); + if (len != LOCHDR) { + throw new ZipException("ZipFile error reading zip file"); + } + if (LOCSIG(loc) != LOCSIG) { + throw new ZipException("ZipFile invalid LOC header (bad signature)"); + } + pos += LOCHDR + LOCNAM(loc) + LOCEXT(loc); + } + return pos; } public int read(byte b[], int off, int len) throws IOException { synchronized (ZipFile.this) { - long rem = this.rem; - long pos = this.pos; + ensureOpenOrZipException(); + initDataOffset(); if (rem == 0) { return -1; } @@ -717,14 +741,10 @@ public int read(byte b[], int off, int len) throws IOException { if (len > rem) { len = (int) rem; } - - // Check if ZipFile open - ensureOpenOrZipException(); - len = ZipFile.read(ZipFile.this.jzfile, jzentry, pos, b, - off, len); + len = ZipFile.this.zsrc.readAt(b, off, len, pos); if (len > 0) { - this.pos = (pos + len); - this.rem = (rem - len); + pos += len; + rem -= len; } } if (rem == 0) { @@ -742,11 +762,16 @@ public int read() throws IOException { } } - public long skip(long n) { - if (n > rem) - n = rem; - pos += n; - rem -= n; + public long skip(long n) throws IOException { + synchronized (ZipFile.this) { + ensureOpenOrZipException(); + initDataOffset(); + if (n > rem) { + n = rem; + } + pos += n; + rem -= n; + } if (rem == 0) { close(); } @@ -762,17 +787,11 @@ public long size() { } public void close() { - if (zfisCloseRequested) + if (closeRequested) { return; - zfisCloseRequested = true; - - rem = 0; - synchronized (ZipFile.this) { - if (jzentry != 0 && ZipFile.this.jzfile != 0) { - freeEntry(ZipFile.this.jzfile, jzentry); - jzentry = 0; - } } + closeRequested = true; + rem = 0; synchronized (streams) { streams.remove(this); } @@ -787,40 +806,492 @@ protected void finalize() { SharedSecrets.setJavaUtilZipFileAccess( new JavaUtilZipFileAccess() { public boolean startsWithLocHeader(ZipFile zip) { - return zip.startsWithLocHeader(); + return zip.zsrc.locsig; } - } + public String[] getMetaInfEntryNames(ZipFile zip) { + return zip.getMetaInfEntryNames(); + } + } ); } - /** - * Returns {@code true} if, and only if, the zip file begins with {@code - * LOCSIG}. + /* + * Returns an array of strings representing the names of all entries + * that begin with "META-INF/" (case ignored). This method is used + * in JarFile, via SharedSecrets, as an optimization when looking up + * manifest and signature file entries. Returns null if no entries + * were found. */ - private boolean startsWithLocHeader() { - return locsig; + private String[] getMetaInfEntryNames() { + synchronized (this) { + ensureOpen(); + if (zsrc.metanames.size() == 0) { + return null; + } + String[] names = new String[zsrc.metanames.size()]; + byte[] cen = zsrc.cen; + for (int i = 0; i < names.length; i++) { + int pos = zsrc.metanames.get(i); + names[i] = zc.toStringUTF8(cen, pos + CENHDR, CENNAM(cen, pos)); + } + return names; + } } - private static native long open(String name, int mode, long lastModified, - boolean usemmap) throws IOException; - private static native int getTotal(long jzfile); - private static native boolean startsWithLOC(long jzfile); - private static native int read(long jzfile, long jzentry, - long pos, byte[] b, int off, int len); - - // access to the native zentry object - private static native long getEntryTime(long jzentry); - private static native long getEntryCrc(long jzentry); - private static native long getEntryCSize(long jzentry); - private static native long getEntrySize(long jzentry); - private static native int getEntryMethod(long jzentry); - private static native int getEntryFlag(long jzentry); - private static native byte[] getCommentBytes(long jzfile); - - private static final int JZENTRY_NAME = 0; - private static final int JZENTRY_EXTRA = 1; - private static final int JZENTRY_COMMENT = 2; - private static native byte[] getEntryBytes(long jzentry, int type); - - private static native String getZipMessage(long jzfile); + private static class Source { + private final Key key; // the key in files + private int refs = 1; + + private RandomAccessFile zfile; // zfile of the underlying zip file + private byte[] cen; // CEN & ENDHDR + private long locpos; // position of first LOC header (usually 0) + private byte[] comment; // zip file comment + // list of meta entries in META-INF dir + private ArrayList metanames = new ArrayList<>(); + private final boolean locsig; // true, if zip file starts with LOCSIG (usually true) + + // A Hashmap for all entries. + // + // A cen entry of Zip/JAR file. As we have one for every entry in every active Zip/JAR, + // We might have a lot of these in a typical system. In order to save space we don't + // keep the name in memory, but merely remember a 32 bit {@code hash} value of the + // entry name and its offset {@code pos} in the central directory hdeader. + // + // private static class Entry { + // int hash; // 32 bit hashcode on name + // int next; // hash chain: index into entries + // int pos; // Offset of central directory file header + // } + // private Entry[] entries; // array of hashed cen entry + // + // To reduce the total size of entries further, we use a int[] here to store 3 "int" + // {@code hash}, {@code next and {@code "pos for each entry. The entry can then be + // referred by their index of their positions in the {@code entries}. + // + private int[] entries; // array of hashed cen entry + private int addEntry(int index, int hash, int next, int pos) { + entries[index++] = hash; + entries[index++] = next; + entries[index++] = pos; + return index; + } + private int getEntryHash(int index) { return entries[index]; } + private int getEntryNext(int index) { return entries[index + 1]; } + private int getEntryPos(int index) { return entries[index + 2]; } + private static final int ZIP_ENDCHAIN = -1; + private int total; // total number of entries + private int[] table; // Hash chain heads: indexes into entries + private int tablelen; // number of hash heads + + private static class Key { + BasicFileAttributes attrs; + File file; + + public Key(File file, BasicFileAttributes attrs) { + this.attrs = attrs; + this.file = file; + } + + public int hashCode() { + long t = attrs.lastModifiedTime().toMillis(); + return ((int)(t ^ (t >>> 32))) + file.hashCode(); + } + + public boolean equals(Object obj) { + if (obj instanceof Key) { + Key key = (Key)obj; + if (!attrs.lastModifiedTime().equals(key.attrs.lastModifiedTime())) { + return false; + } + Object fk = attrs.fileKey(); + if (fk != null) { + return fk.equals(key.attrs.fileKey()); + } else { + return file.equals(key.file); + } + } + return false; + } + } + private static final HashMap files = new HashMap<>(); + + public static Source get(File file, boolean toDelete) throws IOException { + Key key = new Key(file, + Files.readAttributes(file.toPath(), BasicFileAttributes.class)); + Source src = null; + synchronized (files) { + src = files.get(key); + if (src != null) { + src.refs++; + return src; + } + } + src = new Source(key, toDelete); + synchronized (files) { + if (files.containsKey(key)) { // someone else put in first + src.close(); // close the newly created one + src = files.get(key); + src.refs++; + return src; + } + files.put(key, src); + return src; + } + } + + private static void close(Source src) throws IOException { + synchronized (files) { + if (--src.refs == 0) { + files.remove(src.key); + src.close(); + } + } + } + + private Source(Key key, boolean toDelete) throws IOException { + this.key = key; + this.zfile = new RandomAccessFile(key.file, "r"); + if (toDelete) { + key.file.delete(); + } + initCEN(-1); + byte[] buf = new byte[4]; + readFullyAt(buf, 0, 4, 0); + this.locsig = (LOCSIG(buf) != LOCSIG); + } + + private void close() throws IOException { + zfile.close(); + zfile = null; + cen = null; + entries = null; + table = null; + metanames = null; + } + + private static final int BUF_SIZE = 8192; + private final int readFullyAt(byte[] buf, int off, int len, long pos) + throws IOException + { + synchronized(zfile) { + zfile.seek(pos); + int N = len; + while (N > 0) { + int n = Math.min(BUF_SIZE, N); + zfile.readFully(buf, off, n); + off += n; + N -= n; + } + return len; + } + } + + private final int readAt(byte[] buf, int off, int len, long pos) + throws IOException + { + synchronized(zfile) { + zfile.seek(pos); + return zfile.read(buf, off, len); + } + } + + private static final int hashN(byte[] a, int off, int len) { + int h = 1; + while (len-- > 0) { + h = 31 * h + a[off++]; + } + return h; + } + + private static final int hash_append(int hash, byte b) { + return hash * 31 + b; + } + + private static class End { + int centot; // 4 bytes + long cenlen; // 4 bytes + long cenoff; // 4 bytes + long endpos; // 4 bytes + } + + /* + * Searches for end of central directory (END) header. The contents of + * the END header will be read and placed in endbuf. Returns the file + * position of the END header, otherwise returns -1 if the END header + * was not found or an error occurred. + */ + private End findEND() throws IOException { + long ziplen = zfile.length(); + if (ziplen <= 0) + zerror("zip file is empty"); + End end = new End(); + byte[] buf = new byte[READBLOCKSZ]; + long minHDR = (ziplen - END_MAXLEN) > 0 ? ziplen - END_MAXLEN : 0; + long minPos = minHDR - (buf.length - ENDHDR); + for (long pos = ziplen - buf.length; pos >= minPos; pos -= (buf.length - ENDHDR)) { + int off = 0; + if (pos < 0) { + // Pretend there are some NUL bytes before start of file + off = (int)-pos; + Arrays.fill(buf, 0, off, (byte)0); + } + int len = buf.length - off; + if (readFullyAt(buf, off, len, pos + off) != len ) { + zerror("zip END header not found"); + } + // Now scan the block backwards for END header signature + for (int i = buf.length - ENDHDR; i >= 0; i--) { + if (buf[i+0] == (byte)'P' && + buf[i+1] == (byte)'K' && + buf[i+2] == (byte)'\005' && + buf[i+3] == (byte)'\006') { + // Found ENDSIG header + byte[] endbuf = Arrays.copyOfRange(buf, i, i + ENDHDR); + end.centot = ENDTOT(endbuf); + end.cenlen = ENDSIZ(endbuf); + end.cenoff = ENDOFF(endbuf); + end.endpos = pos + i; + int comlen = ENDCOM(endbuf); + if (end.endpos + ENDHDR + comlen != ziplen) { + // ENDSIG matched, however the size of file comment in it does + // not match the real size. One "common" cause for this problem + // is some "extra" bytes are padded at the end of the zipfile. + // Let's do some extra verification, we don't care about the + // performance in this situation. + byte[] sbuf = new byte[4]; + long cenpos = end.endpos - end.cenlen; + long locpos = cenpos - end.cenoff; + if (cenpos < 0 || + locpos < 0 || + readFullyAt(sbuf, 0, sbuf.length, cenpos) != 4 || + GETSIG(sbuf) != CENSIG || + readFullyAt(sbuf, 0, sbuf.length, locpos) != 4 || + GETSIG(sbuf) != LOCSIG) { + continue; + } + } + if (comlen > 0) { // this zip file has comlen + comment = new byte[comlen]; + if (readFullyAt(comment, 0, comlen, end.endpos + ENDHDR) != comlen) { + zerror("zip comment read failed"); + } + } + if (end.cenlen == ZIP64_MAGICVAL || + end.cenoff == ZIP64_MAGICVAL || + end.centot == ZIP64_MAGICCOUNT) + { + // need to find the zip64 end; + try { + byte[] loc64 = new byte[ZIP64_LOCHDR]; + if (readFullyAt(loc64, 0, loc64.length, end.endpos - ZIP64_LOCHDR) + != loc64.length || GETSIG(loc64) != ZIP64_LOCSIG) { + return end; + } + long end64pos = ZIP64_LOCOFF(loc64); + byte[] end64buf = new byte[ZIP64_ENDHDR]; + if (readFullyAt(end64buf, 0, end64buf.length, end64pos) + != end64buf.length || GETSIG(end64buf) != ZIP64_ENDSIG) { + return end; + } + // end64 found, re-calcualte everything. + end.cenlen = ZIP64_ENDSIZ(end64buf); + end.cenoff = ZIP64_ENDOFF(end64buf); + end.centot = (int)ZIP64_ENDTOT(end64buf); // assume total < 2g + end.endpos = end64pos; + } catch (IOException x) {} // no zip64 loc/end + } + return end; + } + } + } + zerror("zip END header not found"); + return null; //make compiler happy + } + + // Reads zip file central directory. + private void initCEN(int knownTotal) throws IOException { + if (knownTotal == -1) { + End end = findEND(); + if (end.endpos == 0) { + locpos = 0; + total = 0; + entries = new int[0]; + cen = null; + return; // only END header present + } + if (end.cenlen > end.endpos) + zerror("invalid END header (bad central directory size)"); + long cenpos = end.endpos - end.cenlen; // position of CEN table + // Get position of first local file (LOC) header, taking into + // account that there may be a stub prefixed to the zip file. + locpos = cenpos - end.cenoff; + if (locpos < 0) { + zerror("invalid END header (bad central directory offset)"); + } + // read in the CEN and END + cen = new byte[(int)(end.cenlen + ENDHDR)]; + if (readFullyAt(cen, 0, cen.length, cenpos) != end.cenlen + ENDHDR) { + zerror("read CEN tables failed"); + } + total = end.centot; + } else { + total = knownTotal; + } + // hash table for entries + entries = new int[total * 3]; + tablelen = ((total/2) | 1); // Odd -> fewer collisions + table = new int[tablelen]; + Arrays.fill(table, ZIP_ENDCHAIN); + int idx = 0; + int hash = 0; + int next = -1; + + // list for all meta entries + metanames = new ArrayList<>(); + + // Iterate through the entries in the central directory + int i = 0; + int hsh = 0; + int pos = 0; + int limit = cen.length - ENDHDR; + while (pos + CENHDR <= limit) { + if (i >= total) { + // This will only happen if the zip file has an incorrect + // ENDTOT field, which usually means it contains more than + // 65535 entries. + initCEN(countCENHeaders(cen, limit)); + return; + } + if (CENSIG(cen, pos) != CENSIG) + zerror("invalid CEN header (bad signature)"); + int method = CENHOW(cen, pos); + int nlen = CENNAM(cen, pos); + int elen = CENEXT(cen, pos); + int clen = CENCOM(cen, pos); + if ((CENFLG(cen, pos) & 1) != 0) + zerror("invalid CEN header (encrypted entry)"); + if (method != STORED && method != DEFLATED) + zerror("invalid CEN header (bad compression method: " + method + ")"); + if (pos + CENHDR + nlen > limit) + zerror("invalid CEN header (bad header size)"); + // Record the CEN offset and the name hash in our hash cell. + hash = hashN(cen, pos + CENHDR, nlen); + hsh = (hash & 0x7fffffff) % tablelen; + next = table[hsh]; + table[hsh] = idx; + idx = addEntry(idx, hash, next, pos); + // Adds name to metanames. + if (isMetaName(cen, pos + CENHDR, nlen)) { + metanames.add(pos); + } + // skip ext and comment + pos += (CENHDR + nlen + elen + clen); + i++; + } + total = i; + if (pos + ENDHDR != cen.length) { + zerror("invalid CEN header (bad header size)"); + } + } + + private static void zerror(String msg) throws ZipException { + throw new ZipException(msg); + } + + /* + * Returns the {@code pos} of the zip cen entry corresponding to the + * specified entry name, or -1 if not found. + */ + private int getEntryPos(byte[] name, boolean addSlash) { + if (total == 0) { + return -1; + } + int hsh = hashN(name, 0, name.length); + int idx = table[(hsh & 0x7fffffff) % tablelen]; + /* + * This while loop is an optimization where a double lookup + * for name and name+/ is being performed. The name char + * array has enough room at the end to try again with a + * slash appended if the first table lookup does not succeed. + */ + while(true) { + /* + * Search down the target hash chain for a entry whose + * 32 bit hash matches the hashed name. + */ + while (idx != ZIP_ENDCHAIN) { + if (getEntryHash(idx) == hsh) { + // The CEN name must match the specfied one + int pos = getEntryPos(idx); + if (name.length == CENNAM(cen, pos)) { + boolean matched = true; + int nameoff = pos + CENHDR; + for (int i = 0; i < name.length; i++) { + if (name[i] != cen[nameoff++]) { + matched = false; + break; + } + } + if (matched) { + return pos; + } + } + } + idx = getEntryNext(idx); + } + /* If not addSlash, or slash is already there, we are done */ + if (!addSlash || name[name.length - 1] == '/') { + return -1; + } + /* Add slash and try once more */ + name = Arrays.copyOf(name, name.length + 1); + name[name.length - 1] = '/'; + hsh = hash_append(hsh, (byte)'/'); + //idx = table[hsh % tablelen]; + idx = table[(hsh & 0x7fffffff) % tablelen]; + addSlash = false; + } + } + + private static byte[] metainf = new byte[] { + 'M', 'E', 'T', 'A', '-', 'I' , 'N', 'F', '/', + }; + + /* + * Returns true if the specified entry's name begins with the string + * "META-INF/" irrespective of case. + */ + private static boolean isMetaName(byte[] name, int off, int len) { + if (len < 9 || (name[off] != 'M' && name[off] != 'm')) { // sizeof("META-INF/") - 1 + return false; + } + off++; + for (int i = 1; i < metainf.length; i++) { + byte c = name[off++]; + // Avoid toupper; it's locale-dependent + if (c >= 'a' && c <= 'z') { + c += 'A' - 'a'; + } + if (metainf[i] != c) { + return false; + } + } + return true; + } + + /* + * Counts the number of CEN headers in a central directory extending + * from BEG to END. Might return a bogus answer if the zip file is + * corrupt, but will not crash. + */ + static int countCENHeaders(byte[] cen, int end) { + int count = 0; + int pos = 0; + while (pos + CENHDR <= end) { + count++; + pos += (CENHDR + CENNAM(cen, pos) + CENEXT(cen, pos) + CENCOM(cen, pos)); + } + return count; + } + } } diff --git a/jdk/src/java.base/share/classes/java/util/zip/ZipUtils.java b/jdk/src/java.base/share/classes/java/util/zip/ZipUtils.java index 81882fdcec6..a6632f0fa83 100644 --- a/jdk/src/java.base/share/classes/java/util/zip/ZipUtils.java +++ b/jdk/src/java.base/share/classes/java/util/zip/ZipUtils.java @@ -31,6 +31,8 @@ import java.time.ZoneId; import java.util.concurrent.TimeUnit; +import static java.util.zip.ZipConstants.ENDHDR; + class ZipUtils { // used to adjust values between Windows and java epoch @@ -133,7 +135,7 @@ public static long javaToExtendedDosTime(long time) { * The bytes are assumed to be in Intel (little-endian) byte order. */ public static final int get16(byte b[], int off) { - return Byte.toUnsignedInt(b[off]) | (Byte.toUnsignedInt(b[off+1]) << 8); + return (b[off] & 0xff) | ((b[off + 1] & 0xff) << 8); } /** @@ -160,4 +162,79 @@ public static final long get64(byte b[], int off) { public static final int get32S(byte b[], int off) { return (get16(b, off) | (get16(b, off+2) << 16)); } + + // fields access methods + static final int CH(byte[] b, int n) { + return b[n] & 0xff ; + } + + static final int SH(byte[] b, int n) { + return (b[n] & 0xff) | ((b[n + 1] & 0xff) << 8); + } + + static final long LG(byte[] b, int n) { + return ((SH(b, n)) | (SH(b, n + 2) << 16)) & 0xffffffffL; + } + + static final long LL(byte[] b, int n) { + return (LG(b, n)) | (LG(b, n + 4) << 32); + } + + static final long GETSIG(byte[] b) { + return LG(b, 0); + } + + // local file (LOC) header fields + static final long LOCSIG(byte[] b) { return LG(b, 0); } // signature + static final int LOCVER(byte[] b) { return SH(b, 4); } // version needed to extract + static final int LOCFLG(byte[] b) { return SH(b, 6); } // general purpose bit flags + static final int LOCHOW(byte[] b) { return SH(b, 8); } // compression method + static final long LOCTIM(byte[] b) { return LG(b, 10);} // modification time + static final long LOCCRC(byte[] b) { return LG(b, 14);} // crc of uncompressed data + static final long LOCSIZ(byte[] b) { return LG(b, 18);} // compressed data size + static final long LOCLEN(byte[] b) { return LG(b, 22);} // uncompressed data size + static final int LOCNAM(byte[] b) { return SH(b, 26);} // filename length + static final int LOCEXT(byte[] b) { return SH(b, 28);} // extra field length + + // extra local (EXT) header fields + static final long EXTCRC(byte[] b) { return LG(b, 4);} // crc of uncompressed data + static final long EXTSIZ(byte[] b) { return LG(b, 8);} // compressed size + static final long EXTLEN(byte[] b) { return LG(b, 12);} // uncompressed size + + // end of central directory header (END) fields + static final int ENDSUB(byte[] b) { return SH(b, 8); } // number of entries on this disk + static final int ENDTOT(byte[] b) { return SH(b, 10);} // total number of entries + static final long ENDSIZ(byte[] b) { return LG(b, 12);} // central directory size + static final long ENDOFF(byte[] b) { return LG(b, 16);} // central directory offset + static final int ENDCOM(byte[] b) { return SH(b, 20);} // size of zip file comment + static final int ENDCOM(byte[] b, int off) { return SH(b, off + 20);} + + // zip64 end of central directory recoder fields + static final long ZIP64_ENDTOD(byte[] b) { return LL(b, 24);} // total number of entries on disk + static final long ZIP64_ENDTOT(byte[] b) { return LL(b, 32);} // total number of entries + static final long ZIP64_ENDSIZ(byte[] b) { return LL(b, 40);} // central directory size + static final long ZIP64_ENDOFF(byte[] b) { return LL(b, 48);} // central directory offset + static final long ZIP64_LOCOFF(byte[] b) { return LL(b, 8);} // zip64 end offset + + // central directory header (CEN) fields + static final long CENSIG(byte[] b, int pos) { return LG(b, pos + 0); } + static final int CENVEM(byte[] b, int pos) { return SH(b, pos + 4); } + static final int CENVER(byte[] b, int pos) { return SH(b, pos + 6); } + static final int CENFLG(byte[] b, int pos) { return SH(b, pos + 8); } + static final int CENHOW(byte[] b, int pos) { return SH(b, pos + 10);} + static final long CENTIM(byte[] b, int pos) { return LG(b, pos + 12);} + static final long CENCRC(byte[] b, int pos) { return LG(b, pos + 16);} + static final long CENSIZ(byte[] b, int pos) { return LG(b, pos + 20);} + static final long CENLEN(byte[] b, int pos) { return LG(b, pos + 24);} + static final int CENNAM(byte[] b, int pos) { return SH(b, pos + 28);} + static final int CENEXT(byte[] b, int pos) { return SH(b, pos + 30);} + static final int CENCOM(byte[] b, int pos) { return SH(b, pos + 32);} + static final int CENDSK(byte[] b, int pos) { return SH(b, pos + 34);} + static final int CENATT(byte[] b, int pos) { return SH(b, pos + 36);} + static final long CENATX(byte[] b, int pos) { return LG(b, pos + 38);} + static final long CENOFF(byte[] b, int pos) { return LG(b, pos + 42);} + + // The END header is followed by a variable length comment of size < 64k. + static final long END_MAXLEN = 0xFFFF + ENDHDR; + static final int READBLOCKSZ = 128; } diff --git a/jdk/src/java.base/share/classes/jdk/internal/misc/JavaUtilZipFileAccess.java b/jdk/src/java.base/share/classes/jdk/internal/misc/JavaUtilZipFileAccess.java index df10fda22ca..9b9b1e85788 100644 --- a/jdk/src/java.base/share/classes/jdk/internal/misc/JavaUtilZipFileAccess.java +++ b/jdk/src/java.base/share/classes/jdk/internal/misc/JavaUtilZipFileAccess.java @@ -29,5 +29,6 @@ public interface JavaUtilZipFileAccess { public boolean startsWithLocHeader(ZipFile zip); + public String[] getMetaInfEntryNames(ZipFile zip); } diff --git a/jdk/src/java.base/share/classes/sun/misc/VM.java b/jdk/src/java.base/share/classes/sun/misc/VM.java index 0c75f10c657..37dc3b38fa1 100644 --- a/jdk/src/java.base/share/classes/sun/misc/VM.java +++ b/jdk/src/java.base/share/classes/sun/misc/VM.java @@ -274,9 +274,6 @@ public static void saveAndRemoveProperties(Properties props) { // used by java.lang.Integer.IntegerCache props.remove("java.lang.Integer.IntegerCache.high"); - // used by java.util.zip.ZipFile - props.remove("sun.zip.disableMemoryMapping"); - // used by sun.launcher.LauncherHelper props.remove("sun.java.launcher.diag"); } diff --git a/jdk/src/java.base/share/native/libzip/ZipFile.c b/jdk/src/java.base/share/native/libzip/ZipFile.c deleted file mode 100644 index d7a21a6cf88..00000000000 --- a/jdk/src/java.base/share/native/libzip/ZipFile.c +++ /dev/null @@ -1,406 +0,0 @@ -/* - * Copyright (c) 1998, 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. - */ - -/* - * Native method support for java.util.zip.ZipFile - */ - -#include -#include -#include -#include -#include -#include -#include "jlong.h" -#include "jvm.h" -#include "jni.h" -#include "jni_util.h" -#include "zip_util.h" -#ifdef WIN32 -#include "io_util_md.h" -#else -#include "io_util.h" -#endif - -#include "java_util_zip_ZipFile.h" -#include "java_util_jar_JarFile.h" - -#define DEFLATED 8 -#define STORED 0 - -static jfieldID jzfileID; - -static int OPEN_READ = java_util_zip_ZipFile_OPEN_READ; -static int OPEN_DELETE = java_util_zip_ZipFile_OPEN_DELETE; - - -/* - * Declare library specific JNI_Onload entry if static build - */ -DEF_STATIC_JNI_OnLoad - -JNIEXPORT void JNICALL -Java_java_util_zip_ZipFile_initIDs(JNIEnv *env, jclass cls) -{ - jzfileID = (*env)->GetFieldID(env, cls, "jzfile", "J"); - assert(jzfileID != 0); -} - -static void -ThrowZipException(JNIEnv *env, const char *msg) -{ - jstring s = NULL; - jobject x; - - if (msg != NULL) { - s = JNU_NewStringPlatform(env, msg); - } - if (s != NULL) { - x = JNU_NewObjectByName(env, - "java/util/zip/ZipException", - "(Ljava/lang/String;)V", s); - if (x != NULL) { - (*env)->Throw(env, x); - } - } -} - -JNIEXPORT jlong JNICALL -Java_java_util_zip_ZipFile_open(JNIEnv *env, jclass cls, jstring name, - jint mode, jlong lastModified, - jboolean usemmap) -{ - const char *path = JNU_GetStringPlatformChars(env, name, 0); - char *msg = 0; - jlong result = 0; - int flag = 0; - jzfile *zip = 0; - - if (mode & OPEN_READ) flag |= O_RDONLY; - - if (path != 0) { - zip = ZIP_Get_From_Cache(path, &msg, lastModified); - if (zip == 0 && msg == 0) { - ZFILE zfd = 0; -#ifdef WIN32 - if (mode & OPEN_DELETE) flag |= O_TEMPORARY; - zfd = winFileHandleOpen(env, name, flag); - if (zfd == -1) { - /* Exception already pending. */ - goto finally; - } -#else - zfd = open(path, flag, 0); - if (zfd < 0) { - throwFileNotFoundException(env, name); - goto finally; - } - if (mode & OPEN_DELETE) { - unlink(path); - } -#endif - zip = ZIP_Put_In_Cache0(path, zfd, &msg, lastModified, usemmap); - } - - if (zip != 0) { - result = ptr_to_jlong(zip); - } else if (msg != 0) { - ThrowZipException(env, msg); - free(msg); - } else if (errno == ENOMEM) { - JNU_ThrowOutOfMemoryError(env, 0); - } else { - ThrowZipException(env, "error in opening zip file"); - } -finally: - JNU_ReleaseStringPlatformChars(env, name, path); - } - return result; -} - -JNIEXPORT jint JNICALL -Java_java_util_zip_ZipFile_getTotal(JNIEnv *env, jclass cls, jlong zfile) -{ - jzfile *zip = jlong_to_ptr(zfile); - - return zip->total; -} - -JNIEXPORT jboolean JNICALL -Java_java_util_zip_ZipFile_startsWithLOC(JNIEnv *env, jclass cls, jlong zfile) -{ - jzfile *zip = jlong_to_ptr(zfile); - - return zip->locsig; -} - -JNIEXPORT void JNICALL -Java_java_util_zip_ZipFile_close(JNIEnv *env, jclass cls, jlong zfile) -{ - ZIP_Close(jlong_to_ptr(zfile)); -} - -JNIEXPORT jlong JNICALL -Java_java_util_zip_ZipFile_getEntry(JNIEnv *env, jclass cls, jlong zfile, - jbyteArray name, jboolean addSlash) -{ -#define MAXNAME 1024 - jzfile *zip = jlong_to_ptr(zfile); - jsize ulen = (*env)->GetArrayLength(env, name); - char buf[MAXNAME+2], *path; - jzentry *ze; - - if (ulen > MAXNAME) { - path = malloc(ulen + 2); - if (path == 0) { - JNU_ThrowOutOfMemoryError(env, 0); - return 0; - } - } else { - path = buf; - } - (*env)->GetByteArrayRegion(env, name, 0, ulen, (jbyte *)path); - path[ulen] = '\0'; - ze = ZIP_GetEntry2(zip, path, (jint)ulen, addSlash); - if (path != buf) { - free(path); - } - return ptr_to_jlong(ze); -} - -JNIEXPORT void JNICALL -Java_java_util_zip_ZipFile_freeEntry(JNIEnv *env, jclass cls, jlong zfile, - jlong zentry) -{ - jzfile *zip = jlong_to_ptr(zfile); - jzentry *ze = jlong_to_ptr(zentry); - ZIP_FreeEntry(zip, ze); -} - -JNIEXPORT jlong JNICALL -Java_java_util_zip_ZipFile_getNextEntry(JNIEnv *env, jclass cls, jlong zfile, - jint n) -{ - jzentry *ze = ZIP_GetNextEntry(jlong_to_ptr(zfile), n); - return ptr_to_jlong(ze); -} - -JNIEXPORT jint JNICALL -Java_java_util_zip_ZipFile_getEntryMethod(JNIEnv *env, jclass cls, jlong zentry) -{ - jzentry *ze = jlong_to_ptr(zentry); - return ze->csize != 0 ? DEFLATED : STORED; -} - -JNIEXPORT jint JNICALL -Java_java_util_zip_ZipFile_getEntryFlag(JNIEnv *env, jclass cls, jlong zentry) -{ - jzentry *ze = jlong_to_ptr(zentry); - return ze->flag; -} - -JNIEXPORT jlong JNICALL -Java_java_util_zip_ZipFile_getEntryCSize(JNIEnv *env, jclass cls, jlong zentry) -{ - jzentry *ze = jlong_to_ptr(zentry); - return ze->csize != 0 ? ze->csize : ze->size; -} - -JNIEXPORT jlong JNICALL -Java_java_util_zip_ZipFile_getEntrySize(JNIEnv *env, jclass cls, jlong zentry) -{ - jzentry *ze = jlong_to_ptr(zentry); - return ze->size; -} - -JNIEXPORT jlong JNICALL -Java_java_util_zip_ZipFile_getEntryTime(JNIEnv *env, jclass cls, jlong zentry) -{ - jzentry *ze = jlong_to_ptr(zentry); - return (jlong)ze->time & 0xffffffffUL; -} - -JNIEXPORT jlong JNICALL -Java_java_util_zip_ZipFile_getEntryCrc(JNIEnv *env, jclass cls, jlong zentry) -{ - jzentry *ze = jlong_to_ptr(zentry); - return (jlong)ze->crc & 0xffffffffUL; -} - -JNIEXPORT jbyteArray JNICALL -Java_java_util_zip_ZipFile_getCommentBytes(JNIEnv *env, - jclass cls, - jlong zfile) -{ - jzfile *zip = jlong_to_ptr(zfile); - jbyteArray jba = NULL; - - if (zip->comment != NULL) { - if ((jba = (*env)->NewByteArray(env, zip->clen)) == NULL) - return NULL; - (*env)->SetByteArrayRegion(env, jba, 0, zip->clen, (jbyte*)zip->comment); - } - return jba; -} - -JNIEXPORT jbyteArray JNICALL -Java_java_util_zip_ZipFile_getEntryBytes(JNIEnv *env, - jclass cls, - jlong zentry, jint type) -{ - jzentry *ze = jlong_to_ptr(zentry); - int len = 0; - jbyteArray jba = NULL; - switch (type) { - case java_util_zip_ZipFile_JZENTRY_NAME: - if (ze->name != 0) { - len = (int)ze->nlen; - // Unlike for extra and comment, we never return null for - // an (extremely rarely seen) empty name - if ((jba = (*env)->NewByteArray(env, len)) == NULL) - break; - (*env)->SetByteArrayRegion(env, jba, 0, len, (jbyte *)ze->name); - } - break; - case java_util_zip_ZipFile_JZENTRY_EXTRA: - if (ze->extra != 0) { - unsigned char *bp = (unsigned char *)&ze->extra[0]; - len = (bp[0] | (bp[1] << 8)); - if (len <= 0 || (jba = (*env)->NewByteArray(env, len)) == NULL) - break; - (*env)->SetByteArrayRegion(env, jba, 0, len, &ze->extra[2]); - } - break; - case java_util_zip_ZipFile_JZENTRY_COMMENT: - if (ze->comment != 0) { - len = (int)strlen(ze->comment); - if (len == 0 || (jba = (*env)->NewByteArray(env, len)) == NULL) - break; - (*env)->SetByteArrayRegion(env, jba, 0, len, (jbyte*)ze->comment); - } - break; - } - return jba; -} - -JNIEXPORT jint JNICALL -Java_java_util_zip_ZipFile_read(JNIEnv *env, jclass cls, jlong zfile, - jlong zentry, jlong pos, jbyteArray bytes, - jint off, jint len) -{ - jzfile *zip = jlong_to_ptr(zfile); - char *msg; - -#define BUFSIZE 8192 - /* copy via tmp stack buffer: */ - jbyte buf[BUFSIZE]; - - if (len > BUFSIZE) { - len = BUFSIZE; - } - - ZIP_Lock(zip); - len = ZIP_Read(zip, jlong_to_ptr(zentry), pos, buf, len); - msg = zip->msg; - ZIP_Unlock(zip); - if (len != -1) { - (*env)->SetByteArrayRegion(env, bytes, off, len, buf); - } - - if (len == -1) { - if (msg != 0) { - ThrowZipException(env, msg); - } else { - char errmsg[128]; - sprintf(errmsg, "errno: %d, error: %s\n", - errno, "Error reading ZIP file"); - JNU_ThrowIOExceptionWithLastError(env, errmsg); - } - } - - return len; -} - -/* - * Returns an array of strings representing the names of all entries - * that begin with "META-INF/" (case ignored). This native method is - * used in JarFile as an optimization when looking up manifest and - * signature file entries. Returns null if no entries were found. - */ -JNIEXPORT jobjectArray JNICALL -Java_java_util_jar_JarFile_getMetaInfEntryNames(JNIEnv *env, jobject obj) -{ - jlong zfile = (*env)->GetLongField(env, obj, jzfileID); - jzfile *zip; - int i, count; - jobjectArray result = 0; - - if (zfile == 0) { - JNU_ThrowByName(env, - "java/lang/IllegalStateException", "zip file closed"); - return NULL; - } - zip = jlong_to_ptr(zfile); - - /* count the number of valid ZIP metanames */ - count = 0; - if (zip->metanames != 0) { - for (i = 0; i < zip->metacount; i++) { - if (zip->metanames[i] != 0) { - count++; - } - } - } - - /* If some names were found then build array of java strings */ - if (count > 0) { - jclass cls = JNU_ClassString(env); - CHECK_NULL_RETURN(cls, NULL); - result = (*env)->NewObjectArray(env, count, cls, 0); - CHECK_NULL_RETURN(result, NULL); - if (result != 0) { - for (i = 0; i < count; i++) { - jstring str = (*env)->NewStringUTF(env, zip->metanames[i]); - if (str == 0) { - break; - } - (*env)->SetObjectArrayElement(env, result, i, str); - (*env)->DeleteLocalRef(env, str); - } - } - } - return result; -} - -JNIEXPORT jstring JNICALL -Java_java_util_zip_ZipFile_getZipMessage(JNIEnv *env, jclass cls, jlong zfile) -{ - jzfile *zip = jlong_to_ptr(zfile); - char *msg = zip->msg; - if (msg == NULL) { - return NULL; - } - return JNU_NewStringPlatform(env, msg); -} diff --git a/jdk/test/java/util/zip/ZipFile/ReadZip.java b/jdk/test/java/util/zip/ZipFile/ReadZip.java index 1052642eda7..fe923e81eee 100644 --- a/jdk/test/java/util/zip/ZipFile/ReadZip.java +++ b/jdk/test/java/util/zip/ZipFile/ReadZip.java @@ -30,6 +30,7 @@ import java.io.*; import java.nio.file.Files; import java.nio.file.Paths; +import java.nio.file.NoSuchFileException; import java.nio.file.StandardCopyOption; import java.nio.file.StandardOpenOption; import java.util.zip.*; @@ -110,6 +111,6 @@ public static void main(String args[]) throws Exception { "input" + String.valueOf(new java.util.Random().nextInt()) + ".zip"))); - } catch (FileNotFoundException fnfe) {} + } catch (NoSuchFileException nsfe) {} } } diff --git a/jdk/test/java/util/zip/ZipFile/TestZipFile.java b/jdk/test/java/util/zip/ZipFile/TestZipFile.java new file mode 100644 index 00000000000..986877731db --- /dev/null +++ b/jdk/test/java/util/zip/ZipFile/TestZipFile.java @@ -0,0 +1,361 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8142508 + * @summary Tests various ZipFile apis + * @run main/manual TestZipFile + */ + +import java.io.*; +import java.lang.reflect.Method; +import java.nio.*; +import java.nio.file.*; +import java.nio.file.attribute.*; +import java.util.*; +import java.util.concurrent.*; +import java.util.zip.*; + +public class TestZipFile { + + private static Random r = new Random(); + private static int N = 50; + private static int NN = 10; + private static int ENUM = 10000; + private static int ESZ = 10000; + private static ExecutorService executor = Executors.newFixedThreadPool(20); + private static Set paths = new HashSet<>(); + + static void realMain (String[] args) throws Throwable { + + try { + for (int i = 0; i < N; i++) { + test(r.nextInt(ENUM), r.nextInt(ESZ), false, true); + test(r.nextInt(ENUM), r.nextInt(ESZ), true, true); + } + + for (int i = 0; i < NN; i++) { + test(r.nextInt(ENUM), 100000 + r.nextInt(ESZ), false, true); + test(r.nextInt(ENUM), 100000 + r.nextInt(ESZ), true, true); + testCachedDelete(); + testCachedOverwrite(); + //test(r.nextInt(ENUM), r.nextInt(ESZ), false, true); + } + + test(70000, 1000, false, true); // > 65536 entry number; + testDelete(); // OPEN_DELETE + + executor.shutdown(); + executor.awaitTermination(10, TimeUnit.MINUTES); + } finally { + for (Path path : paths) { + Files.deleteIfExists(path); + } + } + } + + static void test(int numEntry, int szMax, boolean addPrefix, boolean cleanOld) { + String name = "zftest" + r.nextInt() + ".zip"; + Zip zip = new Zip(name, numEntry, szMax, addPrefix, cleanOld); + for (int i = 0; i < NN; i++) { + executor.submit(() -> doTest(zip)); + } + } + + // test scenario: + // (1) open the ZipFile(zip) with OPEN_READ | OPEN_DELETE + // (2) test the ZipFile works correctly + // (3) check the zip is deleted after ZipFile gets closed + static void testDelete() throws Throwable { + String name = "zftest" + r.nextInt() + ".zip"; + Zip zip = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, true); + try (ZipFile zf = new ZipFile(new File(zip.name), + ZipFile.OPEN_READ | ZipFile.OPEN_DELETE )) + { + doTest0(zip, zf); + } + Path p = Paths.get(name); + if (Files.exists(p)) { + fail("Failed to delete " + name + " with OPEN_DELETE"); + } + } + + // test scenario: + // (1) keep a ZipFile(zip1) alive (in ZipFile's cache), dont close it + // (2) delete zip1 and create zip2 with the same name the zip1 with zip2 + // (3) zip1 tests should fail, but no crash + // (4) zip2 tasks should all get zip2, then pass normal testing. + static void testCachedDelete() throws Throwable { + String name = "zftest" + r.nextInt() + ".zip"; + Zip zip1 = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, true); + + try (ZipFile zf = new ZipFile(zip1.name)) { + for (int i = 0; i < NN; i++) { + executor.submit(() -> verifyNoCrash(zip1)); + } + // delete the "zip1" and create a new one to test + Zip zip2 = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, true); + /* + System.out.println("========================================"); + System.out.printf(" zip1=%s, mt=%d, enum=%d%n ->attrs=[key=%s, sz=%d, mt=%d]%n", + zip1.name, zip1.lastModified, zip1.entries.size(), + zip1.attrs.fileKey(), zip1.attrs.size(), zip1.attrs.lastModifiedTime().toMillis()); + System.out.printf(" zip2=%s, mt=%d, enum=%d%n ->attrs=[key=%s, sz=%d, mt=%d]%n", + zip2.name, zip2.lastModified, zip2.entries.size(), + zip2.attrs.fileKey(), zip2.attrs.size(), zip2.attrs.lastModifiedTime().toMillis()); + */ + for (int i = 0; i < NN; i++) { + executor.submit(() -> doTest(zip2)); + } + } + } + + // overwrite the "zip1" and create a new one to test. So the two zip files + // have the same fileKey, but probably different lastModified() + static void testCachedOverwrite() throws Throwable { + String name = "zftest" + r.nextInt() + ".zip"; + Zip zip1 = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, true); + try (ZipFile zf = new ZipFile(zip1.name)) { + for (int i = 0; i < NN; i++) { + executor.submit(() -> verifyNoCrash(zip1)); + } + // overwrite the "zip1" with new contents + Zip zip2 = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, false); + for (int i = 0; i < NN; i++) { + executor.submit(() -> doTest(zip2)); + } + } + } + + // just check the entries and contents. since the file has been either overwritten + // or deleted/rewritten, we only care if it crahes or not. + static void verifyNoCrash(Zip zip) throws RuntimeException { + try (ZipFile zf = new ZipFile(zip.name)) { + List zlist = new ArrayList(zip.entries.keySet()); + String[] elist = zf.stream().map( e -> e.getName()).toArray(String[]::new); + if (!Arrays.equals(elist, + zlist.stream().map( e -> e.getName()).toArray(String[]::new))) + { + //System.out.printf("++++++ LIST NG [%s] entries.len=%d, expected=%d+++++++%n", + // zf.getName(), elist.length, zlist.size()); + return; + } + for (ZipEntry ze : zlist) { + byte[] zdata = zip.entries.get(ze); + ZipEntry e = zf.getEntry(ze.getName()); + if (e != null) { + checkEqual(e, ze); + if (!e.isDirectory()) { + // check with readAllBytes + try (InputStream is = zf.getInputStream(e)) { + if (!Arrays.equals(zdata, is.readAllBytes())) { + //System.out.printf("++++++ BYTES NG [%s]/[%s] ++++++++%n", + // zf.getName(), ze.getName()); + } + } + } + } + } + } catch (Throwable t) { + // t.printStackTrace(); + // fail(t.toString()); + } + } + + static void checkEqual(ZipEntry x, ZipEntry y) { + if (x.getName().equals(y.getName()) && + x.isDirectory() == y.isDirectory() && + x.getMethod() == y.getMethod() && + (x.getTime() / 2000) == y.getTime() / 2000 && + x.getSize() == y.getSize() && + x.getCompressedSize() == y.getCompressedSize() && + x.getCrc() == y.getCrc() && + x.getComment().equals(y.getComment()) + ) { + pass(); + } else { + fail(x + " not equal to " + y); + System.out.printf(" %s %s%n", x.getName(), y.getName()); + System.out.printf(" %d %d%n", x.getMethod(), y.getMethod()); + System.out.printf(" %d %d%n", x.getTime(), y.getTime()); + System.out.printf(" %d %d%n", x.getSize(), y.getSize()); + System.out.printf(" %d %d%n", x.getCompressedSize(), y.getCompressedSize()); + System.out.printf(" %d %d%n", x.getCrc(), y.getCrc()); + System.out.println("-----------------"); + } + } + + static void doTest(Zip zip) throws RuntimeException { + //Thread me = Thread.currentThread(); + try (ZipFile zf = new ZipFile(zip.name)) { + doTest0(zip, zf); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + + static void doTest0(Zip zip, ZipFile zf) throws Throwable { + List list = new ArrayList(zip.entries.keySet()); + // (1) check entry list, in expected order + if (!check(Arrays.equals( + list.stream().map( e -> e.getName()).toArray(String[]::new), + zf.stream().map( e -> e.getName()).toArray(String[]::new)))) { + return; + } + // (2) shuffle, and check each entry and its bytes + Collections.shuffle(list); + for (ZipEntry ze : list) { + byte[] data = zip.entries.get(ze); + ZipEntry e = zf.getEntry(ze.getName()); + checkEqual(e, ze); + if (!e.isDirectory()) { + // check with readAllBytes + try (InputStream is = zf.getInputStream(e)) { + check(Arrays.equals(data, is.readAllBytes())); + } + // check with smaller sized buf + try (InputStream is = zf.getInputStream(e)) { + byte[] buf = new byte[(int)e.getSize()]; + int sz = r.nextInt((int)e.getSize()/4 + 1) + 1; + int off = 0; + int n; + while ((n = is.read(buf, off, buf.length - off)) > 0) { + off += n; + } + check(is.read() == -1); + check(Arrays.equals(data, buf)); + } + } + } + // (3) check getMetaInfEntryNames + String[] metas = list.stream() + .map( e -> e.getName()) + .filter( s -> s.startsWith("META-INF/")) + .sorted() + .toArray(String[]::new); + if (metas.length > 0) { + // meta-inf entries + Method getMetas = ZipFile.class.getDeclaredMethod("getMetaInfEntryNames"); + getMetas.setAccessible(true); + String[] names = (String[])getMetas.invoke(zf); + if (names == null) { + fail("Failed to get metanames from " + zf); + } else { + Arrays.sort(names); + check(Arrays.equals(names, metas)); + } + } + } + + private static class Zip { + String name; + Map entries; + BasicFileAttributes attrs; + long lastModified; + + Zip(String name, int num, int szMax, boolean prefix, boolean clean) { + this.name = name; + entries = new LinkedHashMap<>(num); + try { + Path p = Paths.get(name); + if (clean) { + Files.deleteIfExists(p); + } + paths.add(p); + } catch (Exception x) { + throw (RuntimeException)x; + } + + try (FileOutputStream fos = new FileOutputStream(name); + BufferedOutputStream bos = new BufferedOutputStream(fos); + ZipOutputStream zos = new ZipOutputStream(bos)) + { + if (prefix) { + byte[] bytes = new byte[r.nextInt(1000)]; + r.nextBytes(bytes); + bos.write(bytes); + } + CRC32 crc = new CRC32(); + for (int i = 0; i < num; i++) { + String ename = "entry-" + i + "-name-" + r.nextLong(); + ZipEntry ze = new ZipEntry(ename); + int method = r.nextBoolean() ? ZipEntry.STORED : ZipEntry.DEFLATED; + writeEntry(zos, crc, ze, ZipEntry.STORED, szMax); + } + // add some manifest entries + for (int i = 0; i < r.nextInt(20); i++) { + String meta = "META-INF/" + "entry-" + i + "-metainf-" + r.nextLong(); + ZipEntry ze = new ZipEntry(meta); + writeEntry(zos, crc, ze, ZipEntry.STORED, szMax); + } + } catch (Exception x) { + throw (RuntimeException)x; + } + try { + this.attrs = Files.readAttributes(Paths.get(name), BasicFileAttributes.class); + this.lastModified = new File(name).lastModified(); + } catch (Exception x) { + throw (RuntimeException)x; + } + } + + private void writeEntry(ZipOutputStream zos, CRC32 crc, + ZipEntry ze, int method, int szMax) + throws IOException + { + ze.setMethod(method); + byte[] data = new byte[r.nextInt(szMax + 1)]; + r.nextBytes(data); + if (method == ZipEntry.STORED) { // must set size/csize/crc + ze.setSize(data.length); + ze.setCompressedSize(data.length); + crc.reset(); + crc.update(data); + ze.setCrc(crc.getValue()); + } + ze.setTime(System.currentTimeMillis()); + ze.setComment(ze.getName()); + zos.putNextEntry(ze); + zos.write(data); + zos.closeEntry(); + entries.put(ze, data); + } + } + + //--------------------- Infrastructure --------------------------- + static volatile int passed = 0, failed = 0; + static void pass() {passed++;} + static void pass(String msg) {System.out.println(msg); passed++;} + static void fail() {failed++; Thread.dumpStack();} + static void fail(String msg) {System.out.println(msg); fail();} + static void unexpected(Throwable t) {failed++; t.printStackTrace();} + static void unexpected(Throwable t, String msg) { + System.out.println(msg); failed++; t.printStackTrace();} + static boolean check(boolean cond) {if (cond) pass(); else fail(); return cond;} + + public static void main(String[] args) throws Throwable { + try {realMain(args);} catch (Throwable t) {unexpected(t);} + System.out.println("\nPassed = " + passed + " failed = " + failed); + if (failed > 0) throw new AssertionError("Some tests failed");} +} From b0e22f96036337799a90954d9c5eb98d5ffed0e6 Mon Sep 17 00:00:00 2001 From: Xueming Shen Date: Tue, 8 Dec 2015 16:43:58 -0800 Subject: [PATCH 25/62] 8144958: changes by JDK-8142508 seems to have broken jtreg Reviewed-by: darcy --- jdk/make/mapfiles/libzip/mapfile-vers | 22 +- jdk/make/mapfiles/libzip/reorder-sparc | 16 + jdk/make/mapfiles/libzip/reorder-sparcv9 | 15 + jdk/make/mapfiles/libzip/reorder-x86 | 18 + .../share/classes/java/util/jar/JarFile.java | 5 +- .../share/classes/java/util/zip/ZipCoder.java | 21 +- .../share/classes/java/util/zip/ZipFile.java | 847 ++++-------------- .../share/classes/java/util/zip/ZipUtils.java | 79 +- .../internal/misc/JavaUtilZipFileAccess.java | 1 - .../java.base/share/classes/sun/misc/VM.java | 3 + .../java.base/share/native/libzip/ZipFile.c | 406 +++++++++ jdk/test/java/util/zip/ZipFile/ReadZip.java | 3 +- .../java/util/zip/ZipFile/TestZipFile.java | 361 -------- 13 files changed, 677 insertions(+), 1120 deletions(-) create mode 100644 jdk/src/java.base/share/native/libzip/ZipFile.c delete mode 100644 jdk/test/java/util/zip/ZipFile/TestZipFile.java diff --git a/jdk/make/mapfiles/libzip/mapfile-vers b/jdk/make/mapfiles/libzip/mapfile-vers index c1ab48c10cf..ceace23f26d 100644 --- a/jdk/make/mapfiles/libzip/mapfile-vers +++ b/jdk/make/mapfiles/libzip/mapfile-vers @@ -1,5 +1,5 @@ # -# Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 1997, 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 @@ -27,6 +27,7 @@ SUNWprivate_1.1 { global: + Java_java_util_jar_JarFile_getMetaInfEntryNames; Java_java_util_zip_Adler32_update; Java_java_util_zip_Adler32_updateBytes; Java_java_util_zip_Adler32_updateByteBuffer; @@ -47,6 +48,25 @@ SUNWprivate_1.1 { Java_java_util_zip_Inflater_initIDs; Java_java_util_zip_Inflater_reset; Java_java_util_zip_Inflater_setDictionary; + Java_java_util_zip_ZipFile_close; + Java_java_util_zip_ZipFile_getCommentBytes; + Java_java_util_zip_ZipFile_freeEntry; + Java_java_util_zip_ZipFile_getEntry; + Java_java_util_zip_ZipFile_getEntryBytes; + Java_java_util_zip_ZipFile_getEntryCrc; + Java_java_util_zip_ZipFile_getEntryCSize; + Java_java_util_zip_ZipFile_getEntryFlag; + Java_java_util_zip_ZipFile_getEntryMethod; + Java_java_util_zip_ZipFile_getEntrySize; + Java_java_util_zip_ZipFile_getEntryTime; + Java_java_util_zip_ZipFile_getNextEntry; + Java_java_util_zip_ZipFile_getZipMessage; + Java_java_util_zip_ZipFile_getTotal; + Java_java_util_zip_ZipFile_initIDs; + Java_java_util_zip_ZipFile_open; + Java_java_util_zip_ZipFile_read; + Java_java_util_zip_ZipFile_startsWithLOC; + ZIP_Close; ZIP_CRC32; ZIP_FindEntry; diff --git a/jdk/make/mapfiles/libzip/reorder-sparc b/jdk/make/mapfiles/libzip/reorder-sparc index 63b2ad6fc28..154e7998a53 100644 --- a/jdk/make/mapfiles/libzip/reorder-sparc +++ b/jdk/make/mapfiles/libzip/reorder-sparc @@ -16,14 +16,30 @@ text: .text%ZIP_InflateFully; text: .text%ZIP_Lock; text: .text%ZIP_Unlock; text: .text%ZIP_FreeEntry; +text: .text%Java_java_util_zip_ZipFile_initIDs; +text: .text%Java_java_util_zip_ZipFile_open; +text: .text%Java_java_util_zip_ZipFile_getTotal; +text: .text%Java_java_util_zip_ZipFile_startsWithLOC; +text: .text%Java_java_util_zip_ZipFile_getEntry; +text: .text%Java_java_util_zip_ZipFile_freeEntry; +text: .text%Java_java_util_zip_ZipFile_getEntryTime; +text: .text%Java_java_util_zip_ZipFile_getEntryCrc; +text: .text%Java_java_util_zip_ZipFile_getEntryCSize; +text: .text%Java_java_util_zip_ZipFile_getEntrySize; +text: .text%Java_java_util_zip_ZipFile_getEntryFlag; +text: .text%Java_java_util_zip_ZipFile_getEntryMethod; +text: .text%Java_java_util_zip_ZipFile_getEntryBytes; text: .text%Java_java_util_zip_Inflater_initIDs; text: .text%Java_java_util_zip_Inflater_init; text: .text%inflateInit2_; text: .text%zcalloc; text: .text%Java_java_util_zip_Inflater_inflateBytes; +text: .text%Java_java_util_zip_ZipFile_read; text: .text%ZIP_Read; text: .text%zcfree; +text: .text%Java_java_util_jar_JarFile_getMetaInfEntryNames; text: .text%Java_java_util_zip_Inflater_reset; text: .text%Java_java_util_zip_Inflater_end; text: .text%inflateEnd; +text: .text%Java_java_util_zip_ZipFile_close; text: .text%ZIP_Close; diff --git a/jdk/make/mapfiles/libzip/reorder-sparcv9 b/jdk/make/mapfiles/libzip/reorder-sparcv9 index caca118de98..89690b8dabb 100644 --- a/jdk/make/mapfiles/libzip/reorder-sparcv9 +++ b/jdk/make/mapfiles/libzip/reorder-sparcv9 @@ -15,6 +15,19 @@ text: .text%ZIP_InflateFully; text: .text%ZIP_Lock; text: .text%ZIP_Unlock; text: .text%ZIP_FreeEntry; +text: .text%Java_java_util_zip_ZipFile_initIDs; +text: .text%Java_java_util_zip_ZipFile_open; +text: .text%Java_java_util_zip_ZipFile_getTotal; +text: .text%Java_java_util_zip_ZipFile_startsWithLOC; +text: .text%Java_java_util_zip_ZipFile_getEntry; +text: .text%Java_java_util_zip_ZipFile_freeEntry; +text: .text%Java_java_util_zip_ZipFile_getEntryTime; +text: .text%Java_java_util_zip_ZipFile_getEntryCrc; +text: .text%Java_java_util_zip_ZipFile_getEntryCSize; +text: .text%Java_java_util_zip_ZipFile_getEntrySize; +text: .text%Java_java_util_zip_ZipFile_getEntryFlag; +text: .text%Java_java_util_zip_ZipFile_getEntryMethod; +text: .text%Java_java_util_zip_ZipFile_getEntryBytes; text: .text%Java_java_util_zip_Inflater_initIDs; text: .text%Java_java_util_zip_Inflater_init; text: .text%inflateInit2_; @@ -22,6 +35,7 @@ text: .text%zcalloc; text: .text%inflateReset; text: .text%Java_java_util_zip_Inflater_inflateBytes; text: .text%inflate; +text: .text%Java_java_util_zip_ZipFile_read; text: .text%ZIP_Read; text: .text%zcfree; text: .text%Java_java_util_jar_JarFile_getMetaInfEntryNames; @@ -29,5 +43,6 @@ text: .text%ZIP_ReadEntry; text: .text%InflateFully; text: .text%inflateEnd; text: .text%Java_java_util_zip_Inflater_reset; +text: .text%Java_java_util_zip_ZipFile_close; text: .text%ZIP_Close; text: .text%Java_java_util_zip_Inflater_end; diff --git a/jdk/make/mapfiles/libzip/reorder-x86 b/jdk/make/mapfiles/libzip/reorder-x86 index dfd57c7752e..746948315eb 100644 --- a/jdk/make/mapfiles/libzip/reorder-x86 +++ b/jdk/make/mapfiles/libzip/reorder-x86 @@ -16,16 +16,34 @@ text: .text%ZIP_InflateFully; text: .text%ZIP_Lock; text: .text%ZIP_Unlock; text: .text%ZIP_FreeEntry; +text: .text%Java_java_util_zip_ZipFile_initIDs; +text: .text%Java_java_util_zip_ZipFile_open; +text: .text%Java_java_util_zip_ZipFile_getTotal; +text: .text%Java_java_util_zip_ZipFile_startsWithLOC; +text: .text%Java_java_util_zip_ZipFile_getEntry; +text: .text%Java_java_util_zip_ZipFile_freeEntry; +text: .text%Java_java_util_zip_ZipFile_getEntryTime; +text: .text%Java_java_util_zip_ZipFile_getEntryCrc; +text: .text%Java_java_util_zip_ZipFile_getEntryCSize; +text: .text%Java_java_util_zip_ZipFile_getEntrySize; +text: .text%Java_java_util_zip_ZipFile_getEntryFlag; +text: .text%Java_java_util_zip_ZipFile_getEntryMethod; +text: .text%Java_java_util_zip_ZipFile_getEntryBytes; +text: .text%Java_java_util_zip_Inflater_initIDs; +text: .text%Java_java_util_zip_Inflater_init; text: .text%inflateInit2_; text: .text%zcalloc; text: .text%inflateReset; text: .text%Java_java_util_zip_Inflater_inflateBytes; text: .text%inflate; +text: .text%Java_java_util_zip_ZipFile_read; text: .text%ZIP_Read; text: .text%zcfree; +text: .text%Java_java_util_jar_JarFile_getMetaInfEntryNames; text: .text%ZIP_ReadEntry; text: .text%InflateFully; text: .text%inflateEnd; text: .text%Java_java_util_zip_Inflater_reset; +text: .text%Java_java_util_zip_ZipFile_close; text: .text%ZIP_Close; text: .text%Java_java_util_zip_Inflater_end; diff --git a/jdk/src/java.base/share/classes/java/util/jar/JarFile.java b/jdk/src/java.base/share/classes/java/util/jar/JarFile.java index 62734ceefbd..6d16a517ac9 100644 --- a/jdk/src/java.base/share/classes/java/util/jar/JarFile.java +++ b/jdk/src/java.base/share/classes/java/util/jar/JarFile.java @@ -203,10 +203,7 @@ private Manifest getManifestFromReference() throws IOException { return man; } - private String[] getMetaInfEntryNames() { - return jdk.internal.misc.SharedSecrets.getJavaUtilZipFileAccess() - .getMetaInfEntryNames((ZipFile)this); - } + private native String[] getMetaInfEntryNames(); /** * Returns the {@code JarEntry} for the given entry name or diff --git a/jdk/src/java.base/share/classes/java/util/zip/ZipCoder.java b/jdk/src/java.base/share/classes/java/util/zip/ZipCoder.java index 243d6e8c065..b920b820e03 100644 --- a/jdk/src/java.base/share/classes/java/util/zip/ZipCoder.java +++ b/jdk/src/java.base/share/classes/java/util/zip/ZipCoder.java @@ -43,7 +43,7 @@ final class ZipCoder { - String toString(byte[] ba, int off, int length) { + String toString(byte[] ba, int length) { CharsetDecoder cd = decoder().reset(); int len = (int)(length * cd.maxCharsPerByte()); char[] ca = new char[len]; @@ -53,12 +53,12 @@ String toString(byte[] ba, int off, int length) { // CodingErrorAction.REPLACE mode. ZipCoder uses // REPORT mode. if (isUTF8 && cd instanceof ArrayDecoder) { - int clen = ((ArrayDecoder)cd).decode(ba, off, length, ca); + int clen = ((ArrayDecoder)cd).decode(ba, 0, length, ca); if (clen == -1) // malformed throw new IllegalArgumentException("MALFORMED"); return new String(ca, 0, clen); } - ByteBuffer bb = ByteBuffer.wrap(ba, off, length); + ByteBuffer bb = ByteBuffer.wrap(ba, 0, length); CharBuffer cb = CharBuffer.wrap(ca); CoderResult cr = cd.decode(bb, cb, true); if (!cr.isUnderflow()) @@ -69,12 +69,8 @@ String toString(byte[] ba, int off, int length) { return new String(ca, 0, cb.position()); } - String toString(byte[] ba, int length) { - return toString(ba, 0, length); - } - String toString(byte[] ba) { - return toString(ba, 0, ba.length); + return toString(ba, ba.length); } byte[] getBytes(String s) { @@ -115,16 +111,13 @@ byte[] getBytesUTF8(String s) { return utf8.getBytes(s); } - String toStringUTF8(byte[] ba, int len) { - return toStringUTF8(ba, 0, len); - } - String toStringUTF8(byte[] ba, int off, int len) { + String toStringUTF8(byte[] ba, int len) { if (isUTF8) - return toString(ba, off, len); + return toString(ba, len); if (utf8 == null) utf8 = new ZipCoder(StandardCharsets.UTF_8); - return utf8.toString(ba, off, len); + return utf8.toString(ba, len); } boolean isUTF8() { diff --git a/jdk/src/java.base/share/classes/java/util/zip/ZipFile.java b/jdk/src/java.base/share/classes/java/util/zip/ZipFile.java index 561023c30e7..4e3a6d20417 100644 --- a/jdk/src/java.base/share/classes/java/util/zip/ZipFile.java +++ b/jdk/src/java.base/share/classes/java/util/zip/ZipFile.java @@ -30,22 +30,14 @@ import java.io.IOException; import java.io.EOFException; import java.io.File; -import java.io.RandomAccessFile; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; -import java.nio.file.attribute.BasicFileAttributes; -import java.nio.file.Path; -import java.nio.file.Files; - import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Arrays; import java.util.Deque; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.Map; -import java.util.Objects; import java.util.NoSuchElementException; import java.util.Spliterator; import java.util.Spliterators; @@ -55,9 +47,7 @@ import jdk.internal.misc.JavaUtilZipFileAccess; import jdk.internal.misc.SharedSecrets; -import static java.util.zip.ZipConstants.*; import static java.util.zip.ZipConstants64.*; -import static java.util.zip.ZipUtils.*; /** * This class is used to read entries from a zip file. @@ -70,11 +60,11 @@ */ public class ZipFile implements ZipConstants, Closeable { - + private long jzfile; // address of jzfile data private final String name; // zip file name + private final int total; // total number of entries + private final boolean locsig; // if zip file starts with LOCSIG (usually true) private volatile boolean closeRequested = false; - private Source zsrc; - private ZipCoder zc; private static final int STORED = ZipEntry.STORED; private static final int DEFLATED = ZipEntry.DEFLATED; @@ -93,6 +83,23 @@ class ZipFile implements ZipConstants, Closeable { */ public static final int OPEN_DELETE = 0x4; + static { + /* Zip library is loaded from System.initializeSystemClass */ + initIDs(); + } + + private static native void initIDs(); + + private static final boolean usemmap; + + static { + // A system prpperty to disable mmap use to avoid vm crash when + // in-use zip file is accidently overwritten by others. + String prop = sun.misc.VM.getSavedProperty("sun.zip.disableMemoryMapping"); + usemmap = (prop == null || + !(prop.length() == 0 || prop.equalsIgnoreCase("true"))); + } + /** * Opens a zip file for reading. * @@ -158,6 +165,8 @@ public ZipFile(File file) throws ZipException, IOException { this(file, OPEN_READ); } + private ZipCoder zc; + /** * Opens a new {@code ZipFile} to read from the specified * {@code File} object in the specified mode. The mode argument @@ -205,13 +214,16 @@ public ZipFile(File file, int mode, Charset charset) throws IOException sm.checkDelete(name); } } - Objects.requireNonNull(charset, "charset"); + if (charset == null) + throw new NullPointerException("charset is null"); this.zc = ZipCoder.get(charset); - this.name = name; long t0 = System.nanoTime(); - this.zsrc = Source.get(file, (mode & OPEN_DELETE) != 0); + jzfile = open(name, mode, file.lastModified(), usemmap); sun.misc.PerfCounter.getZipFileOpenTime().addElapsedTimeFrom(t0); sun.misc.PerfCounter.getZipFileCount().increment(); + this.name = name; + this.total = getTotal(jzfile); + this.locsig = startsWithLOC(jzfile); } /** @@ -245,7 +257,6 @@ public ZipFile(String name, Charset charset) throws IOException /** * Opens a ZIP file for reading given the specified File object. - * * @param file the ZIP file to be opened for reading * @param charset * The {@linkplain java.nio.charset.Charset charset} to be @@ -276,10 +287,10 @@ public ZipFile(File file, Charset charset) throws IOException public String getComment() { synchronized (this) { ensureOpen(); - if (zsrc.comment == null) { + byte[] bcomm = getCommentBytes(jzfile); + if (bcomm == null) return null; - } - return zc.toString(zsrc.comment); + return zc.toString(bcomm, bcomm.length); } } @@ -292,27 +303,38 @@ public String getComment() { * @throws IllegalStateException if the zip file has been closed */ public ZipEntry getEntry(String name) { - Objects.requireNonNull(name, "name"); + if (name == null) { + throw new NullPointerException("name"); + } + long jzentry = 0; synchronized (this) { ensureOpen(); - int pos = zsrc.getEntryPos(zc.getBytes(name), true); - if (pos != -1) { - return getZipEntry(name, pos); + jzentry = getEntry(jzfile, zc.getBytes(name), true); + if (jzentry != 0) { + ZipEntry ze = getZipEntry(name, jzentry); + freeEntry(jzfile, jzentry); + return ze; } } return null; } - // The outstanding inputstreams that need to be closed, + private static native long getEntry(long jzfile, byte[] name, + boolean addSlash); + + // freeEntry releases the C jzentry struct. + private static native void freeEntry(long jzfile, long jzentry); + + // the outstanding inputstreams that need to be closed, // mapped to the inflater objects they use. private final Map streams = new WeakHashMap<>(); /** * Returns an input stream for reading the contents of the specified * zip file entry. - *

- * Closing this ZIP file will, in turn, close all input streams that - * have been returned by invocations of this method. + * + *

Closing this ZIP file will, in turn, close all input + * streams that have been returned by invocations of this method. * * @param entry the zip file entry * @return the input stream for reading the contents of the specified @@ -322,38 +344,37 @@ public ZipEntry getEntry(String name) { * @throws IllegalStateException if the zip file has been closed */ public InputStream getInputStream(ZipEntry entry) throws IOException { - Objects.requireNonNull(entry, "entry"); - int pos = -1; + if (entry == null) { + throw new NullPointerException("entry"); + } + long jzentry = 0; ZipFileInputStream in = null; synchronized (this) { ensureOpen(); if (!zc.isUTF8() && (entry.flag & EFS) != 0) { - pos = zsrc.getEntryPos(zc.getBytesUTF8(entry.name), false); + jzentry = getEntry(jzfile, zc.getBytesUTF8(entry.name), false); } else { - pos = zsrc.getEntryPos(zc.getBytes(entry.name), false); + jzentry = getEntry(jzfile, zc.getBytes(entry.name), false); } - if (pos == -1) { + if (jzentry == 0) { return null; } - in = new ZipFileInputStream(zsrc.cen, pos); - switch (CENHOW(zsrc.cen, pos)) { + in = new ZipFileInputStream(jzentry); + + switch (getEntryMethod(jzentry)) { case STORED: synchronized (streams) { streams.put(in, null); } return in; case DEFLATED: - // Inflater likes a bit of slack // MORE: Compute good size for inflater stream: - long size = CENLEN(zsrc.cen, pos) + 2; - if (size > 65536) { - size = 8192; - } - if (size <= 0) { - size = 4096; - } + long size = getEntrySize(jzentry) + 2; // Inflater likes a bit of slack + if (size > 65536) size = 8192; + if (size <= 0) size = 4096; Inflater inf = getInflater(); - InputStream is = new ZipFileInflaterInputStream(in, inf, (int)size); + InputStream is = + new ZipFileInflaterInputStream(in, inf, (int)size); synchronized (streams) { streams.put(is, inf); } @@ -426,8 +447,8 @@ protected void finalize() throws Throwable { private Inflater getInflater() { Inflater inf; synchronized (inflaterCache) { - while ((inf = inflaterCache.poll()) != null) { - if (!inf.ended()) { + while (null != (inf = inflaterCache.poll())) { + if (false == inf.ended()) { return inf; } } @@ -439,7 +460,7 @@ private Inflater getInflater() { * Releases the specified inflater to the list of available inflaters. */ private void releaseInflater(Inflater inf) { - if (!inf.ended()) { + if (false == inf.ended()) { inf.reset(); synchronized (inflaterCache) { inflaterCache.add(inf); @@ -448,7 +469,7 @@ private void releaseInflater(Inflater inf) { } // List of available Inflater objects for decompression - private final Deque inflaterCache = new ArrayDeque<>(); + private Deque inflaterCache = new ArrayDeque<>(); /** * Returns the path name of the ZIP file. @@ -472,7 +493,7 @@ public boolean hasMoreElements() { public boolean hasNext() { synchronized (ZipFile.this) { ensureOpen(); - return i < zsrc.total; + return i < total; } } @@ -483,11 +504,28 @@ public ZipEntry nextElement() { public ZipEntry next() { synchronized (ZipFile.this) { ensureOpen(); - if (i >= zsrc.total) { + if (i >= total) { throw new NoSuchElementException(); } - // each "entry" has 3 ints in table entries - return getZipEntry(null, zsrc.getEntryPos(i++ * 3)); + long jzentry = getNextEntry(jzfile, i++); + if (jzentry == 0) { + String message; + if (closeRequested) { + message = "ZipFile concurrently closed"; + } else { + message = getZipMessage(ZipFile.this.jzfile); + } + throw new ZipError("jzentry == 0" + + ",\n jzfile = " + ZipFile.this.jzfile + + ",\n total = " + ZipFile.this.total + + ",\n name = " + ZipFile.this.name + + ",\n i = " + i + + ",\n message = " + message + ); + } + ZipEntry ze = getZipEntry(null, jzentry); + freeEntry(jzfile, jzentry); + return ze; } } @@ -521,53 +559,48 @@ public Stream stream() { Spliterator.IMMUTABLE | Spliterator.NONNULL), false); } - /* Checks ensureOpen() before invoke this method */ - private ZipEntry getZipEntry(String name, int pos) { - byte[] cen = zsrc.cen; + private ZipEntry getZipEntry(String name, long jzentry) { ZipEntry e = new ZipEntry(); - int nlen = CENNAM(cen, pos); - int elen = CENEXT(cen, pos); - int clen = CENCOM(cen, pos); - e.flag = CENFLG(cen, pos); // get the flag first + e.flag = getEntryFlag(jzentry); // get the flag first if (name != null) { e.name = name; } else { + byte[] bname = getEntryBytes(jzentry, JZENTRY_NAME); if (!zc.isUTF8() && (e.flag & EFS) != 0) { - e.name = zc.toStringUTF8(cen, pos + CENHDR, nlen); + e.name = zc.toStringUTF8(bname, bname.length); } else { - e.name = zc.toString(cen, pos + CENHDR, nlen); - } - } - e.xdostime = CENTIM(cen, pos); - e.crc = CENCRC(cen, pos); - e.size = CENLEN(cen, pos); - e.csize = CENSIZ(cen, pos); - e.method = CENHOW(cen, pos); - if (elen != 0) { - e.setExtra0(Arrays.copyOfRange(cen, pos + CENHDR + nlen, - pos + CENHDR + nlen + elen), true); - } - if (clen != 0) { + e.name = zc.toString(bname, bname.length); + } + } + e.xdostime = getEntryTime(jzentry); + e.crc = getEntryCrc(jzentry); + e.size = getEntrySize(jzentry); + e.csize = getEntryCSize(jzentry); + e.method = getEntryMethod(jzentry); + e.setExtra0(getEntryBytes(jzentry, JZENTRY_EXTRA), false); + byte[] bcomm = getEntryBytes(jzentry, JZENTRY_COMMENT); + if (bcomm == null) { + e.comment = null; + } else { if (!zc.isUTF8() && (e.flag & EFS) != 0) { - e.comment = zc.toStringUTF8(cen, pos + CENHDR + nlen + elen, clen); + e.comment = zc.toStringUTF8(bcomm, bcomm.length); } else { - e.comment = zc.toString(cen, pos + CENHDR + nlen + elen, clen); + e.comment = zc.toString(bcomm, bcomm.length); } } return e; } + private static native long getNextEntry(long jzfile, int i); + /** * Returns the number of entries in the ZIP file. - * * @return the number of entries in the ZIP file * @throws IllegalStateException if the zip file has been closed */ public int size() { - synchronized (this) { - ensureOpen(); - return zsrc.total; - } + ensureOpen(); + return total; } /** @@ -579,15 +612,14 @@ public int size() { * @throws IOException if an I/O error has occurred */ public void close() throws IOException { - if (closeRequested) { + if (closeRequested) return; - } closeRequested = true; synchronized (this) { // Close streams, release their inflaters synchronized (streams) { - if (!streams.isEmpty()) { + if (false == streams.isEmpty()) { Map copy = new HashMap<>(streams); streams.clear(); for (Map.Entry e : copy.entrySet()) { @@ -599,17 +631,21 @@ public void close() throws IOException { } } } + // Release cached inflaters + Inflater inf; synchronized (inflaterCache) { - Inflater inf; - while ((inf = inflaterCache.poll()) != null) { + while (null != (inf = inflaterCache.poll())) { inf.end(); } } - // Release zip src - if (zsrc != null) { - Source.close(zsrc); - zsrc = null; + + if (jzfile != 0) { + // Close the zip file + long zf = this.jzfile; + jzfile = 0; + + close(zf); } } } @@ -632,11 +668,14 @@ protected void finalize() throws IOException { close(); } + private static native void close(long jzfile); + private void ensureOpen() { if (closeRequested) { throw new IllegalStateException("zip file closed"); } - if (zsrc == null) { + + if (jzfile == 0) { throw new IllegalStateException("The object is not initialized."); } } @@ -652,86 +691,23 @@ private void ensureOpenOrZipException() throws IOException { * (possibly compressed) zip file entry. */ private class ZipFileInputStream extends InputStream { - private volatile boolean closeRequested = false; + private volatile boolean zfisCloseRequested = false; + protected long jzentry; // address of jzentry data private long pos; // current position within entry data protected long rem; // number of remaining bytes within entry protected long size; // uncompressed size of this entry - ZipFileInputStream(byte[] cen, int cenpos) throws IOException { - rem = CENSIZ(cen, cenpos); - size = CENLEN(cen, cenpos); - pos = CENOFF(cen, cenpos); - // zip64 - if (rem == ZIP64_MAGICVAL || size == ZIP64_MAGICVAL || - pos == ZIP64_MAGICVAL) { - checkZIP64(cen, cenpos); - } - // negative for lazy initialization, see getDataOffset(); - pos = - (pos + ZipFile.this.zsrc.locpos); - } - - private void checkZIP64(byte[] cen, int cenpos) throws IOException { - int off = cenpos + CENHDR + CENNAM(cen, cenpos); - int end = off + CENEXT(cen, cenpos); - while (off + 4 < end) { - int tag = get16(cen, off); - int sz = get16(cen, off + 2); - off += 4; - if (off + sz > end) // invalid data - break; - if (tag == EXTID_ZIP64) { - if (size == ZIP64_MAGICVAL) { - if (sz < 8 || (off + 8) > end) - break; - size = get64(cen, off); - sz -= 8; - off += 8; - } - if (rem == ZIP64_MAGICVAL) { - if (sz < 8 || (off + 8) > end) - break; - rem = get64(cen, off); - sz -= 8; - off += 8; - } - if (pos == ZIP64_MAGICVAL) { - if (sz < 8 || (off + 8) > end) - break; - pos = get64(cen, off); - sz -= 8; - off += 8; - } - break; - } - off += sz; - } - } - - /* The Zip file spec explicitly allows the LOC extra data size to - * be different from the CEN extra data size. Since we cannot trust - * the CEN extra data size, we need to read the LOC to determine - * the entry data offset. - */ - private long initDataOffset() throws IOException { - if (pos <= 0) { - byte[] loc = new byte[LOCHDR]; - pos = -pos; - int len = ZipFile.this.zsrc.readFullyAt(loc, 0, loc.length, pos); - if (len != LOCHDR) { - throw new ZipException("ZipFile error reading zip file"); - } - if (LOCSIG(loc) != LOCSIG) { - throw new ZipException("ZipFile invalid LOC header (bad signature)"); - } - pos += LOCHDR + LOCNAM(loc) + LOCEXT(loc); - } - return pos; + ZipFileInputStream(long jzentry) { + pos = 0; + rem = getEntryCSize(jzentry); + size = getEntrySize(jzentry); + this.jzentry = jzentry; } public int read(byte b[], int off, int len) throws IOException { synchronized (ZipFile.this) { - ensureOpenOrZipException(); - initDataOffset(); + long rem = this.rem; + long pos = this.pos; if (rem == 0) { return -1; } @@ -741,10 +717,14 @@ public int read(byte b[], int off, int len) throws IOException { if (len > rem) { len = (int) rem; } - len = ZipFile.this.zsrc.readAt(b, off, len, pos); + + // Check if ZipFile open + ensureOpenOrZipException(); + len = ZipFile.read(ZipFile.this.jzfile, jzentry, pos, b, + off, len); if (len > 0) { - pos += len; - rem -= len; + this.pos = (pos + len); + this.rem = (rem - len); } } if (rem == 0) { @@ -762,16 +742,11 @@ public int read() throws IOException { } } - public long skip(long n) throws IOException { - synchronized (ZipFile.this) { - ensureOpenOrZipException(); - initDataOffset(); - if (n > rem) { - n = rem; - } - pos += n; - rem -= n; - } + public long skip(long n) { + if (n > rem) + n = rem; + pos += n; + rem -= n; if (rem == 0) { close(); } @@ -787,11 +762,17 @@ public long size() { } public void close() { - if (closeRequested) { + if (zfisCloseRequested) return; - } - closeRequested = true; + zfisCloseRequested = true; + rem = 0; + synchronized (ZipFile.this) { + if (jzentry != 0 && ZipFile.this.jzfile != 0) { + freeEntry(ZipFile.this.jzfile, jzentry); + jzentry = 0; + } + } synchronized (streams) { streams.remove(this); } @@ -806,492 +787,40 @@ protected void finalize() { SharedSecrets.setJavaUtilZipFileAccess( new JavaUtilZipFileAccess() { public boolean startsWithLocHeader(ZipFile zip) { - return zip.zsrc.locsig; - } - public String[] getMetaInfEntryNames(ZipFile zip) { - return zip.getMetaInfEntryNames(); + return zip.startsWithLocHeader(); } - } + } ); } - /* - * Returns an array of strings representing the names of all entries - * that begin with "META-INF/" (case ignored). This method is used - * in JarFile, via SharedSecrets, as an optimization when looking up - * manifest and signature file entries. Returns null if no entries - * were found. + /** + * Returns {@code true} if, and only if, the zip file begins with {@code + * LOCSIG}. */ - private String[] getMetaInfEntryNames() { - synchronized (this) { - ensureOpen(); - if (zsrc.metanames.size() == 0) { - return null; - } - String[] names = new String[zsrc.metanames.size()]; - byte[] cen = zsrc.cen; - for (int i = 0; i < names.length; i++) { - int pos = zsrc.metanames.get(i); - names[i] = zc.toStringUTF8(cen, pos + CENHDR, CENNAM(cen, pos)); - } - return names; - } + private boolean startsWithLocHeader() { + return locsig; } - private static class Source { - private final Key key; // the key in files - private int refs = 1; - - private RandomAccessFile zfile; // zfile of the underlying zip file - private byte[] cen; // CEN & ENDHDR - private long locpos; // position of first LOC header (usually 0) - private byte[] comment; // zip file comment - // list of meta entries in META-INF dir - private ArrayList metanames = new ArrayList<>(); - private final boolean locsig; // true, if zip file starts with LOCSIG (usually true) - - // A Hashmap for all entries. - // - // A cen entry of Zip/JAR file. As we have one for every entry in every active Zip/JAR, - // We might have a lot of these in a typical system. In order to save space we don't - // keep the name in memory, but merely remember a 32 bit {@code hash} value of the - // entry name and its offset {@code pos} in the central directory hdeader. - // - // private static class Entry { - // int hash; // 32 bit hashcode on name - // int next; // hash chain: index into entries - // int pos; // Offset of central directory file header - // } - // private Entry[] entries; // array of hashed cen entry - // - // To reduce the total size of entries further, we use a int[] here to store 3 "int" - // {@code hash}, {@code next and {@code "pos for each entry. The entry can then be - // referred by their index of their positions in the {@code entries}. - // - private int[] entries; // array of hashed cen entry - private int addEntry(int index, int hash, int next, int pos) { - entries[index++] = hash; - entries[index++] = next; - entries[index++] = pos; - return index; - } - private int getEntryHash(int index) { return entries[index]; } - private int getEntryNext(int index) { return entries[index + 1]; } - private int getEntryPos(int index) { return entries[index + 2]; } - private static final int ZIP_ENDCHAIN = -1; - private int total; // total number of entries - private int[] table; // Hash chain heads: indexes into entries - private int tablelen; // number of hash heads - - private static class Key { - BasicFileAttributes attrs; - File file; - - public Key(File file, BasicFileAttributes attrs) { - this.attrs = attrs; - this.file = file; - } - - public int hashCode() { - long t = attrs.lastModifiedTime().toMillis(); - return ((int)(t ^ (t >>> 32))) + file.hashCode(); - } - - public boolean equals(Object obj) { - if (obj instanceof Key) { - Key key = (Key)obj; - if (!attrs.lastModifiedTime().equals(key.attrs.lastModifiedTime())) { - return false; - } - Object fk = attrs.fileKey(); - if (fk != null) { - return fk.equals(key.attrs.fileKey()); - } else { - return file.equals(key.file); - } - } - return false; - } - } - private static final HashMap files = new HashMap<>(); - - public static Source get(File file, boolean toDelete) throws IOException { - Key key = new Key(file, - Files.readAttributes(file.toPath(), BasicFileAttributes.class)); - Source src = null; - synchronized (files) { - src = files.get(key); - if (src != null) { - src.refs++; - return src; - } - } - src = new Source(key, toDelete); - synchronized (files) { - if (files.containsKey(key)) { // someone else put in first - src.close(); // close the newly created one - src = files.get(key); - src.refs++; - return src; - } - files.put(key, src); - return src; - } - } - - private static void close(Source src) throws IOException { - synchronized (files) { - if (--src.refs == 0) { - files.remove(src.key); - src.close(); - } - } - } - - private Source(Key key, boolean toDelete) throws IOException { - this.key = key; - this.zfile = new RandomAccessFile(key.file, "r"); - if (toDelete) { - key.file.delete(); - } - initCEN(-1); - byte[] buf = new byte[4]; - readFullyAt(buf, 0, 4, 0); - this.locsig = (LOCSIG(buf) != LOCSIG); - } - - private void close() throws IOException { - zfile.close(); - zfile = null; - cen = null; - entries = null; - table = null; - metanames = null; - } - - private static final int BUF_SIZE = 8192; - private final int readFullyAt(byte[] buf, int off, int len, long pos) - throws IOException - { - synchronized(zfile) { - zfile.seek(pos); - int N = len; - while (N > 0) { - int n = Math.min(BUF_SIZE, N); - zfile.readFully(buf, off, n); - off += n; - N -= n; - } - return len; - } - } - - private final int readAt(byte[] buf, int off, int len, long pos) - throws IOException - { - synchronized(zfile) { - zfile.seek(pos); - return zfile.read(buf, off, len); - } - } - - private static final int hashN(byte[] a, int off, int len) { - int h = 1; - while (len-- > 0) { - h = 31 * h + a[off++]; - } - return h; - } - - private static final int hash_append(int hash, byte b) { - return hash * 31 + b; - } - - private static class End { - int centot; // 4 bytes - long cenlen; // 4 bytes - long cenoff; // 4 bytes - long endpos; // 4 bytes - } - - /* - * Searches for end of central directory (END) header. The contents of - * the END header will be read and placed in endbuf. Returns the file - * position of the END header, otherwise returns -1 if the END header - * was not found or an error occurred. - */ - private End findEND() throws IOException { - long ziplen = zfile.length(); - if (ziplen <= 0) - zerror("zip file is empty"); - End end = new End(); - byte[] buf = new byte[READBLOCKSZ]; - long minHDR = (ziplen - END_MAXLEN) > 0 ? ziplen - END_MAXLEN : 0; - long minPos = minHDR - (buf.length - ENDHDR); - for (long pos = ziplen - buf.length; pos >= minPos; pos -= (buf.length - ENDHDR)) { - int off = 0; - if (pos < 0) { - // Pretend there are some NUL bytes before start of file - off = (int)-pos; - Arrays.fill(buf, 0, off, (byte)0); - } - int len = buf.length - off; - if (readFullyAt(buf, off, len, pos + off) != len ) { - zerror("zip END header not found"); - } - // Now scan the block backwards for END header signature - for (int i = buf.length - ENDHDR; i >= 0; i--) { - if (buf[i+0] == (byte)'P' && - buf[i+1] == (byte)'K' && - buf[i+2] == (byte)'\005' && - buf[i+3] == (byte)'\006') { - // Found ENDSIG header - byte[] endbuf = Arrays.copyOfRange(buf, i, i + ENDHDR); - end.centot = ENDTOT(endbuf); - end.cenlen = ENDSIZ(endbuf); - end.cenoff = ENDOFF(endbuf); - end.endpos = pos + i; - int comlen = ENDCOM(endbuf); - if (end.endpos + ENDHDR + comlen != ziplen) { - // ENDSIG matched, however the size of file comment in it does - // not match the real size. One "common" cause for this problem - // is some "extra" bytes are padded at the end of the zipfile. - // Let's do some extra verification, we don't care about the - // performance in this situation. - byte[] sbuf = new byte[4]; - long cenpos = end.endpos - end.cenlen; - long locpos = cenpos - end.cenoff; - if (cenpos < 0 || - locpos < 0 || - readFullyAt(sbuf, 0, sbuf.length, cenpos) != 4 || - GETSIG(sbuf) != CENSIG || - readFullyAt(sbuf, 0, sbuf.length, locpos) != 4 || - GETSIG(sbuf) != LOCSIG) { - continue; - } - } - if (comlen > 0) { // this zip file has comlen - comment = new byte[comlen]; - if (readFullyAt(comment, 0, comlen, end.endpos + ENDHDR) != comlen) { - zerror("zip comment read failed"); - } - } - if (end.cenlen == ZIP64_MAGICVAL || - end.cenoff == ZIP64_MAGICVAL || - end.centot == ZIP64_MAGICCOUNT) - { - // need to find the zip64 end; - try { - byte[] loc64 = new byte[ZIP64_LOCHDR]; - if (readFullyAt(loc64, 0, loc64.length, end.endpos - ZIP64_LOCHDR) - != loc64.length || GETSIG(loc64) != ZIP64_LOCSIG) { - return end; - } - long end64pos = ZIP64_LOCOFF(loc64); - byte[] end64buf = new byte[ZIP64_ENDHDR]; - if (readFullyAt(end64buf, 0, end64buf.length, end64pos) - != end64buf.length || GETSIG(end64buf) != ZIP64_ENDSIG) { - return end; - } - // end64 found, re-calcualte everything. - end.cenlen = ZIP64_ENDSIZ(end64buf); - end.cenoff = ZIP64_ENDOFF(end64buf); - end.centot = (int)ZIP64_ENDTOT(end64buf); // assume total < 2g - end.endpos = end64pos; - } catch (IOException x) {} // no zip64 loc/end - } - return end; - } - } - } - zerror("zip END header not found"); - return null; //make compiler happy - } - - // Reads zip file central directory. - private void initCEN(int knownTotal) throws IOException { - if (knownTotal == -1) { - End end = findEND(); - if (end.endpos == 0) { - locpos = 0; - total = 0; - entries = new int[0]; - cen = null; - return; // only END header present - } - if (end.cenlen > end.endpos) - zerror("invalid END header (bad central directory size)"); - long cenpos = end.endpos - end.cenlen; // position of CEN table - // Get position of first local file (LOC) header, taking into - // account that there may be a stub prefixed to the zip file. - locpos = cenpos - end.cenoff; - if (locpos < 0) { - zerror("invalid END header (bad central directory offset)"); - } - // read in the CEN and END - cen = new byte[(int)(end.cenlen + ENDHDR)]; - if (readFullyAt(cen, 0, cen.length, cenpos) != end.cenlen + ENDHDR) { - zerror("read CEN tables failed"); - } - total = end.centot; - } else { - total = knownTotal; - } - // hash table for entries - entries = new int[total * 3]; - tablelen = ((total/2) | 1); // Odd -> fewer collisions - table = new int[tablelen]; - Arrays.fill(table, ZIP_ENDCHAIN); - int idx = 0; - int hash = 0; - int next = -1; - - // list for all meta entries - metanames = new ArrayList<>(); - - // Iterate through the entries in the central directory - int i = 0; - int hsh = 0; - int pos = 0; - int limit = cen.length - ENDHDR; - while (pos + CENHDR <= limit) { - if (i >= total) { - // This will only happen if the zip file has an incorrect - // ENDTOT field, which usually means it contains more than - // 65535 entries. - initCEN(countCENHeaders(cen, limit)); - return; - } - if (CENSIG(cen, pos) != CENSIG) - zerror("invalid CEN header (bad signature)"); - int method = CENHOW(cen, pos); - int nlen = CENNAM(cen, pos); - int elen = CENEXT(cen, pos); - int clen = CENCOM(cen, pos); - if ((CENFLG(cen, pos) & 1) != 0) - zerror("invalid CEN header (encrypted entry)"); - if (method != STORED && method != DEFLATED) - zerror("invalid CEN header (bad compression method: " + method + ")"); - if (pos + CENHDR + nlen > limit) - zerror("invalid CEN header (bad header size)"); - // Record the CEN offset and the name hash in our hash cell. - hash = hashN(cen, pos + CENHDR, nlen); - hsh = (hash & 0x7fffffff) % tablelen; - next = table[hsh]; - table[hsh] = idx; - idx = addEntry(idx, hash, next, pos); - // Adds name to metanames. - if (isMetaName(cen, pos + CENHDR, nlen)) { - metanames.add(pos); - } - // skip ext and comment - pos += (CENHDR + nlen + elen + clen); - i++; - } - total = i; - if (pos + ENDHDR != cen.length) { - zerror("invalid CEN header (bad header size)"); - } - } - - private static void zerror(String msg) throws ZipException { - throw new ZipException(msg); - } - - /* - * Returns the {@code pos} of the zip cen entry corresponding to the - * specified entry name, or -1 if not found. - */ - private int getEntryPos(byte[] name, boolean addSlash) { - if (total == 0) { - return -1; - } - int hsh = hashN(name, 0, name.length); - int idx = table[(hsh & 0x7fffffff) % tablelen]; - /* - * This while loop is an optimization where a double lookup - * for name and name+/ is being performed. The name char - * array has enough room at the end to try again with a - * slash appended if the first table lookup does not succeed. - */ - while(true) { - /* - * Search down the target hash chain for a entry whose - * 32 bit hash matches the hashed name. - */ - while (idx != ZIP_ENDCHAIN) { - if (getEntryHash(idx) == hsh) { - // The CEN name must match the specfied one - int pos = getEntryPos(idx); - if (name.length == CENNAM(cen, pos)) { - boolean matched = true; - int nameoff = pos + CENHDR; - for (int i = 0; i < name.length; i++) { - if (name[i] != cen[nameoff++]) { - matched = false; - break; - } - } - if (matched) { - return pos; - } - } - } - idx = getEntryNext(idx); - } - /* If not addSlash, or slash is already there, we are done */ - if (!addSlash || name[name.length - 1] == '/') { - return -1; - } - /* Add slash and try once more */ - name = Arrays.copyOf(name, name.length + 1); - name[name.length - 1] = '/'; - hsh = hash_append(hsh, (byte)'/'); - //idx = table[hsh % tablelen]; - idx = table[(hsh & 0x7fffffff) % tablelen]; - addSlash = false; - } - } - - private static byte[] metainf = new byte[] { - 'M', 'E', 'T', 'A', '-', 'I' , 'N', 'F', '/', - }; - - /* - * Returns true if the specified entry's name begins with the string - * "META-INF/" irrespective of case. - */ - private static boolean isMetaName(byte[] name, int off, int len) { - if (len < 9 || (name[off] != 'M' && name[off] != 'm')) { // sizeof("META-INF/") - 1 - return false; - } - off++; - for (int i = 1; i < metainf.length; i++) { - byte c = name[off++]; - // Avoid toupper; it's locale-dependent - if (c >= 'a' && c <= 'z') { - c += 'A' - 'a'; - } - if (metainf[i] != c) { - return false; - } - } - return true; - } - - /* - * Counts the number of CEN headers in a central directory extending - * from BEG to END. Might return a bogus answer if the zip file is - * corrupt, but will not crash. - */ - static int countCENHeaders(byte[] cen, int end) { - int count = 0; - int pos = 0; - while (pos + CENHDR <= end) { - count++; - pos += (CENHDR + CENNAM(cen, pos) + CENEXT(cen, pos) + CENCOM(cen, pos)); - } - return count; - } - } + private static native long open(String name, int mode, long lastModified, + boolean usemmap) throws IOException; + private static native int getTotal(long jzfile); + private static native boolean startsWithLOC(long jzfile); + private static native int read(long jzfile, long jzentry, + long pos, byte[] b, int off, int len); + + // access to the native zentry object + private static native long getEntryTime(long jzentry); + private static native long getEntryCrc(long jzentry); + private static native long getEntryCSize(long jzentry); + private static native long getEntrySize(long jzentry); + private static native int getEntryMethod(long jzentry); + private static native int getEntryFlag(long jzentry); + private static native byte[] getCommentBytes(long jzfile); + + private static final int JZENTRY_NAME = 0; + private static final int JZENTRY_EXTRA = 1; + private static final int JZENTRY_COMMENT = 2; + private static native byte[] getEntryBytes(long jzentry, int type); + + private static native String getZipMessage(long jzfile); } diff --git a/jdk/src/java.base/share/classes/java/util/zip/ZipUtils.java b/jdk/src/java.base/share/classes/java/util/zip/ZipUtils.java index a6632f0fa83..81882fdcec6 100644 --- a/jdk/src/java.base/share/classes/java/util/zip/ZipUtils.java +++ b/jdk/src/java.base/share/classes/java/util/zip/ZipUtils.java @@ -31,8 +31,6 @@ import java.time.ZoneId; import java.util.concurrent.TimeUnit; -import static java.util.zip.ZipConstants.ENDHDR; - class ZipUtils { // used to adjust values between Windows and java epoch @@ -135,7 +133,7 @@ public static long javaToExtendedDosTime(long time) { * The bytes are assumed to be in Intel (little-endian) byte order. */ public static final int get16(byte b[], int off) { - return (b[off] & 0xff) | ((b[off + 1] & 0xff) << 8); + return Byte.toUnsignedInt(b[off]) | (Byte.toUnsignedInt(b[off+1]) << 8); } /** @@ -162,79 +160,4 @@ public static final long get64(byte b[], int off) { public static final int get32S(byte b[], int off) { return (get16(b, off) | (get16(b, off+2) << 16)); } - - // fields access methods - static final int CH(byte[] b, int n) { - return b[n] & 0xff ; - } - - static final int SH(byte[] b, int n) { - return (b[n] & 0xff) | ((b[n + 1] & 0xff) << 8); - } - - static final long LG(byte[] b, int n) { - return ((SH(b, n)) | (SH(b, n + 2) << 16)) & 0xffffffffL; - } - - static final long LL(byte[] b, int n) { - return (LG(b, n)) | (LG(b, n + 4) << 32); - } - - static final long GETSIG(byte[] b) { - return LG(b, 0); - } - - // local file (LOC) header fields - static final long LOCSIG(byte[] b) { return LG(b, 0); } // signature - static final int LOCVER(byte[] b) { return SH(b, 4); } // version needed to extract - static final int LOCFLG(byte[] b) { return SH(b, 6); } // general purpose bit flags - static final int LOCHOW(byte[] b) { return SH(b, 8); } // compression method - static final long LOCTIM(byte[] b) { return LG(b, 10);} // modification time - static final long LOCCRC(byte[] b) { return LG(b, 14);} // crc of uncompressed data - static final long LOCSIZ(byte[] b) { return LG(b, 18);} // compressed data size - static final long LOCLEN(byte[] b) { return LG(b, 22);} // uncompressed data size - static final int LOCNAM(byte[] b) { return SH(b, 26);} // filename length - static final int LOCEXT(byte[] b) { return SH(b, 28);} // extra field length - - // extra local (EXT) header fields - static final long EXTCRC(byte[] b) { return LG(b, 4);} // crc of uncompressed data - static final long EXTSIZ(byte[] b) { return LG(b, 8);} // compressed size - static final long EXTLEN(byte[] b) { return LG(b, 12);} // uncompressed size - - // end of central directory header (END) fields - static final int ENDSUB(byte[] b) { return SH(b, 8); } // number of entries on this disk - static final int ENDTOT(byte[] b) { return SH(b, 10);} // total number of entries - static final long ENDSIZ(byte[] b) { return LG(b, 12);} // central directory size - static final long ENDOFF(byte[] b) { return LG(b, 16);} // central directory offset - static final int ENDCOM(byte[] b) { return SH(b, 20);} // size of zip file comment - static final int ENDCOM(byte[] b, int off) { return SH(b, off + 20);} - - // zip64 end of central directory recoder fields - static final long ZIP64_ENDTOD(byte[] b) { return LL(b, 24);} // total number of entries on disk - static final long ZIP64_ENDTOT(byte[] b) { return LL(b, 32);} // total number of entries - static final long ZIP64_ENDSIZ(byte[] b) { return LL(b, 40);} // central directory size - static final long ZIP64_ENDOFF(byte[] b) { return LL(b, 48);} // central directory offset - static final long ZIP64_LOCOFF(byte[] b) { return LL(b, 8);} // zip64 end offset - - // central directory header (CEN) fields - static final long CENSIG(byte[] b, int pos) { return LG(b, pos + 0); } - static final int CENVEM(byte[] b, int pos) { return SH(b, pos + 4); } - static final int CENVER(byte[] b, int pos) { return SH(b, pos + 6); } - static final int CENFLG(byte[] b, int pos) { return SH(b, pos + 8); } - static final int CENHOW(byte[] b, int pos) { return SH(b, pos + 10);} - static final long CENTIM(byte[] b, int pos) { return LG(b, pos + 12);} - static final long CENCRC(byte[] b, int pos) { return LG(b, pos + 16);} - static final long CENSIZ(byte[] b, int pos) { return LG(b, pos + 20);} - static final long CENLEN(byte[] b, int pos) { return LG(b, pos + 24);} - static final int CENNAM(byte[] b, int pos) { return SH(b, pos + 28);} - static final int CENEXT(byte[] b, int pos) { return SH(b, pos + 30);} - static final int CENCOM(byte[] b, int pos) { return SH(b, pos + 32);} - static final int CENDSK(byte[] b, int pos) { return SH(b, pos + 34);} - static final int CENATT(byte[] b, int pos) { return SH(b, pos + 36);} - static final long CENATX(byte[] b, int pos) { return LG(b, pos + 38);} - static final long CENOFF(byte[] b, int pos) { return LG(b, pos + 42);} - - // The END header is followed by a variable length comment of size < 64k. - static final long END_MAXLEN = 0xFFFF + ENDHDR; - static final int READBLOCKSZ = 128; } diff --git a/jdk/src/java.base/share/classes/jdk/internal/misc/JavaUtilZipFileAccess.java b/jdk/src/java.base/share/classes/jdk/internal/misc/JavaUtilZipFileAccess.java index 9b9b1e85788..df10fda22ca 100644 --- a/jdk/src/java.base/share/classes/jdk/internal/misc/JavaUtilZipFileAccess.java +++ b/jdk/src/java.base/share/classes/jdk/internal/misc/JavaUtilZipFileAccess.java @@ -29,6 +29,5 @@ public interface JavaUtilZipFileAccess { public boolean startsWithLocHeader(ZipFile zip); - public String[] getMetaInfEntryNames(ZipFile zip); } diff --git a/jdk/src/java.base/share/classes/sun/misc/VM.java b/jdk/src/java.base/share/classes/sun/misc/VM.java index 37dc3b38fa1..0c75f10c657 100644 --- a/jdk/src/java.base/share/classes/sun/misc/VM.java +++ b/jdk/src/java.base/share/classes/sun/misc/VM.java @@ -274,6 +274,9 @@ public static void saveAndRemoveProperties(Properties props) { // used by java.lang.Integer.IntegerCache props.remove("java.lang.Integer.IntegerCache.high"); + // used by java.util.zip.ZipFile + props.remove("sun.zip.disableMemoryMapping"); + // used by sun.launcher.LauncherHelper props.remove("sun.java.launcher.diag"); } diff --git a/jdk/src/java.base/share/native/libzip/ZipFile.c b/jdk/src/java.base/share/native/libzip/ZipFile.c new file mode 100644 index 00000000000..d7a21a6cf88 --- /dev/null +++ b/jdk/src/java.base/share/native/libzip/ZipFile.c @@ -0,0 +1,406 @@ +/* + * Copyright (c) 1998, 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. + */ + +/* + * Native method support for java.util.zip.ZipFile + */ + +#include +#include +#include +#include +#include +#include +#include "jlong.h" +#include "jvm.h" +#include "jni.h" +#include "jni_util.h" +#include "zip_util.h" +#ifdef WIN32 +#include "io_util_md.h" +#else +#include "io_util.h" +#endif + +#include "java_util_zip_ZipFile.h" +#include "java_util_jar_JarFile.h" + +#define DEFLATED 8 +#define STORED 0 + +static jfieldID jzfileID; + +static int OPEN_READ = java_util_zip_ZipFile_OPEN_READ; +static int OPEN_DELETE = java_util_zip_ZipFile_OPEN_DELETE; + + +/* + * Declare library specific JNI_Onload entry if static build + */ +DEF_STATIC_JNI_OnLoad + +JNIEXPORT void JNICALL +Java_java_util_zip_ZipFile_initIDs(JNIEnv *env, jclass cls) +{ + jzfileID = (*env)->GetFieldID(env, cls, "jzfile", "J"); + assert(jzfileID != 0); +} + +static void +ThrowZipException(JNIEnv *env, const char *msg) +{ + jstring s = NULL; + jobject x; + + if (msg != NULL) { + s = JNU_NewStringPlatform(env, msg); + } + if (s != NULL) { + x = JNU_NewObjectByName(env, + "java/util/zip/ZipException", + "(Ljava/lang/String;)V", s); + if (x != NULL) { + (*env)->Throw(env, x); + } + } +} + +JNIEXPORT jlong JNICALL +Java_java_util_zip_ZipFile_open(JNIEnv *env, jclass cls, jstring name, + jint mode, jlong lastModified, + jboolean usemmap) +{ + const char *path = JNU_GetStringPlatformChars(env, name, 0); + char *msg = 0; + jlong result = 0; + int flag = 0; + jzfile *zip = 0; + + if (mode & OPEN_READ) flag |= O_RDONLY; + + if (path != 0) { + zip = ZIP_Get_From_Cache(path, &msg, lastModified); + if (zip == 0 && msg == 0) { + ZFILE zfd = 0; +#ifdef WIN32 + if (mode & OPEN_DELETE) flag |= O_TEMPORARY; + zfd = winFileHandleOpen(env, name, flag); + if (zfd == -1) { + /* Exception already pending. */ + goto finally; + } +#else + zfd = open(path, flag, 0); + if (zfd < 0) { + throwFileNotFoundException(env, name); + goto finally; + } + if (mode & OPEN_DELETE) { + unlink(path); + } +#endif + zip = ZIP_Put_In_Cache0(path, zfd, &msg, lastModified, usemmap); + } + + if (zip != 0) { + result = ptr_to_jlong(zip); + } else if (msg != 0) { + ThrowZipException(env, msg); + free(msg); + } else if (errno == ENOMEM) { + JNU_ThrowOutOfMemoryError(env, 0); + } else { + ThrowZipException(env, "error in opening zip file"); + } +finally: + JNU_ReleaseStringPlatformChars(env, name, path); + } + return result; +} + +JNIEXPORT jint JNICALL +Java_java_util_zip_ZipFile_getTotal(JNIEnv *env, jclass cls, jlong zfile) +{ + jzfile *zip = jlong_to_ptr(zfile); + + return zip->total; +} + +JNIEXPORT jboolean JNICALL +Java_java_util_zip_ZipFile_startsWithLOC(JNIEnv *env, jclass cls, jlong zfile) +{ + jzfile *zip = jlong_to_ptr(zfile); + + return zip->locsig; +} + +JNIEXPORT void JNICALL +Java_java_util_zip_ZipFile_close(JNIEnv *env, jclass cls, jlong zfile) +{ + ZIP_Close(jlong_to_ptr(zfile)); +} + +JNIEXPORT jlong JNICALL +Java_java_util_zip_ZipFile_getEntry(JNIEnv *env, jclass cls, jlong zfile, + jbyteArray name, jboolean addSlash) +{ +#define MAXNAME 1024 + jzfile *zip = jlong_to_ptr(zfile); + jsize ulen = (*env)->GetArrayLength(env, name); + char buf[MAXNAME+2], *path; + jzentry *ze; + + if (ulen > MAXNAME) { + path = malloc(ulen + 2); + if (path == 0) { + JNU_ThrowOutOfMemoryError(env, 0); + return 0; + } + } else { + path = buf; + } + (*env)->GetByteArrayRegion(env, name, 0, ulen, (jbyte *)path); + path[ulen] = '\0'; + ze = ZIP_GetEntry2(zip, path, (jint)ulen, addSlash); + if (path != buf) { + free(path); + } + return ptr_to_jlong(ze); +} + +JNIEXPORT void JNICALL +Java_java_util_zip_ZipFile_freeEntry(JNIEnv *env, jclass cls, jlong zfile, + jlong zentry) +{ + jzfile *zip = jlong_to_ptr(zfile); + jzentry *ze = jlong_to_ptr(zentry); + ZIP_FreeEntry(zip, ze); +} + +JNIEXPORT jlong JNICALL +Java_java_util_zip_ZipFile_getNextEntry(JNIEnv *env, jclass cls, jlong zfile, + jint n) +{ + jzentry *ze = ZIP_GetNextEntry(jlong_to_ptr(zfile), n); + return ptr_to_jlong(ze); +} + +JNIEXPORT jint JNICALL +Java_java_util_zip_ZipFile_getEntryMethod(JNIEnv *env, jclass cls, jlong zentry) +{ + jzentry *ze = jlong_to_ptr(zentry); + return ze->csize != 0 ? DEFLATED : STORED; +} + +JNIEXPORT jint JNICALL +Java_java_util_zip_ZipFile_getEntryFlag(JNIEnv *env, jclass cls, jlong zentry) +{ + jzentry *ze = jlong_to_ptr(zentry); + return ze->flag; +} + +JNIEXPORT jlong JNICALL +Java_java_util_zip_ZipFile_getEntryCSize(JNIEnv *env, jclass cls, jlong zentry) +{ + jzentry *ze = jlong_to_ptr(zentry); + return ze->csize != 0 ? ze->csize : ze->size; +} + +JNIEXPORT jlong JNICALL +Java_java_util_zip_ZipFile_getEntrySize(JNIEnv *env, jclass cls, jlong zentry) +{ + jzentry *ze = jlong_to_ptr(zentry); + return ze->size; +} + +JNIEXPORT jlong JNICALL +Java_java_util_zip_ZipFile_getEntryTime(JNIEnv *env, jclass cls, jlong zentry) +{ + jzentry *ze = jlong_to_ptr(zentry); + return (jlong)ze->time & 0xffffffffUL; +} + +JNIEXPORT jlong JNICALL +Java_java_util_zip_ZipFile_getEntryCrc(JNIEnv *env, jclass cls, jlong zentry) +{ + jzentry *ze = jlong_to_ptr(zentry); + return (jlong)ze->crc & 0xffffffffUL; +} + +JNIEXPORT jbyteArray JNICALL +Java_java_util_zip_ZipFile_getCommentBytes(JNIEnv *env, + jclass cls, + jlong zfile) +{ + jzfile *zip = jlong_to_ptr(zfile); + jbyteArray jba = NULL; + + if (zip->comment != NULL) { + if ((jba = (*env)->NewByteArray(env, zip->clen)) == NULL) + return NULL; + (*env)->SetByteArrayRegion(env, jba, 0, zip->clen, (jbyte*)zip->comment); + } + return jba; +} + +JNIEXPORT jbyteArray JNICALL +Java_java_util_zip_ZipFile_getEntryBytes(JNIEnv *env, + jclass cls, + jlong zentry, jint type) +{ + jzentry *ze = jlong_to_ptr(zentry); + int len = 0; + jbyteArray jba = NULL; + switch (type) { + case java_util_zip_ZipFile_JZENTRY_NAME: + if (ze->name != 0) { + len = (int)ze->nlen; + // Unlike for extra and comment, we never return null for + // an (extremely rarely seen) empty name + if ((jba = (*env)->NewByteArray(env, len)) == NULL) + break; + (*env)->SetByteArrayRegion(env, jba, 0, len, (jbyte *)ze->name); + } + break; + case java_util_zip_ZipFile_JZENTRY_EXTRA: + if (ze->extra != 0) { + unsigned char *bp = (unsigned char *)&ze->extra[0]; + len = (bp[0] | (bp[1] << 8)); + if (len <= 0 || (jba = (*env)->NewByteArray(env, len)) == NULL) + break; + (*env)->SetByteArrayRegion(env, jba, 0, len, &ze->extra[2]); + } + break; + case java_util_zip_ZipFile_JZENTRY_COMMENT: + if (ze->comment != 0) { + len = (int)strlen(ze->comment); + if (len == 0 || (jba = (*env)->NewByteArray(env, len)) == NULL) + break; + (*env)->SetByteArrayRegion(env, jba, 0, len, (jbyte*)ze->comment); + } + break; + } + return jba; +} + +JNIEXPORT jint JNICALL +Java_java_util_zip_ZipFile_read(JNIEnv *env, jclass cls, jlong zfile, + jlong zentry, jlong pos, jbyteArray bytes, + jint off, jint len) +{ + jzfile *zip = jlong_to_ptr(zfile); + char *msg; + +#define BUFSIZE 8192 + /* copy via tmp stack buffer: */ + jbyte buf[BUFSIZE]; + + if (len > BUFSIZE) { + len = BUFSIZE; + } + + ZIP_Lock(zip); + len = ZIP_Read(zip, jlong_to_ptr(zentry), pos, buf, len); + msg = zip->msg; + ZIP_Unlock(zip); + if (len != -1) { + (*env)->SetByteArrayRegion(env, bytes, off, len, buf); + } + + if (len == -1) { + if (msg != 0) { + ThrowZipException(env, msg); + } else { + char errmsg[128]; + sprintf(errmsg, "errno: %d, error: %s\n", + errno, "Error reading ZIP file"); + JNU_ThrowIOExceptionWithLastError(env, errmsg); + } + } + + return len; +} + +/* + * Returns an array of strings representing the names of all entries + * that begin with "META-INF/" (case ignored). This native method is + * used in JarFile as an optimization when looking up manifest and + * signature file entries. Returns null if no entries were found. + */ +JNIEXPORT jobjectArray JNICALL +Java_java_util_jar_JarFile_getMetaInfEntryNames(JNIEnv *env, jobject obj) +{ + jlong zfile = (*env)->GetLongField(env, obj, jzfileID); + jzfile *zip; + int i, count; + jobjectArray result = 0; + + if (zfile == 0) { + JNU_ThrowByName(env, + "java/lang/IllegalStateException", "zip file closed"); + return NULL; + } + zip = jlong_to_ptr(zfile); + + /* count the number of valid ZIP metanames */ + count = 0; + if (zip->metanames != 0) { + for (i = 0; i < zip->metacount; i++) { + if (zip->metanames[i] != 0) { + count++; + } + } + } + + /* If some names were found then build array of java strings */ + if (count > 0) { + jclass cls = JNU_ClassString(env); + CHECK_NULL_RETURN(cls, NULL); + result = (*env)->NewObjectArray(env, count, cls, 0); + CHECK_NULL_RETURN(result, NULL); + if (result != 0) { + for (i = 0; i < count; i++) { + jstring str = (*env)->NewStringUTF(env, zip->metanames[i]); + if (str == 0) { + break; + } + (*env)->SetObjectArrayElement(env, result, i, str); + (*env)->DeleteLocalRef(env, str); + } + } + } + return result; +} + +JNIEXPORT jstring JNICALL +Java_java_util_zip_ZipFile_getZipMessage(JNIEnv *env, jclass cls, jlong zfile) +{ + jzfile *zip = jlong_to_ptr(zfile); + char *msg = zip->msg; + if (msg == NULL) { + return NULL; + } + return JNU_NewStringPlatform(env, msg); +} diff --git a/jdk/test/java/util/zip/ZipFile/ReadZip.java b/jdk/test/java/util/zip/ZipFile/ReadZip.java index fe923e81eee..1052642eda7 100644 --- a/jdk/test/java/util/zip/ZipFile/ReadZip.java +++ b/jdk/test/java/util/zip/ZipFile/ReadZip.java @@ -30,7 +30,6 @@ import java.io.*; import java.nio.file.Files; import java.nio.file.Paths; -import java.nio.file.NoSuchFileException; import java.nio.file.StandardCopyOption; import java.nio.file.StandardOpenOption; import java.util.zip.*; @@ -111,6 +110,6 @@ public static void main(String args[]) throws Exception { "input" + String.valueOf(new java.util.Random().nextInt()) + ".zip"))); - } catch (NoSuchFileException nsfe) {} + } catch (FileNotFoundException fnfe) {} } } diff --git a/jdk/test/java/util/zip/ZipFile/TestZipFile.java b/jdk/test/java/util/zip/ZipFile/TestZipFile.java deleted file mode 100644 index 986877731db..00000000000 --- a/jdk/test/java/util/zip/ZipFile/TestZipFile.java +++ /dev/null @@ -1,361 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test - * @bug 8142508 - * @summary Tests various ZipFile apis - * @run main/manual TestZipFile - */ - -import java.io.*; -import java.lang.reflect.Method; -import java.nio.*; -import java.nio.file.*; -import java.nio.file.attribute.*; -import java.util.*; -import java.util.concurrent.*; -import java.util.zip.*; - -public class TestZipFile { - - private static Random r = new Random(); - private static int N = 50; - private static int NN = 10; - private static int ENUM = 10000; - private static int ESZ = 10000; - private static ExecutorService executor = Executors.newFixedThreadPool(20); - private static Set paths = new HashSet<>(); - - static void realMain (String[] args) throws Throwable { - - try { - for (int i = 0; i < N; i++) { - test(r.nextInt(ENUM), r.nextInt(ESZ), false, true); - test(r.nextInt(ENUM), r.nextInt(ESZ), true, true); - } - - for (int i = 0; i < NN; i++) { - test(r.nextInt(ENUM), 100000 + r.nextInt(ESZ), false, true); - test(r.nextInt(ENUM), 100000 + r.nextInt(ESZ), true, true); - testCachedDelete(); - testCachedOverwrite(); - //test(r.nextInt(ENUM), r.nextInt(ESZ), false, true); - } - - test(70000, 1000, false, true); // > 65536 entry number; - testDelete(); // OPEN_DELETE - - executor.shutdown(); - executor.awaitTermination(10, TimeUnit.MINUTES); - } finally { - for (Path path : paths) { - Files.deleteIfExists(path); - } - } - } - - static void test(int numEntry, int szMax, boolean addPrefix, boolean cleanOld) { - String name = "zftest" + r.nextInt() + ".zip"; - Zip zip = new Zip(name, numEntry, szMax, addPrefix, cleanOld); - for (int i = 0; i < NN; i++) { - executor.submit(() -> doTest(zip)); - } - } - - // test scenario: - // (1) open the ZipFile(zip) with OPEN_READ | OPEN_DELETE - // (2) test the ZipFile works correctly - // (3) check the zip is deleted after ZipFile gets closed - static void testDelete() throws Throwable { - String name = "zftest" + r.nextInt() + ".zip"; - Zip zip = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, true); - try (ZipFile zf = new ZipFile(new File(zip.name), - ZipFile.OPEN_READ | ZipFile.OPEN_DELETE )) - { - doTest0(zip, zf); - } - Path p = Paths.get(name); - if (Files.exists(p)) { - fail("Failed to delete " + name + " with OPEN_DELETE"); - } - } - - // test scenario: - // (1) keep a ZipFile(zip1) alive (in ZipFile's cache), dont close it - // (2) delete zip1 and create zip2 with the same name the zip1 with zip2 - // (3) zip1 tests should fail, but no crash - // (4) zip2 tasks should all get zip2, then pass normal testing. - static void testCachedDelete() throws Throwable { - String name = "zftest" + r.nextInt() + ".zip"; - Zip zip1 = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, true); - - try (ZipFile zf = new ZipFile(zip1.name)) { - for (int i = 0; i < NN; i++) { - executor.submit(() -> verifyNoCrash(zip1)); - } - // delete the "zip1" and create a new one to test - Zip zip2 = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, true); - /* - System.out.println("========================================"); - System.out.printf(" zip1=%s, mt=%d, enum=%d%n ->attrs=[key=%s, sz=%d, mt=%d]%n", - zip1.name, zip1.lastModified, zip1.entries.size(), - zip1.attrs.fileKey(), zip1.attrs.size(), zip1.attrs.lastModifiedTime().toMillis()); - System.out.printf(" zip2=%s, mt=%d, enum=%d%n ->attrs=[key=%s, sz=%d, mt=%d]%n", - zip2.name, zip2.lastModified, zip2.entries.size(), - zip2.attrs.fileKey(), zip2.attrs.size(), zip2.attrs.lastModifiedTime().toMillis()); - */ - for (int i = 0; i < NN; i++) { - executor.submit(() -> doTest(zip2)); - } - } - } - - // overwrite the "zip1" and create a new one to test. So the two zip files - // have the same fileKey, but probably different lastModified() - static void testCachedOverwrite() throws Throwable { - String name = "zftest" + r.nextInt() + ".zip"; - Zip zip1 = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, true); - try (ZipFile zf = new ZipFile(zip1.name)) { - for (int i = 0; i < NN; i++) { - executor.submit(() -> verifyNoCrash(zip1)); - } - // overwrite the "zip1" with new contents - Zip zip2 = new Zip(name, r.nextInt(ENUM), r.nextInt(ESZ), false, false); - for (int i = 0; i < NN; i++) { - executor.submit(() -> doTest(zip2)); - } - } - } - - // just check the entries and contents. since the file has been either overwritten - // or deleted/rewritten, we only care if it crahes or not. - static void verifyNoCrash(Zip zip) throws RuntimeException { - try (ZipFile zf = new ZipFile(zip.name)) { - List zlist = new ArrayList(zip.entries.keySet()); - String[] elist = zf.stream().map( e -> e.getName()).toArray(String[]::new); - if (!Arrays.equals(elist, - zlist.stream().map( e -> e.getName()).toArray(String[]::new))) - { - //System.out.printf("++++++ LIST NG [%s] entries.len=%d, expected=%d+++++++%n", - // zf.getName(), elist.length, zlist.size()); - return; - } - for (ZipEntry ze : zlist) { - byte[] zdata = zip.entries.get(ze); - ZipEntry e = zf.getEntry(ze.getName()); - if (e != null) { - checkEqual(e, ze); - if (!e.isDirectory()) { - // check with readAllBytes - try (InputStream is = zf.getInputStream(e)) { - if (!Arrays.equals(zdata, is.readAllBytes())) { - //System.out.printf("++++++ BYTES NG [%s]/[%s] ++++++++%n", - // zf.getName(), ze.getName()); - } - } - } - } - } - } catch (Throwable t) { - // t.printStackTrace(); - // fail(t.toString()); - } - } - - static void checkEqual(ZipEntry x, ZipEntry y) { - if (x.getName().equals(y.getName()) && - x.isDirectory() == y.isDirectory() && - x.getMethod() == y.getMethod() && - (x.getTime() / 2000) == y.getTime() / 2000 && - x.getSize() == y.getSize() && - x.getCompressedSize() == y.getCompressedSize() && - x.getCrc() == y.getCrc() && - x.getComment().equals(y.getComment()) - ) { - pass(); - } else { - fail(x + " not equal to " + y); - System.out.printf(" %s %s%n", x.getName(), y.getName()); - System.out.printf(" %d %d%n", x.getMethod(), y.getMethod()); - System.out.printf(" %d %d%n", x.getTime(), y.getTime()); - System.out.printf(" %d %d%n", x.getSize(), y.getSize()); - System.out.printf(" %d %d%n", x.getCompressedSize(), y.getCompressedSize()); - System.out.printf(" %d %d%n", x.getCrc(), y.getCrc()); - System.out.println("-----------------"); - } - } - - static void doTest(Zip zip) throws RuntimeException { - //Thread me = Thread.currentThread(); - try (ZipFile zf = new ZipFile(zip.name)) { - doTest0(zip, zf); - } catch (Throwable t) { - throw new RuntimeException(t); - } - } - - static void doTest0(Zip zip, ZipFile zf) throws Throwable { - List list = new ArrayList(zip.entries.keySet()); - // (1) check entry list, in expected order - if (!check(Arrays.equals( - list.stream().map( e -> e.getName()).toArray(String[]::new), - zf.stream().map( e -> e.getName()).toArray(String[]::new)))) { - return; - } - // (2) shuffle, and check each entry and its bytes - Collections.shuffle(list); - for (ZipEntry ze : list) { - byte[] data = zip.entries.get(ze); - ZipEntry e = zf.getEntry(ze.getName()); - checkEqual(e, ze); - if (!e.isDirectory()) { - // check with readAllBytes - try (InputStream is = zf.getInputStream(e)) { - check(Arrays.equals(data, is.readAllBytes())); - } - // check with smaller sized buf - try (InputStream is = zf.getInputStream(e)) { - byte[] buf = new byte[(int)e.getSize()]; - int sz = r.nextInt((int)e.getSize()/4 + 1) + 1; - int off = 0; - int n; - while ((n = is.read(buf, off, buf.length - off)) > 0) { - off += n; - } - check(is.read() == -1); - check(Arrays.equals(data, buf)); - } - } - } - // (3) check getMetaInfEntryNames - String[] metas = list.stream() - .map( e -> e.getName()) - .filter( s -> s.startsWith("META-INF/")) - .sorted() - .toArray(String[]::new); - if (metas.length > 0) { - // meta-inf entries - Method getMetas = ZipFile.class.getDeclaredMethod("getMetaInfEntryNames"); - getMetas.setAccessible(true); - String[] names = (String[])getMetas.invoke(zf); - if (names == null) { - fail("Failed to get metanames from " + zf); - } else { - Arrays.sort(names); - check(Arrays.equals(names, metas)); - } - } - } - - private static class Zip { - String name; - Map entries; - BasicFileAttributes attrs; - long lastModified; - - Zip(String name, int num, int szMax, boolean prefix, boolean clean) { - this.name = name; - entries = new LinkedHashMap<>(num); - try { - Path p = Paths.get(name); - if (clean) { - Files.deleteIfExists(p); - } - paths.add(p); - } catch (Exception x) { - throw (RuntimeException)x; - } - - try (FileOutputStream fos = new FileOutputStream(name); - BufferedOutputStream bos = new BufferedOutputStream(fos); - ZipOutputStream zos = new ZipOutputStream(bos)) - { - if (prefix) { - byte[] bytes = new byte[r.nextInt(1000)]; - r.nextBytes(bytes); - bos.write(bytes); - } - CRC32 crc = new CRC32(); - for (int i = 0; i < num; i++) { - String ename = "entry-" + i + "-name-" + r.nextLong(); - ZipEntry ze = new ZipEntry(ename); - int method = r.nextBoolean() ? ZipEntry.STORED : ZipEntry.DEFLATED; - writeEntry(zos, crc, ze, ZipEntry.STORED, szMax); - } - // add some manifest entries - for (int i = 0; i < r.nextInt(20); i++) { - String meta = "META-INF/" + "entry-" + i + "-metainf-" + r.nextLong(); - ZipEntry ze = new ZipEntry(meta); - writeEntry(zos, crc, ze, ZipEntry.STORED, szMax); - } - } catch (Exception x) { - throw (RuntimeException)x; - } - try { - this.attrs = Files.readAttributes(Paths.get(name), BasicFileAttributes.class); - this.lastModified = new File(name).lastModified(); - } catch (Exception x) { - throw (RuntimeException)x; - } - } - - private void writeEntry(ZipOutputStream zos, CRC32 crc, - ZipEntry ze, int method, int szMax) - throws IOException - { - ze.setMethod(method); - byte[] data = new byte[r.nextInt(szMax + 1)]; - r.nextBytes(data); - if (method == ZipEntry.STORED) { // must set size/csize/crc - ze.setSize(data.length); - ze.setCompressedSize(data.length); - crc.reset(); - crc.update(data); - ze.setCrc(crc.getValue()); - } - ze.setTime(System.currentTimeMillis()); - ze.setComment(ze.getName()); - zos.putNextEntry(ze); - zos.write(data); - zos.closeEntry(); - entries.put(ze, data); - } - } - - //--------------------- Infrastructure --------------------------- - static volatile int passed = 0, failed = 0; - static void pass() {passed++;} - static void pass(String msg) {System.out.println(msg); passed++;} - static void fail() {failed++; Thread.dumpStack();} - static void fail(String msg) {System.out.println(msg); fail();} - static void unexpected(Throwable t) {failed++; t.printStackTrace();} - static void unexpected(Throwable t, String msg) { - System.out.println(msg); failed++; t.printStackTrace();} - static boolean check(boolean cond) {if (cond) pass(); else fail(); return cond;} - - public static void main(String[] args) throws Throwable { - try {realMain(args);} catch (Throwable t) {unexpected(t);} - System.out.println("\nPassed = " + passed + " failed = " + failed); - if (failed > 0) throw new AssertionError("Some tests failed");} -} From d42e70fc3c5552af8dcaa1375322f9464019efad Mon Sep 17 00:00:00 2001 From: Stuart Marks Date: Tue, 8 Dec 2015 13:48:22 -0800 Subject: [PATCH 26/62] 8139232: JEP-269 initial API and skeleton implementations Reviewed-by: psandoz, rriggs --- .../classes/java/util/KeyValueHolder.java | 126 +++++ .../share/classes/java/util/List.java | 332 +++++++++++- .../share/classes/java/util/Map.java | 496 +++++++++++++++++- .../share/classes/java/util/Set.java | 362 ++++++++++++- jdk/test/java/util/Collection/MOAT.java | 125 ++++- .../java/util/Collection/SetFactories.java | 275 ++++++++++ jdk/test/java/util/List/ListFactories.java | 234 +++++++++ jdk/test/java/util/Map/MapFactories.java | 380 ++++++++++++++ 8 files changed, 2315 insertions(+), 15 deletions(-) create mode 100644 jdk/src/java.base/share/classes/java/util/KeyValueHolder.java create mode 100644 jdk/test/java/util/Collection/SetFactories.java create mode 100644 jdk/test/java/util/List/ListFactories.java create mode 100644 jdk/test/java/util/Map/MapFactories.java diff --git a/jdk/src/java.base/share/classes/java/util/KeyValueHolder.java b/jdk/src/java.base/share/classes/java/util/KeyValueHolder.java new file mode 100644 index 00000000000..96ca0753ac6 --- /dev/null +++ b/jdk/src/java.base/share/classes/java/util/KeyValueHolder.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.util; + +/** + * An immutable container for a key and a value, suitable for use + * in creating and populating {@code Map} instances. + * + *

This is a value-based + * class; use of identity-sensitive operations (including reference equality + * ({@code ==}), identity hash code, or synchronization) on instances of + * {@code KeyValueHolder} may have unpredictable results and should be avoided. + * + * @apiNote + * This class is not public. Instances can be created using the + * {@link Map#entry Map.entry(k, v)} factory method, which is public. + * + *

This class differs from AbstractMap.SimpleImmutableEntry in the following ways: + * it is not serializable, it is final, and its key and value must be non-null. + * + * @param the key type + * @param the value type + * + * @see Map#ofEntries Map.ofEntries() + * @since 9 + */ +final class KeyValueHolder implements Map.Entry { + final K key; + final V value; + + KeyValueHolder(K k, V v) { + key = Objects.requireNonNull(k); + value = Objects.requireNonNull(v); + } + + /** + * Gets the key from this holder. + * + * @return the key + */ + @Override + public K getKey() { + return key; + } + + /** + * Gets the value from this holder. + * + * @return the value + */ + @Override + public V getValue() { + return value; + } + + /** + * Throws {@link UnsupportedOperationException}. + * + * @param value ignored + * @return never returns normally + */ + @Override + public V setValue(V value) { + throw new UnsupportedOperationException("not supported"); + } + + /** + * Compares the specified object with this entry for equality. + * Returns {@code true} if the given object is also a map entry and + * the two entries' keys and values are equal. Note that key and + * value are non-null, so equals() can be called safely on them. + */ + @Override + public boolean equals(Object o) { + if (!(o instanceof Map.Entry)) + return false; + Map.Entry e = (Map.Entry)o; + return key.equals(e.getKey()) && value.equals(e.getValue()); + } + + /** + * Returns the hash code value for this map entry. The hash code + * is {@code key.hashCode() ^ value.hashCode()}. Note that key and + * value are non-null, so hashCode() can be called safely on them. + */ + @Override + public int hashCode() { + return key.hashCode() ^ value.hashCode(); + } + + /** + * Returns a String representation of this map entry. This + * implementation returns the string representation of this + * entry's key followed by the equals character ("{@code =}") + * followed by the string representation of this entry's value. + * + * @return a String representation of this map entry + */ + @Override + public String toString() { + return key + "=" + value; + } +} diff --git a/jdk/src/java.base/share/classes/java/util/List.java b/jdk/src/java.base/share/classes/java/util/List.java index 6e3751fa046..48464801316 100644 --- a/jdk/src/java.base/share/classes/java/util/List.java +++ b/jdk/src/java.base/share/classes/java/util/List.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -87,6 +87,28 @@ * Such exceptions are marked as "optional" in the specification for this * interface. * + *

Immutable List Static Factory Methods

+ *

The {@link List#of(Object...) List.of()} static factory methods + * provide a convenient way to create immutable lists. The {@code List} + * instances created by these methods have the following characteristics: + * + *

    + *
  • They are structurally immutable. Elements cannot be added, removed, + * or replaced. Attempts to do so result in {@code UnsupportedOperationException}. + * However, if the contained elements are themselves mutable, + * this may cause the List's contents to appear to change. + *
  • They disallow {@code null} elements. Attempts to create them with + * {@code null} elements result in {@code NullPointerException}. + *
  • They are serializable if all elements are serializable. + *
  • The order of elements in the list is the same as the order of the + * provided arguments, or of the elements in the provided array. + *
  • They are value-based. + * Callers should make no assumptions about the identity of the returned instances. + * Factories are free to create new instances or reuse existing ones. Therefore, + * identity-sensitive operations on these instances (reference equality ({@code ==}), + * identity hash code, and synchronization) are unreliable and should be avoided. + *
+ * *

This interface is a member of the * * Java Collections Framework. @@ -731,4 +753,312 @@ default void sort(Comparator c) { default Spliterator spliterator() { return Spliterators.spliterator(this, Spliterator.ORDERED); } + + /** + * Returns an immutable list containing zero elements. + * + * See Immutable List Static Factory Methods for details. + * + * @param the {@code List}'s element type + * @return an empty {@code List} + * + * @since 9 + */ + static List of() { + return Collections.emptyList(); + } + + /** + * Returns an immutable list containing one element. + * + * See Immutable List Static Factory Methods for details. + * + * @param the {@code List}'s element type + * @param e1 the single element + * @return a {@code List} containing the specified element + * @throws NullPointerException if the element is {@code null} + * + * @since 9 + */ + static List of(E e1) { + return Collections.singletonList(Objects.requireNonNull(e1)); + } + + /** + * Returns an immutable list containing two elements. + * + * See Immutable List Static Factory Methods for details. + * + * @param the {@code List}'s element type + * @param e1 the first element + * @param e2 the second element + * @return a {@code List} containing the specified elements + * @throws NullPointerException if an element is {@code null} + * + * @since 9 + */ + static List of(E e1, E e2) { + return Collections.unmodifiableList( + Arrays.asList(Objects.requireNonNull(e1), + Objects.requireNonNull(e2))); + } + + /** + * Returns an immutable list containing three elements. + * + * See Immutable List Static Factory Methods for details. + * + * @param the {@code List}'s element type + * @param e1 the first element + * @param e2 the second element + * @param e3 the third element + * @return a {@code List} containing the specified elements + * @throws NullPointerException if an element is {@code null} + * + * @since 9 + */ + static List of(E e1, E e2, E e3) { + return Collections.unmodifiableList( + Arrays.asList(Objects.requireNonNull(e1), + Objects.requireNonNull(e2), + Objects.requireNonNull(e3))); + } + + /** + * Returns an immutable list containing four elements. + * + * See Immutable List Static Factory Methods for details. + * + * @param the {@code List}'s element type + * @param e1 the first element + * @param e2 the second element + * @param e3 the third element + * @param e4 the fourth element + * @return a {@code List} containing the specified elements + * @throws NullPointerException if an element is {@code null} + * + * @since 9 + */ + static List of(E e1, E e2, E e3, E e4) { + return Collections.unmodifiableList( + Arrays.asList(Objects.requireNonNull(e1), + Objects.requireNonNull(e2), + Objects.requireNonNull(e3), + Objects.requireNonNull(e4))); + } + + /** + * Returns an immutable list containing five elements. + * + * See Immutable List Static Factory Methods for details. + * + * @param the {@code List}'s element type + * @param e1 the first element + * @param e2 the second element + * @param e3 the third element + * @param e4 the fourth element + * @param e5 the fifth element + * @return a {@code List} containing the specified elements + * @throws NullPointerException if an element is {@code null} + * + * @since 9 + */ + static List of(E e1, E e2, E e3, E e4, E e5) { + return Collections.unmodifiableList( + Arrays.asList(Objects.requireNonNull(e1), + Objects.requireNonNull(e2), + Objects.requireNonNull(e3), + Objects.requireNonNull(e4), + Objects.requireNonNull(e5))); + } + + /** + * Returns an immutable list containing six elements. + * + * See Immutable List Static Factory Methods for details. + * + * @param the {@code List}'s element type + * @param e1 the first element + * @param e2 the second element + * @param e3 the third element + * @param e4 the fourth element + * @param e5 the fifth element + * @param e6 the sixth element + * @return a {@code List} containing the specified elements + * @throws NullPointerException if an element is {@code null} + * + * @since 9 + */ + static List of(E e1, E e2, E e3, E e4, E e5, E e6) { + return Collections.unmodifiableList( + Arrays.asList(Objects.requireNonNull(e1), + Objects.requireNonNull(e2), + Objects.requireNonNull(e3), + Objects.requireNonNull(e4), + Objects.requireNonNull(e5), + Objects.requireNonNull(e6))); + } + + /** + * Returns an immutable list containing seven elements. + * + * See Immutable List Static Factory Methods for details. + * + * @param the {@code List}'s element type + * @param e1 the first element + * @param e2 the second element + * @param e3 the third element + * @param e4 the fourth element + * @param e5 the fifth element + * @param e6 the sixth element + * @param e7 the seventh element + * @return a {@code List} containing the specified elements + * @throws NullPointerException if an element is {@code null} + * + * @since 9 + */ + static List of(E e1, E e2, E e3, E e4, E e5, E e6, E e7) { + return Collections.unmodifiableList( + Arrays.asList(Objects.requireNonNull(e1), + Objects.requireNonNull(e2), + Objects.requireNonNull(e3), + Objects.requireNonNull(e4), + Objects.requireNonNull(e5), + Objects.requireNonNull(e6), + Objects.requireNonNull(e7))); + } + + /** + * Returns an immutable list containing eight elements. + * + * See Immutable List Static Factory Methods for details. + * + * @param the {@code List}'s element type + * @param e1 the first element + * @param e2 the second element + * @param e3 the third element + * @param e4 the fourth element + * @param e5 the fifth element + * @param e6 the sixth element + * @param e7 the seventh element + * @param e8 the eighth element + * @return a {@code List} containing the specified elements + * @throws NullPointerException if an element is {@code null} + * + * @since 9 + */ + static List of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8) { + return Collections.unmodifiableList( + Arrays.asList(Objects.requireNonNull(e1), + Objects.requireNonNull(e2), + Objects.requireNonNull(e3), + Objects.requireNonNull(e4), + Objects.requireNonNull(e5), + Objects.requireNonNull(e6), + Objects.requireNonNull(e7), + Objects.requireNonNull(e8))); + } + + /** + * Returns an immutable list containing nine elements. + * + * See Immutable List Static Factory Methods for details. + * + * @param the {@code List}'s element type + * @param e1 the first element + * @param e2 the second element + * @param e3 the third element + * @param e4 the fourth element + * @param e5 the fifth element + * @param e6 the sixth element + * @param e7 the seventh element + * @param e8 the eighth element + * @param e9 the ninth element + * @return a {@code List} containing the specified elements + * @throws NullPointerException if an element is {@code null} + * + * @since 9 + */ + static List of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9) { + return Collections.unmodifiableList( + Arrays.asList(Objects.requireNonNull(e1), + Objects.requireNonNull(e2), + Objects.requireNonNull(e3), + Objects.requireNonNull(e4), + Objects.requireNonNull(e5), + Objects.requireNonNull(e6), + Objects.requireNonNull(e7), + Objects.requireNonNull(e8), + Objects.requireNonNull(e9))); + } + + /** + * Returns an immutable list containing ten elements. + * + * See Immutable List Static Factory Methods for details. + * + * @param the {@code List}'s element type + * @param e1 the first element + * @param e2 the second element + * @param e3 the third element + * @param e4 the fourth element + * @param e5 the fifth element + * @param e6 the sixth element + * @param e7 the seventh element + * @param e8 the eighth element + * @param e9 the ninth element + * @param e10 the tenth element + * @return a {@code List} containing the specified elements + * @throws NullPointerException if an element is {@code null} + * + * @since 9 + */ + static List of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10) { + return Collections.unmodifiableList( + Arrays.asList(Objects.requireNonNull(e1), + Objects.requireNonNull(e2), + Objects.requireNonNull(e3), + Objects.requireNonNull(e4), + Objects.requireNonNull(e5), + Objects.requireNonNull(e6), + Objects.requireNonNull(e7), + Objects.requireNonNull(e8), + Objects.requireNonNull(e9), + Objects.requireNonNull(e10))); + } + + /** + * Returns an immutable list containing an arbitrary number of elements. + * See Immutable List Static Factory Methods for details. + * + * @apiNote + * This method also accepts a single array as an argument. The element type of + * the resulting list will be the component type of the array, and the size of + * the list will be equal to the length of the array. To create a list with + * a single element that is an array, do the following: + * + *

{@code
+     *     String[] array = ... ;
+     *     List list = List.of(array);
+     * }
+ * + * This will cause the {@link List#of(Object) List.of(E)} method + * to be invoked instead. + * + * @param the {@code List}'s element type + * @param elements the elements to be contained in the list + * @return a {@code List} containing the specified elements + * @throws NullPointerException if an element is {@code null} or if the array is {@code null} + * + * @since 9 + */ + @SafeVarargs + @SuppressWarnings("varargs") + static List of(E... elements) { + elements = elements.clone(); // throws NPE if es is null + for (E e : elements) { + Objects.requireNonNull(e); + } + return Collections.unmodifiableList(Arrays.asList(elements)); + } } diff --git a/jdk/src/java.base/share/classes/java/util/Map.java b/jdk/src/java.base/share/classes/java/util/Map.java index 9d79c6ce7a9..f958607dfc5 100644 --- a/jdk/src/java.base/share/classes/java/util/Map.java +++ b/jdk/src/java.base/share/classes/java/util/Map.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -110,6 +110,31 @@ * Implementations may optionally handle the self-referential scenario, however * most current implementations do not do so. * + *

Immutable Map Static Factory Methods

+ *

The {@link Map#of() Map.of()} and + * {@link Map#ofEntries(Map.Entry...) Map.ofEntries()} + * static factory methods provide a convenient way to create immutable maps. + * The {@code Map} + * instances created by these methods have the following characteristics: + * + *

    + *
  • They are structurally immutable. Keys and values cannot be added, + * removed, or updated. Attempts to do so result in {@code UnsupportedOperationException}. + * However, if the contained keys or values are themselves mutable, this may cause the + * Map to behave inconsistently or its contents to appear to change. + *
  • They disallow {@code null} keys and values. Attempts to create them with + * {@code null} keys or values result in {@code NullPointerException}. + *
  • They are serializable if all keys and values are serializable. + *
  • They reject duplicate keys at creation time. Duplicate keys + * passed to a static factory method result in {@code IllegalArgumentException}. + *
  • The iteration order of mappings is unspecified and is subject to change. + *
  • They are value-based. + * Callers should make no assumptions about the identity of the returned instances. + * Factories are free to create new instances or reuse existing ones. Therefore, + * identity-sensitive operations on these instances (reference equality ({@code ==}), + * identity hash code, and synchronization) are unreliable and should be avoided. + *
+ * *

This interface is a member of the * * Java Collections Framework. @@ -126,7 +151,7 @@ * @see Set * @since 1.2 */ -public interface Map { +public interface Map { // Query Operations /** @@ -373,7 +398,7 @@ public interface Map { * @see Map#entrySet() * @since 1.2 */ - interface Entry { + interface Entry { /** * Returns the key corresponding to this entry. * @@ -468,7 +493,7 @@ interface Entry { * @see Comparable * @since 1.8 */ - public static , V> Comparator> comparingByKey() { + public static , V> Comparator> comparingByKey() { return (Comparator> & Serializable) (c1, c2) -> c1.getKey().compareTo(c2.getKey()); } @@ -485,7 +510,7 @@ public static , V> Comparator> co * @see Comparable * @since 1.8 */ - public static > Comparator> comparingByValue() { + public static > Comparator> comparingByValue() { return (Comparator> & Serializable) (c1, c2) -> c1.getValue().compareTo(c2.getValue()); } @@ -1233,4 +1258,465 @@ default V merge(K key, V value, } return newValue; } + + /** + * Returns an immutable map containing zero mappings. + * See Immutable Map Static Factory Methods for details. + * + * @param the {@code Map}'s key type + * @param the {@code Map}'s value type + * @return an empty {@code Map} + * + * @since 9 + */ + static Map of() { + return Collections.emptyMap(); + } + + /** + * Returns an immutable map containing a single mapping. + * See Immutable Map Static Factory Methods for details. + * + * @param the {@code Map}'s key type + * @param the {@code Map}'s value type + * @param k1 the mapping's key + * @param v1 the mapping's value + * @return a {@code Map} containing the specified mapping + * @throws NullPointerException if the key or the value is {@code null} + * + * @since 9 + */ + static Map of(K k1, V v1) { + return Collections.singletonMap(Objects.requireNonNull(k1), Objects.requireNonNull(v1)); + } + + /** + * Returns an immutable map containing two mappings. + * See Immutable Map Static Factory Methods for details. + * + * @param the {@code Map}'s key type + * @param the {@code Map}'s value type + * @param k1 the first mapping's key + * @param v1 the first mapping's value + * @param k2 the second mapping's key + * @param v2 the second mapping's value + * @return a {@code Map} containing the specified mappings + * @throws IllegalArgumentException if the keys are duplicates + * @throws NullPointerException if any key or value is {@code null} + * + * @since 9 + */ + static Map of(K k1, V v1, K k2, V v2) { + Map map = new HashMap<>(3); // specify number of buckets to avoid resizing + map.put(Objects.requireNonNull(k1), Objects.requireNonNull(v1)); + map.put(Objects.requireNonNull(k2), Objects.requireNonNull(v2)); + if (map.size() != 2) { + throw new IllegalArgumentException("duplicate keys"); + } + return Collections.unmodifiableMap(map); + } + + /** + * Returns an immutable map containing three mappings. + * See Immutable Map Static Factory Methods for details. + * + * @param the {@code Map}'s key type + * @param the {@code Map}'s value type + * @param k1 the first mapping's key + * @param v1 the first mapping's value + * @param k2 the second mapping's key + * @param v2 the second mapping's value + * @param k3 the third mapping's key + * @param v3 the third mapping's value + * @return a {@code Map} containing the specified mappings + * @throws IllegalArgumentException if there are any duplicate keys + * @throws NullPointerException if any key or value is {@code null} + * + * @since 9 + */ + static Map of(K k1, V v1, K k2, V v2, K k3, V v3) { + Map map = new HashMap<>(5); // specify number of buckets to avoid resizing + map.put(Objects.requireNonNull(k1), Objects.requireNonNull(v1)); + map.put(Objects.requireNonNull(k2), Objects.requireNonNull(v2)); + map.put(Objects.requireNonNull(k3), Objects.requireNonNull(v3)); + if (map.size() != 3) { + throw new IllegalArgumentException("duplicate keys"); + } + return Collections.unmodifiableMap(map); + } + + /** + * Returns an immutable map containing four mappings. + * See Immutable Map Static Factory Methods for details. + * + * @param the {@code Map}'s key type + * @param the {@code Map}'s value type + * @param k1 the first mapping's key + * @param v1 the first mapping's value + * @param k2 the second mapping's key + * @param v2 the second mapping's value + * @param k3 the third mapping's key + * @param v3 the third mapping's value + * @param k4 the fourth mapping's key + * @param v4 the fourth mapping's value + * @return a {@code Map} containing the specified mappings + * @throws IllegalArgumentException if there are any duplicate keys + * @throws NullPointerException if any key or value is {@code null} + * + * @since 9 + */ + static Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { + Map map = new HashMap<>(6); // specify number of buckets to avoid resizing + map.put(Objects.requireNonNull(k1), Objects.requireNonNull(v1)); + map.put(Objects.requireNonNull(k2), Objects.requireNonNull(v2)); + map.put(Objects.requireNonNull(k3), Objects.requireNonNull(v3)); + map.put(Objects.requireNonNull(k4), Objects.requireNonNull(v4)); + if (map.size() != 4) { + throw new IllegalArgumentException("duplicate keys"); + } + return Collections.unmodifiableMap(map); + } + + /** + * Returns an immutable map containing five mappings. + * See Immutable Map Static Factory Methods for details. + * + * @param the {@code Map}'s key type + * @param the {@code Map}'s value type + * @param k1 the first mapping's key + * @param v1 the first mapping's value + * @param k2 the second mapping's key + * @param v2 the second mapping's value + * @param k3 the third mapping's key + * @param v3 the third mapping's value + * @param k4 the fourth mapping's key + * @param v4 the fourth mapping's value + * @param k5 the fifth mapping's key + * @param v5 the fifth mapping's value + * @return a {@code Map} containing the specified mappings + * @throws IllegalArgumentException if there are any duplicate keys + * @throws NullPointerException if any key or value is {@code null} + * + * @since 9 + */ + static Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { + Map map = new HashMap<>(7); // specify number of buckets to avoid resizing + map.put(Objects.requireNonNull(k1), Objects.requireNonNull(v1)); + map.put(Objects.requireNonNull(k2), Objects.requireNonNull(v2)); + map.put(Objects.requireNonNull(k3), Objects.requireNonNull(v3)); + map.put(Objects.requireNonNull(k4), Objects.requireNonNull(v4)); + map.put(Objects.requireNonNull(k5), Objects.requireNonNull(v5)); + if (map.size() != 5) { + throw new IllegalArgumentException("duplicate keys"); + } + return Collections.unmodifiableMap(map); + } + + /** + * Returns an immutable map containing six mappings. + * See Immutable Map Static Factory Methods for details. + * + * @param the {@code Map}'s key type + * @param the {@code Map}'s value type + * @param k1 the first mapping's key + * @param v1 the first mapping's value + * @param k2 the second mapping's key + * @param v2 the second mapping's value + * @param k3 the third mapping's key + * @param v3 the third mapping's value + * @param k4 the fourth mapping's key + * @param v4 the fourth mapping's value + * @param k5 the fifth mapping's key + * @param v5 the fifth mapping's value + * @param k6 the sixth mapping's key + * @param v6 the sixth mapping's value + * @return a {@code Map} containing the specified mappings + * @throws IllegalArgumentException if there are any duplicate keys + * @throws NullPointerException if any key or value is {@code null} + * + * @since 9 + */ + static Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, + K k6, V v6) { + Map map = new HashMap<>(9); // specify number of buckets to avoid resizing + map.put(Objects.requireNonNull(k1), Objects.requireNonNull(v1)); + map.put(Objects.requireNonNull(k2), Objects.requireNonNull(v2)); + map.put(Objects.requireNonNull(k3), Objects.requireNonNull(v3)); + map.put(Objects.requireNonNull(k4), Objects.requireNonNull(v4)); + map.put(Objects.requireNonNull(k5), Objects.requireNonNull(v5)); + map.put(Objects.requireNonNull(k6), Objects.requireNonNull(v6)); + if (map.size() != 6) { + throw new IllegalArgumentException("duplicate keys"); + } + return Collections.unmodifiableMap(map); + } + + /** + * Returns an immutable map containing seven mappings. + * See Immutable Map Static Factory Methods for details. + * + * @param the {@code Map}'s key type + * @param the {@code Map}'s value type + * @param k1 the first mapping's key + * @param v1 the first mapping's value + * @param k2 the second mapping's key + * @param v2 the second mapping's value + * @param k3 the third mapping's key + * @param v3 the third mapping's value + * @param k4 the fourth mapping's key + * @param v4 the fourth mapping's value + * @param k5 the fifth mapping's key + * @param v5 the fifth mapping's value + * @param k6 the sixth mapping's key + * @param v6 the sixth mapping's value + * @param k7 the seventh mapping's key + * @param v7 the seventh mapping's value + * @return a {@code Map} containing the specified mappings + * @throws IllegalArgumentException if there are any duplicate keys + * @throws NullPointerException if any key or value is {@code null} + * + * @since 9 + */ + static Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, + K k6, V v6, K k7, V v7) { + Map map = new HashMap<>(10); // specify number of buckets to avoid resizing + map.put(Objects.requireNonNull(k1), Objects.requireNonNull(v1)); + map.put(Objects.requireNonNull(k2), Objects.requireNonNull(v2)); + map.put(Objects.requireNonNull(k3), Objects.requireNonNull(v3)); + map.put(Objects.requireNonNull(k4), Objects.requireNonNull(v4)); + map.put(Objects.requireNonNull(k5), Objects.requireNonNull(v5)); + map.put(Objects.requireNonNull(k6), Objects.requireNonNull(v6)); + map.put(Objects.requireNonNull(k7), Objects.requireNonNull(v7)); + if (map.size() != 7) { + throw new IllegalArgumentException("duplicate keys"); + } + return Collections.unmodifiableMap(map); + } + + /** + * Returns an immutable map containing eight mappings. + * See Immutable Map Static Factory Methods for details. + * + * @param the {@code Map}'s key type + * @param the {@code Map}'s value type + * @param k1 the first mapping's key + * @param v1 the first mapping's value + * @param k2 the second mapping's key + * @param v2 the second mapping's value + * @param k3 the third mapping's key + * @param v3 the third mapping's value + * @param k4 the fourth mapping's key + * @param v4 the fourth mapping's value + * @param k5 the fifth mapping's key + * @param v5 the fifth mapping's value + * @param k6 the sixth mapping's key + * @param v6 the sixth mapping's value + * @param k7 the seventh mapping's key + * @param v7 the seventh mapping's value + * @param k8 the eighth mapping's key + * @param v8 the eighth mapping's value + * @return a {@code Map} containing the specified mappings + * @throws IllegalArgumentException if there are any duplicate keys + * @throws NullPointerException if any key or value is {@code null} + * + * @since 9 + */ + static Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, + K k6, V v6, K k7, V v7, K k8, V v8) { + Map map = new HashMap<>(11); // specify number of buckets to avoid resizing + map.put(Objects.requireNonNull(k1), Objects.requireNonNull(v1)); + map.put(Objects.requireNonNull(k2), Objects.requireNonNull(v2)); + map.put(Objects.requireNonNull(k3), Objects.requireNonNull(v3)); + map.put(Objects.requireNonNull(k4), Objects.requireNonNull(v4)); + map.put(Objects.requireNonNull(k5), Objects.requireNonNull(v5)); + map.put(Objects.requireNonNull(k6), Objects.requireNonNull(v6)); + map.put(Objects.requireNonNull(k7), Objects.requireNonNull(v7)); + map.put(Objects.requireNonNull(k8), Objects.requireNonNull(v8)); + if (map.size() != 8) { + throw new IllegalArgumentException("duplicate keys"); + } + return Collections.unmodifiableMap(map); + } + + /** + * Returns an immutable map containing nine mappings. + * See Immutable Map Static Factory Methods for details. + * + * @param the {@code Map}'s key type + * @param the {@code Map}'s value type + * @param k1 the first mapping's key + * @param v1 the first mapping's value + * @param k2 the second mapping's key + * @param v2 the second mapping's value + * @param k3 the third mapping's key + * @param v3 the third mapping's value + * @param k4 the fourth mapping's key + * @param v4 the fourth mapping's value + * @param k5 the fifth mapping's key + * @param v5 the fifth mapping's value + * @param k6 the sixth mapping's key + * @param v6 the sixth mapping's value + * @param k7 the seventh mapping's key + * @param v7 the seventh mapping's value + * @param k8 the eighth mapping's key + * @param v8 the eighth mapping's value + * @param k9 the ninth mapping's key + * @param v9 the ninth mapping's value + * @return a {@code Map} containing the specified mappings + * @throws IllegalArgumentException if there are any duplicate keys + * @throws NullPointerException if any key or value is {@code null} + * + * @since 9 + */ + static Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, + K k6, V v6, K k7, V v7, K k8, V v8, K k9, V v9) { + Map map = new HashMap<>(13); // specify number of buckets to avoid resizing + map.put(Objects.requireNonNull(k1), Objects.requireNonNull(v1)); + map.put(Objects.requireNonNull(k2), Objects.requireNonNull(v2)); + map.put(Objects.requireNonNull(k3), Objects.requireNonNull(v3)); + map.put(Objects.requireNonNull(k4), Objects.requireNonNull(v4)); + map.put(Objects.requireNonNull(k5), Objects.requireNonNull(v5)); + map.put(Objects.requireNonNull(k6), Objects.requireNonNull(v6)); + map.put(Objects.requireNonNull(k7), Objects.requireNonNull(v7)); + map.put(Objects.requireNonNull(k8), Objects.requireNonNull(v8)); + map.put(Objects.requireNonNull(k9), Objects.requireNonNull(v9)); + if (map.size() != 9) { + throw new IllegalArgumentException("duplicate keys"); + } + return Collections.unmodifiableMap(map); + } + + /** + * Returns an immutable map containing ten mappings. + * See Immutable Map Static Factory Methods for details. + * + * @param the {@code Map}'s key type + * @param the {@code Map}'s value type + * @param k1 the first mapping's key + * @param v1 the first mapping's value + * @param k2 the second mapping's key + * @param v2 the second mapping's value + * @param k3 the third mapping's key + * @param v3 the third mapping's value + * @param k4 the fourth mapping's key + * @param v4 the fourth mapping's value + * @param k5 the fifth mapping's key + * @param v5 the fifth mapping's value + * @param k6 the sixth mapping's key + * @param v6 the sixth mapping's value + * @param k7 the seventh mapping's key + * @param v7 the seventh mapping's value + * @param k8 the eighth mapping's key + * @param v8 the eighth mapping's value + * @param k9 the ninth mapping's key + * @param v9 the ninth mapping's value + * @param k10 the tenth mapping's key + * @param v10 the tenth mapping's value + * @return a {@code Map} containing the specified mappings + * @throws IllegalArgumentException if there are any duplicate keys + * @throws NullPointerException if any key or value is {@code null} + * + * @since 9 + */ + static Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, + K k6, V v6, K k7, V v7, K k8, V v8, K k9, V v9, K k10, V v10) { + Map map = new HashMap<>(14); // specify number of buckets to avoid resizing + map.put(Objects.requireNonNull(k1), Objects.requireNonNull(v1)); + map.put(Objects.requireNonNull(k2), Objects.requireNonNull(v2)); + map.put(Objects.requireNonNull(k3), Objects.requireNonNull(v3)); + map.put(Objects.requireNonNull(k4), Objects.requireNonNull(v4)); + map.put(Objects.requireNonNull(k5), Objects.requireNonNull(v5)); + map.put(Objects.requireNonNull(k6), Objects.requireNonNull(v6)); + map.put(Objects.requireNonNull(k7), Objects.requireNonNull(v7)); + map.put(Objects.requireNonNull(k8), Objects.requireNonNull(v8)); + map.put(Objects.requireNonNull(k9), Objects.requireNonNull(v9)); + map.put(Objects.requireNonNull(k10), Objects.requireNonNull(v10)); + if (map.size() != 10) { + throw new IllegalArgumentException("duplicate keys"); + } + return Collections.unmodifiableMap(map); + } + + /** + * Returns an immutable map containing keys and values extracted from the given entries. + * The entries themselves are not stored in the map. + * See Immutable Map Static Factory Methods for details. + * + * @apiNote + * It is convenient to create the map entries using the {@link Map#entry Map.entry()} method. + * For example, + * + *

{@code
+     *     import static java.util.Map.entry;
+     *
+     *     Map map = Map.ofEntries(
+     *         entry(1, "a"),
+     *         entry(2, "b"),
+     *         entry(3, "c"),
+     *         ...
+     *         entry(26, "z"));
+     * }
+ * + * @param the {@code Map}'s key type + * @param the {@code Map}'s value type + * @param entries {@code Map.Entry}s containing the keys and values from which the map is populated + * @return a {@code Map} containing the specified mappings + * @throws IllegalArgumentException if there are any duplicate keys + * @throws NullPointerException if any entry, key, or value is {@code null}, or if + * the {@code entries} array is {@code null} + * + * @see Map#entry Map.entry() + * @since 9 + */ + @SafeVarargs + @SuppressWarnings("varargs") + static Map ofEntries(Entry... entries) { + Map map = new HashMap<>(entries.length * 4 / 3 + 1); // throws NPE if entries is null + for (Entry e : entries) { + // next line throws NPE if e is null + map.put(Objects.requireNonNull(e.getKey()), Objects.requireNonNull(e.getValue())); + } + if (map.size() != entries.length) { + throw new IllegalArgumentException("duplicate keys"); + } + return Collections.unmodifiableMap(map); + } + + /** + * Returns an immutable {@link Entry} containing the given key and value. + * These entries are suitable for populating {@code Map} instances using the + * {@link Map#ofEntries Map.ofEntries()} method. + * The {@code Entry} instances created by this method have the following characteristics: + * + *
    + *
  • They disallow {@code null} keys and values. Attempts to create them using a {@code null} + * key or value result in {@code NullPointerException}. + *
  • They are immutable. Calls to {@link Entry#setValue Entry.setValue()} + * on a returned {@code Entry} result in {@code UnsupportedOperationException}. + *
  • They are not serializable. + *
  • They are value-based. + * Callers should make no assumptions about the identity of the returned instances. + * This method is free to create new instances or reuse existing ones. Therefore, + * identity-sensitive operations on these instances (reference equality ({@code ==}), + * identity hash code, and synchronization) are unreliable and should be avoided. + *
+ * + * @apiNote + * For a serializable {@code Entry}, see {@link AbstractMap.SimpleEntry} or + * {@link AbstractMap.SimpleImmutableEntry}. + * + * @param the key's type + * @param the value's type + * @param k the key + * @param v the value + * @return an {@code Entry} containing the specified key and value + * @throws NullPointerException if the key or value is {@code null} + * + * @see Map#ofEntries Map.ofEntries() + * @since 9 + */ + static Entry entry(K k, V v) { + // KeyValueHolder checks for nulls + return new KeyValueHolder<>(k, v); + } } diff --git a/jdk/src/java.base/share/classes/java/util/Set.java b/jdk/src/java.base/share/classes/java/util/Set.java index 93d008c20f1..b0db7fc55bd 100644 --- a/jdk/src/java.base/share/classes/java/util/Set.java +++ b/jdk/src/java.base/share/classes/java/util/Set.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -63,6 +63,29 @@ * Such exceptions are marked as "optional" in the specification for this * interface. * + *

Immutable Set Static Factory Methods

+ *

The {@link Set#of(Object...) Set.of()} static factory methods + * provide a convenient way to create immutable sets. The {@code Set} + * instances created by these methods have the following characteristics: + * + *

    + *
  • They are structurally immutable. Elements cannot be added or + * removed. Attempts to do so result in {@code UnsupportedOperationException}. + * However, if the contained elements are themselves mutable, this may cause the + * Set to behave inconsistently or its contents to appear to change. + *
  • They disallow {@code null} elements. Attempts to create them with + * {@code null} elements result in {@code NullPointerException}. + *
  • They are serializable if all elements are serializable. + *
  • They reject duplicate elements at creation time. Duplicate elements + * passed to a static factory method result in {@code IllegalArgumentException}. + *
  • The iteration order of set elements is unspecified and is subject to change. + *
  • They are value-based. + * Callers should make no assumptions about the identity of the returned instances. + * Factories are free to create new instances or reuse existing ones. Therefore, + * identity-sensitive operations on these instances (reference equality ({@code ==}), + * identity hash code, and synchronization) are unreliable and should be avoided. + *
+ * *

This interface is a member of the * * Java Collections Framework. @@ -410,4 +433,341 @@ public interface Set extends Collection { default Spliterator spliterator() { return Spliterators.spliterator(this, Spliterator.DISTINCT); } + + /** + * Returns an immutable set containing zero elements. + * See Immutable Set Static Factory Methods for details. + * + * @param the {@code Set}'s element type + * @return an empty {@code Set} + * + * @since 9 + */ + static Set of() { + return Collections.emptySet(); + } + + /** + * Returns an immutable set containing one element. + * See Immutable Set Static Factory Methods for details. + * + * @param the {@code Set}'s element type + * @param e1 the single element + * @return a {@code Set} containing the specified element + * @throws NullPointerException if the element is {@code null} + * + * @since 9 + */ + static Set of(E e1) { + return Collections.singleton(Objects.requireNonNull(e1)); + } + + /** + * Returns an immutable set containing two elements. + * See Immutable Set Static Factory Methods for details. + * + * @param the {@code Set}'s element type + * @param e1 the first element + * @param e2 the second element + * @return a {@code Set} containing the specified elements + * @throws IllegalArgumentException if the elements are duplicates + * @throws NullPointerException if an element is {@code null} + * + * @since 9 + */ + static Set of(E e1, E e2) { + Set set = new HashSet<>(Arrays.asList(Objects.requireNonNull(e1), + Objects.requireNonNull(e2))); + if (set.size() != 2) { + throw new IllegalArgumentException("duplicate elements"); + } + return Collections.unmodifiableSet(set); + } + + /** + * Returns an immutable set containing three elements. + * See Immutable Set Static Factory Methods for details. + * + * @param the {@code Set}'s element type + * @param e1 the first element + * @param e2 the second element + * @param e3 the third element + * @return a {@code Set} containing the specified elements + * @throws IllegalArgumentException if there are any duplicate elements + * @throws NullPointerException if an element is {@code null} + * + * @since 9 + */ + static Set of(E e1, E e2, E e3) { + Set set = new HashSet<>(Arrays.asList(Objects.requireNonNull(e1), + Objects.requireNonNull(e2), + Objects.requireNonNull(e3))); + if (set.size() != 3) { + throw new IllegalArgumentException("duplicate elements"); + } + return Collections.unmodifiableSet(set); + } + + /** + * Returns an immutable set containing four elements. + * See Immutable Set Static Factory Methods for details. + * + * @param the {@code Set}'s element type + * @param e1 the first element + * @param e2 the second element + * @param e3 the third element + * @param e4 the fourth element + * @return a {@code Set} containing the specified elements + * @throws IllegalArgumentException if there are any duplicate elements + * @throws NullPointerException if an element is {@code null} + * + * @since 9 + */ + static Set of(E e1, E e2, E e3, E e4) { + Set set = new HashSet<>(Arrays.asList(Objects.requireNonNull(e1), + Objects.requireNonNull(e2), + Objects.requireNonNull(e3), + Objects.requireNonNull(e4))); + if (set.size() != 4) { + throw new IllegalArgumentException("duplicate elements"); + } + return Collections.unmodifiableSet(set); + } + + /** + * Returns an immutable set containing five elements. + * See Immutable Set Static Factory Methods for details. + * + * @param the {@code Set}'s element type + * @param e1 the first element + * @param e2 the second element + * @param e3 the third element + * @param e4 the fourth element + * @param e5 the fifth element + * @return a {@code Set} containing the specified elements + * @throws IllegalArgumentException if there are any duplicate elements + * @throws NullPointerException if an element is {@code null} + * + * @since 9 + */ + static Set of(E e1, E e2, E e3, E e4, E e5) { + Set set = new HashSet<>(Arrays.asList(Objects.requireNonNull(e1), + Objects.requireNonNull(e2), + Objects.requireNonNull(e3), + Objects.requireNonNull(e4), + Objects.requireNonNull(e5))); + if (set.size() != 5) { + throw new IllegalArgumentException("duplicate elements"); + } + return Collections.unmodifiableSet(set); + } + + /** + * Returns an immutable set containing six elements. + * See Immutable Set Static Factory Methods for details. + * + * @param the {@code Set}'s element type + * @param e1 the first element + * @param e2 the second element + * @param e3 the third element + * @param e4 the fourth element + * @param e5 the fifth element + * @param e6 the sixth element + * @return a {@code Set} containing the specified elements + * @throws IllegalArgumentException if there are any duplicate elements + * @throws NullPointerException if an element is {@code null} + * + * @since 9 + */ + static Set of(E e1, E e2, E e3, E e4, E e5, E e6) { + Set set = new HashSet<>(Arrays.asList(Objects.requireNonNull(e1), + Objects.requireNonNull(e2), + Objects.requireNonNull(e3), + Objects.requireNonNull(e4), + Objects.requireNonNull(e5), + Objects.requireNonNull(e6))); + if (set.size() != 6) { + throw new IllegalArgumentException("duplicate elements"); + } + return Collections.unmodifiableSet(set); + } + + /** + * Returns an immutable set containing seven elements. + * See Immutable Set Static Factory Methods for details. + * + * @param the {@code Set}'s element type + * @param e1 the first element + * @param e2 the second element + * @param e3 the third element + * @param e4 the fourth element + * @param e5 the fifth element + * @param e6 the sixth element + * @param e7 the seventh element + * @return a {@code Set} containing the specified elements + * @throws IllegalArgumentException if there are any duplicate elements + * @throws NullPointerException if an element is {@code null} + * + * @since 9 + */ + static Set of(E e1, E e2, E e3, E e4, E e5, E e6, E e7) { + Set set = new HashSet<>(Arrays.asList(Objects.requireNonNull(e1), + Objects.requireNonNull(e2), + Objects.requireNonNull(e3), + Objects.requireNonNull(e4), + Objects.requireNonNull(e5), + Objects.requireNonNull(e6), + Objects.requireNonNull(e7))); + if (set.size() != 7) { + throw new IllegalArgumentException("duplicate elements"); + } + return Collections.unmodifiableSet(set); + } + + /** + * Returns an immutable set containing eight elements. + * See Immutable Set Static Factory Methods for details. + * + * @param the {@code Set}'s element type + * @param e1 the first element + * @param e2 the second element + * @param e3 the third element + * @param e4 the fourth element + * @param e5 the fifth element + * @param e6 the sixth element + * @param e7 the seventh element + * @param e8 the eighth element + * @return a {@code Set} containing the specified elements + * @throws IllegalArgumentException if there are any duplicate elements + * @throws NullPointerException if an element is {@code null} + * + * @since 9 + */ + static Set of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8) { + Set set = new HashSet<>(Arrays.asList(Objects.requireNonNull(e1), + Objects.requireNonNull(e2), + Objects.requireNonNull(e3), + Objects.requireNonNull(e4), + Objects.requireNonNull(e5), + Objects.requireNonNull(e6), + Objects.requireNonNull(e7), + Objects.requireNonNull(e8))); + if (set.size() != 8) { + throw new IllegalArgumentException("duplicate elements"); + } + return Collections.unmodifiableSet(set); + } + + /** + * Returns an immutable set containing nine elements. + * See Immutable Set Static Factory Methods for details. + * + * @param the {@code Set}'s element type + * @param e1 the first element + * @param e2 the second element + * @param e3 the third element + * @param e4 the fourth element + * @param e5 the fifth element + * @param e6 the sixth element + * @param e7 the seventh element + * @param e8 the eighth element + * @param e9 the ninth element + * @return a {@code Set} containing the specified elements + * @throws IllegalArgumentException if there are any duplicate elements + * @throws NullPointerException if an element is {@code null} + * + * @since 9 + */ + static Set of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9) { + Set set = new HashSet<>(Arrays.asList(Objects.requireNonNull(e1), + Objects.requireNonNull(e2), + Objects.requireNonNull(e3), + Objects.requireNonNull(e4), + Objects.requireNonNull(e5), + Objects.requireNonNull(e6), + Objects.requireNonNull(e7), + Objects.requireNonNull(e8), + Objects.requireNonNull(e9))); + if (set.size() != 9) { + throw new IllegalArgumentException("duplicate elements"); + } + return Collections.unmodifiableSet(set); + } + + /** + * Returns an immutable set containing ten elements. + * See Immutable Set Static Factory Methods for details. + * + * @param the {@code Set}'s element type + * @param e1 the first element + * @param e2 the second element + * @param e3 the third element + * @param e4 the fourth element + * @param e5 the fifth element + * @param e6 the sixth element + * @param e7 the seventh element + * @param e8 the eighth element + * @param e9 the ninth element + * @param e10 the tenth element + * @return a {@code Set} containing the specified elements + * @throws IllegalArgumentException if there are any duplicate elements + * @throws NullPointerException if an element is {@code null} + * + * @since 9 + */ + static Set of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10) { + Set set = new HashSet<>(Arrays.asList(Objects.requireNonNull(e1), + Objects.requireNonNull(e2), + Objects.requireNonNull(e3), + Objects.requireNonNull(e4), + Objects.requireNonNull(e5), + Objects.requireNonNull(e6), + Objects.requireNonNull(e7), + Objects.requireNonNull(e8), + Objects.requireNonNull(e9), + Objects.requireNonNull(e10))); + if (set.size() != 10) { + throw new IllegalArgumentException("duplicate elements"); + } + return Collections.unmodifiableSet(set); + } + + /** + * Returns an immutable set containing an arbitrary number of elements. + * See Immutable Set Static Factory Methods for details. + * + * @apiNote + * This method also accepts a single array as an argument. The element type of + * the resulting set will be the component type of the array, and the size of + * the set will be equal to the length of the array. To create a set with + * a single element that is an array, do the following: + * + *

{@code
+     *     String[] array = ... ;
+     *     Set list = Set.of(array);
+     * }
+ * + * This will cause the {@link Set#of(Object) Set.of(E)} method + * to be invoked instead. + * + * @param the {@code Set}'s element type + * @param elements the elements to be contained in the set + * @return a {@code Set} containing the specified elements + * @throws IllegalArgumentException if there are any duplicate elements + * @throws NullPointerException if an element is {@code null} or if the array is {@code null} + * + * @since 9 + */ + @SafeVarargs + static Set of(E... elements) { + for (E e : elements) { // throws NPE if es is null + Objects.requireNonNull(e); + } + @SuppressWarnings("varargs") + Set set = new HashSet<>(Arrays.asList(elements)); + if (set.size() != elements.length) { + throw new IllegalArgumentException("duplicate elements"); + } + return Collections.unmodifiableSet(set); + } } diff --git a/jdk/test/java/util/Collection/MOAT.java b/jdk/test/java/util/Collection/MOAT.java index 6a1165e9ba6..fe17c5034a3 100644 --- a/jdk/test/java/util/Collection/MOAT.java +++ b/jdk/test/java/util/Collection/MOAT.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -58,6 +58,21 @@ import java.lang.reflect.*; public class MOAT { + // Collections under test must not be initialized to contain this value, + // and maps under test must not contain this value as a key. + // It's used as a sentinel for absent-element testing. + static final int ABSENT_VALUE = 778347983; + + static final Integer[] integerArray; + static { + Integer[] ia = new Integer[20]; + // fill with 1..20 inclusive + for (int i = 0; i < ia.length; i++) { + ia[i] = i + 1; + } + integerArray = ia; + } + public static void realMain(String[] args) { testCollection(new NewAbstractCollection()); @@ -178,6 +193,70 @@ public static void realMain(String[] args) { equal(singletonMap.size(), 1); testMap(singletonMap); testImmutableMap(singletonMap); + + // Immutable List + testEmptyList(List.of()); + for (List list : Arrays.asList( + List.of(), + List.of(1), + List.of(1, 2), + List.of(1, 2, 3), + List.of(1, 2, 3, 4), + List.of(1, 2, 3, 4, 5), + List.of(1, 2, 3, 4, 5, 6), + List.of(1, 2, 3, 4, 5, 6, 7), + List.of(1, 2, 3, 4, 5, 6, 7, 8), + List.of(1, 2, 3, 4, 5, 6, 7, 8, 9), + List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), + List.of(integerArray))) { + testCollection(list); + testImmutableList(list); + } + + // Immutable Set + testEmptySet(Set.of()); + for (Set set : Arrays.asList( + Set.of(), + Set.of(1), + Set.of(1, 2), + Set.of(1, 2, 3), + Set.of(1, 2, 3, 4), + Set.of(1, 2, 3, 4, 5), + Set.of(1, 2, 3, 4, 5, 6), + Set.of(1, 2, 3, 4, 5, 6, 7), + Set.of(1, 2, 3, 4, 5, 6, 7, 8), + Set.of(1, 2, 3, 4, 5, 6, 7, 8, 9), + Set.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), + Set.of(integerArray))) { + testCollection(set); + testImmutableSet(set); + } + + // Immutable Map + + @SuppressWarnings("unchecked") + Map.Entry[] ea = (Map.Entry[])new Map.Entry[20]; + for (int i = 0; i < ea.length; i++) { + ea[i] = Map.entry(i+1, i+101); + } + + testEmptyMap(Map.of()); + for (Map map : Arrays.asList( + Map.of(), + Map.of(1, 101), + Map.of(1, 101, 2, 202), + Map.of(1, 101, 2, 202, 3, 303), + Map.of(1, 101, 2, 202, 3, 303, 4, 404), + Map.of(1, 101, 2, 202, 3, 303, 4, 404, 5, 505), + Map.of(1, 101, 2, 202, 3, 303, 4, 404, 5, 505, 6, 606), + Map.of(1, 101, 2, 202, 3, 303, 4, 404, 5, 505, 6, 606, 7, 707), + Map.of(1, 101, 2, 202, 3, 303, 4, 404, 5, 505, 6, 606, 7, 707, 8, 808), + Map.of(1, 101, 2, 202, 3, 303, 4, 404, 5, 505, 6, 606, 7, 707, 8, 808, 9, 909), + Map.of(1, 101, 2, 202, 3, 303, 4, 404, 5, 505, 6, 606, 7, 707, 8, 808, 9, 909, 10, 1010), + Map.ofEntries(ea))) { + testMap(map); + testImmutableMap(map); + } } private static void checkContainsSelf(Collection c) { @@ -190,6 +269,17 @@ private static void checkContainsEmpty(Collection c) { check(c.containsAll(new ArrayList())); } + private static void checkUnique(Set s) { + for (Integer i : s) { + int count = 0; + for (Integer j : s) { + if (Objects.equals(i,j)) + ++count; + } + check(count == 1); + } + } + private static void testEmptyCollection(Collection c) { check(c.isEmpty()); equal(c.size(), 0); @@ -330,19 +420,19 @@ private static void oneElement(Collection c) { } private static boolean supportsAdd(Collection c) { - try { check(c.add(778347983)); } + try { check(c.add(ABSENT_VALUE)); } catch (UnsupportedOperationException t) { return false; } catch (Throwable t) { unexpected(t); } try { - check(c.contains(778347983)); - check(c.remove(778347983)); + check(c.contains(ABSENT_VALUE)); + check(c.remove(ABSENT_VALUE)); } catch (Throwable t) { unexpected(t); } return true; } private static boolean supportsRemove(Collection c) { - try { check(! c.remove(19134032)); } + try { check(! c.remove(ABSENT_VALUE)); } catch (UnsupportedOperationException t) { return false; } catch (Throwable t) { unexpected(t); } return true; @@ -359,6 +449,7 @@ private static void checkFunctionalInvariants(Collection c) { checkContainsSelf(c); checkContainsEmpty(c); check(c.size() != 0 ^ c.isEmpty()); + check(! c.contains(ABSENT_VALUE)); { int size = 0; @@ -366,6 +457,10 @@ private static void checkFunctionalInvariants(Collection c) { check(c.size() == size); } + if (c instanceof Set) { + checkUnique((Set)c); + } + check(c.toArray().length == c.size()); check(c.toArray().getClass() == Object[].class || @@ -861,6 +956,20 @@ private static void checkFunctionalInvariants(Map m) { checkFunctionalInvariants(m.keySet()); checkFunctionalInvariants(m.values()); check(m.size() != 0 ^ m.isEmpty()); + check(! m.containsKey(ABSENT_VALUE)); + + if (m instanceof Serializable) { + //System.out.printf("Serializing %s%n", m.getClass().getName()); + try { + Object clone = serialClone(m); + equal(m instanceof Serializable, + clone instanceof Serializable); + equal(m, clone); + } catch (Error xxx) { + if (! (xxx.getCause() instanceof NotSerializableException)) + throw xxx; + } + } } private static void testMap(Map m) { @@ -910,13 +1019,13 @@ private static boolean supportsPut(Map m) { // We're asking for .equals(...) semantics if (m instanceof IdentityHashMap) return false; - try { check(m.put(778347983,12735) == null); } + try { check(m.put(ABSENT_VALUE,12735) == null); } catch (UnsupportedOperationException t) { return false; } catch (Throwable t) { unexpected(t); } try { - check(m.containsKey(778347983)); - check(m.remove(778347983) != null); + check(m.containsKey(ABSENT_VALUE)); + check(m.remove(ABSENT_VALUE) != null); } catch (Throwable t) { unexpected(t); } return true; } diff --git a/jdk/test/java/util/Collection/SetFactories.java b/jdk/test/java/util/Collection/SetFactories.java new file mode 100644 index 00000000000..aabeaa344a7 --- /dev/null +++ b/jdk/test/java/util/Collection/SetFactories.java @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import static org.testng.Assert.assertEquals; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +/* + * @test + * @bug 8048330 + * @summary Test convenience static factory methods on Set. + * @run testng SetFactories + */ + + +public class SetFactories { + + static final int NUM_STRINGS = 20; // should be larger than the largest fixed-arg overload + static final String[] stringArray; + static { + String[] sa = new String[NUM_STRINGS]; + for (int i = 0; i < NUM_STRINGS; i++) { + sa[i] = String.valueOf((char)('a' + i)); + } + stringArray = sa; + } + + static Object[] a(Set act, Set exp) { + return new Object[] { act, exp }; + } + + static Set hashSetOf(String... args) { + return new HashSet<>(Arrays.asList(args)); + } + + @DataProvider(name="empty") + public Iterator empty() { + return Collections.singletonList( + // actual, expected + a(Set.of(), Collections.emptySet()) + ).iterator(); + } + + @DataProvider(name="nonempty") + public Iterator nonempty() { + return Arrays.asList( + // actual, expected + a( Set.of("a"), + hashSetOf("a")), + a( Set.of("a", "b"), + hashSetOf("a", "b")), + a( Set.of("a", "b", "c"), + hashSetOf("a", "b", "c")), + a( Set.of("a", "b", "c", "d"), + hashSetOf("a", "b", "c", "d")), + a( Set.of("a", "b", "c", "d", "e"), + hashSetOf("a", "b", "c", "d", "e")), + a( Set.of("a", "b", "c", "d", "e", "f"), + hashSetOf("a", "b", "c", "d", "e", "f")), + a( Set.of("a", "b", "c", "d", "e", "f", "g"), + hashSetOf("a", "b", "c", "d", "e", "f", "g")), + a( Set.of("a", "b", "c", "d", "e", "f", "g", "h"), + hashSetOf("a", "b", "c", "d", "e", "f", "g", "h")), + a( Set.of("a", "b", "c", "d", "e", "f", "g", "h", "i"), + hashSetOf("a", "b", "c", "d", "e", "f", "g", "h", "i")), + a( Set.of("a", "b", "c", "d", "e", "f", "g", "h", "i", "j"), + hashSetOf("a", "b", "c", "d", "e", "f", "g", "h", "i", "j")), + a( Set.of(stringArray), + hashSetOf(stringArray)) + ).iterator(); + } + + @DataProvider(name="all") + public Iterator all() { + List all = new ArrayList<>(); + empty().forEachRemaining(all::add); + nonempty().forEachRemaining(all::add); + return all.iterator(); + } + + @Test(dataProvider="all", expectedExceptions=UnsupportedOperationException.class) + public void cannotAdd(Set act, Set exp) { + act.add("x"); + } + + @Test(dataProvider="nonempty", expectedExceptions=UnsupportedOperationException.class) + public void cannotRemove(Set act, Set exp) { + act.remove(act.iterator().next()); + } + + @Test(dataProvider="all") + public void contentsMatch(Set act, Set exp) { + assertEquals(act, exp); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupsDisallowed2() { + Set set = Set.of("a", "a"); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupsDisallowed3() { + Set set = Set.of("a", "b", "a"); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupsDisallowed4() { + Set set = Set.of("a", "b", "c", "a"); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupsDisallowed5() { + Set set = Set.of("a", "b", "c", "d", "a"); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupsDisallowed6() { + Set set = Set.of("a", "b", "c", "d", "e", "a"); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupsDisallowed7() { + Set set = Set.of("a", "b", "c", "d", "e", "f", "a"); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupsDisallowed8() { + Set set = Set.of("a", "b", "c", "d", "e", "f", "g", "a"); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupsDisallowed9() { + Set set = Set.of("a", "b", "c", "d", "e", "f", "g", "h", "a"); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupsDisallowed10() { + Set set = Set.of("a", "b", "c", "d", "e", "f", "g", "h", "i", "a"); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupsDisallowedN() { + String[] array = stringArray.clone(); + array[0] = array[1]; + Set set = Set.of(array); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed1() { + Set.of((String)null); // force one-arg overload + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed2a() { + Set.of("a", null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed2b() { + Set.of(null, "b"); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed3() { + Set.of("a", "b", null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed4() { + Set.of("a", "b", "c", null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed5() { + Set.of("a", "b", "c", "d", null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed6() { + Set.of("a", "b", "c", "d", "e", null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed7() { + Set.of("a", "b", "c", "d", "e", "f", null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed8() { + Set.of("a", "b", "c", "d", "e", "f", "g", null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed9() { + Set.of("a", "b", "c", "d", "e", "f", "g", "h", null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed10() { + Set.of("a", "b", "c", "d", "e", "f", "g", "h", "i", null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowedN() { + String[] array = stringArray.clone(); + array[0] = null; + Set.of(array); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullArrayDisallowed() { + Set.of((Object[])null); + } + + @Test(dataProvider="all") + public void serialEquality(Set act, Set exp) { + // assume that act.equals(exp) tested elsewhere + Set copy = serialClone(act); + assertEquals(act, copy); + assertEquals(copy, exp); + } + + @SuppressWarnings("unchecked") + static T serialClone(T obj) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(obj); + oos.close(); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + ObjectInputStream ois = new ObjectInputStream(bais); + return (T) ois.readObject(); + } catch (IOException | ClassNotFoundException e) { + throw new AssertionError(e); + } + } +} diff --git a/jdk/test/java/util/List/ListFactories.java b/jdk/test/java/util/List/ListFactories.java new file mode 100644 index 00000000000..e34ca660705 --- /dev/null +++ b/jdk/test/java/util/List/ListFactories.java @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static java.util.Arrays.asList; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +/* + * @test + * @bug 8048330 + * @summary Test convenience static factory methods on List. + * @run testng ListFactories + */ + +public class ListFactories { + + static final int NUM_STRINGS = 20; // should be larger than the largest fixed-arg overload + static final String[] stringArray; + static { + String[] sa = new String[NUM_STRINGS]; + for (int i = 0; i < NUM_STRINGS; i++) { + sa[i] = String.valueOf((char)('a' + i)); + } + stringArray = sa; + } + + // returns array of [actual, expected] + static Object[] a(List act, List exp) { + return new Object[] { act, exp }; + } + + @DataProvider(name="empty") + public Iterator empty() { + return Collections.singletonList( + a(List.of(), Collections.emptyList()) + ).iterator(); + } + + @DataProvider(name="nonempty") + public Iterator nonempty() { + return asList( + a(List.of("a"), + asList("a")), + a(List.of("a", "b"), + asList("a", "b")), + a(List.of("a", "b", "c"), + asList("a", "b", "c")), + a(List.of("a", "b", "c", "d"), + asList("a", "b", "c", "d")), + a(List.of("a", "b", "c", "d", "e"), + asList("a", "b", "c", "d", "e")), + a(List.of("a", "b", "c", "d", "e", "f"), + asList("a", "b", "c", "d", "e", "f")), + a(List.of("a", "b", "c", "d", "e", "f", "g"), + asList("a", "b", "c", "d", "e", "f", "g")), + a(List.of("a", "b", "c", "d", "e", "f", "g", "h"), + asList("a", "b", "c", "d", "e", "f", "g", "h")), + a(List.of("a", "b", "c", "d", "e", "f", "g", "h", "i"), + asList("a", "b", "c", "d", "e", "f", "g", "h", "i")), + a(List.of("a", "b", "c", "d", "e", "f", "g", "h", "i", "j"), + asList("a", "b", "c", "d", "e", "f", "g", "h", "i", "j")), + a(List.of(stringArray), + asList(stringArray)) + ).iterator(); + } + + @DataProvider(name="all") + public Iterator all() { + List all = new ArrayList<>(); + empty().forEachRemaining(all::add); + nonempty().forEachRemaining(all::add); + return all.iterator(); + } + + @Test(dataProvider="all", expectedExceptions=UnsupportedOperationException.class) + public void cannotAddLast(List act, List exp) { + act.add("x"); + } + + @Test(dataProvider="all", expectedExceptions=UnsupportedOperationException.class) + public void cannotAddFirst(List act, List exp) { + act.add(0, "x"); + } + + @Test(dataProvider="nonempty", expectedExceptions=UnsupportedOperationException.class) + public void cannotRemove(List act, List exp) { + act.remove(0); + } + + @Test(dataProvider="nonempty", expectedExceptions=UnsupportedOperationException.class) + public void cannotSet(List act, List exp) { + act.set(0, "x"); + } + + @Test(dataProvider="all") + public void contentsMatch(List act, List exp) { + assertEquals(act, exp); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed1() { + List.of((Object)null); // force one-arg overload + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed2a() { + List.of("a", null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed2b() { + List.of(null, "b"); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed3() { + List.of("a", "b", null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed4() { + List.of("a", "b", "c", null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed5() { + List.of("a", "b", "c", "d", null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed6() { + List.of("a", "b", "c", "d", "e", null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed7() { + List.of("a", "b", "c", "d", "e", "f", null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed8() { + List.of("a", "b", "c", "d", "e", "f", "g", null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed9() { + List.of("a", "b", "c", "d", "e", "f", "g", "h", null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowed10() { + List.of("a", "b", "c", "d", "e", "f", "g", "h", "i", null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullDisallowedN() { + String[] array = stringArray.clone(); + array[0] = null; + List.of(array); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullArrayDisallowed() { + List.of((Object[])null); + } + + @Test + public void ensureArrayCannotModifyList() { + String[] array = stringArray.clone(); + List list = List.of(array); + array[0] = "xyzzy"; + assertEquals(list, Arrays.asList(stringArray)); + } + + @Test(dataProvider="all") + public void serialEquality(List act, List exp) { + // assume that act.equals(exp) tested elsewhere + List copy = serialClone(act); + assertEquals(act, copy); + assertEquals(copy, exp); + } + + @SuppressWarnings("unchecked") + static T serialClone(T obj) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(obj); + oos.close(); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + ObjectInputStream ois = new ObjectInputStream(bais); + return (T) ois.readObject(); + } catch (IOException | ClassNotFoundException e) { + throw new AssertionError(e); + } + } +} diff --git a/jdk/test/java/util/Map/MapFactories.java b/jdk/test/java/util/Map/MapFactories.java new file mode 100644 index 00000000000..1bdb020680d --- /dev/null +++ b/jdk/test/java/util/Map/MapFactories.java @@ -0,0 +1,380 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +/* + * @test + * @bug 8048330 + * @summary Test convenience static factory methods on Map. + * @run testng MapFactories + */ + +public class MapFactories { + + static final int MAX_ENTRIES = 20; // should be larger than the largest fixed-arg overload + static String valueFor(int i) { + // the String literal below should be of length MAX_ENTRIES + return "abcdefghijklmnopqrst".substring(i, i+1); + } + + // for "expected" values + Map genMap(int n) { + Map result = new HashMap<>(); + for (int i = 0; i < n; i++) { + result.put(i, valueFor(i)); + } + return result; + } + + // for varargs Map.Entry methods + @SuppressWarnings("unchecked") + Map.Entry[] genEntries(int n) { + return IntStream.range(0, n) + .mapToObj(i -> Map.entry(i, valueFor(i))) + .toArray(Map.Entry[]::new); + } + + // returns array of [actual, expected] + static Object[] a(Map act, Map exp) { + return new Object[] { act, exp }; + } + + @DataProvider(name="empty") + public Iterator empty() { + return Collections.singletonList( + a(Map.of(), genMap(0)) + ).iterator(); + } + + @DataProvider(name="nonempty") + @SuppressWarnings("unchecked") + public Iterator nonempty() { + return Arrays.asList( + a(Map.of(0, "a"), genMap(1)), + a(Map.of(0, "a", 1, "b"), genMap(2)), + a(Map.of(0, "a", 1, "b", 2, "c"), genMap(3)), + a(Map.of(0, "a", 1, "b", 2, "c", 3, "d"), genMap(4)), + a(Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e"), genMap(5)), + a(Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", 5, "f"), genMap(6)), + a(Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", 5, "f", 6, "g"), genMap(7)), + a(Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", 5, "f", 6, "g", 7, "h"), genMap(8)), + a(Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", 5, "f", 6, "g", 7, "h", 8, "i"), genMap(9)), + a(Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", 5, "f", 6, "g", 7, "h", 8, "i", 9, "j"), genMap(10)), + a(Map.ofEntries(genEntries(MAX_ENTRIES)), genMap(MAX_ENTRIES)) + ).iterator(); + } + + @DataProvider(name="all") + public Iterator all() { + List all = new ArrayList<>(); + empty().forEachRemaining(all::add); + nonempty().forEachRemaining(all::add); + return all.iterator(); + } + + @Test(dataProvider="all", expectedExceptions=UnsupportedOperationException.class) + public void cannotPutNew(Map act, Map exp) { + act.put(-1, "xyzzy"); + } + + @Test(dataProvider="nonempty", expectedExceptions=UnsupportedOperationException.class) + public void cannotPutOld(Map act, Map exp) { + act.put(0, "a"); + } + + @Test(dataProvider="nonempty", expectedExceptions=UnsupportedOperationException.class) + public void cannotRemove(Map act, Map exp) { + act.remove(act.keySet().iterator().next()); + } + + @Test(dataProvider="all") + public void contentsMatch(Map act, Map exp) { + assertEquals(act, exp); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupKeysDisallowed2() { + Map map = Map.of(0, "a", 0, "b"); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupKeysDisallowed3() { + Map map = Map.of(0, "a", 1, "b", 0, "c"); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupKeysDisallowed4() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 0, "d"); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupKeysDisallowed5() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 0, "e"); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupKeysDisallowed6() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", + 0, "f"); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupKeysDisallowed7() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", + 5, "f", 0, "g"); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupKeysDisallowed8() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", + 5, "f", 6, "g", 0, "h"); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupKeysDisallowed9() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", + 5, "f", 6, "g", 7, "h", 0, "i"); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupKeysDisallowed10() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", + 5, "f", 6, "g", 7, "h", 8, "i", 0, "j"); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void dupKeysDisallowedN() { + Map.Entry[] entries = genEntries(MAX_ENTRIES); + entries[MAX_ENTRIES-1] = Map.entry(0, "xxx"); + Map map = Map.ofEntries(entries); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullKeyDisallowed1() { + Map map = Map.of(null, "a"); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullValueDisallowed1() { + Map map = Map.of(0, null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullKeyDisallowed2() { + Map map = Map.of(0, "a", null, "b"); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullValueDisallowed2() { + Map map = Map.of(0, "a", 1, null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullKeyDisallowed3() { + Map map = Map.of(0, "a", 1, "b", null, "c"); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullValueDisallowed3() { + Map map = Map.of(0, "a", 1, "b", 2, null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullKeyDisallowed4() { + Map map = Map.of(0, "a", 1, "b", 2, "c", null, "d"); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullValueDisallowed4() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullKeyDisallowed5() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", null, "e"); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullValueDisallowed5() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullKeyDisallowed6() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", + null, "f"); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullValueDisallowed6() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", + 5, null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullKeyDisallowed7() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", + 5, "f", null, "g"); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullValueDisallowed7() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", + 5, "f", 6, null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullKeyDisallowed8() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", + 5, "f", 6, "g", null, "h"); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullValueDisallowed8() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", + 5, "f", 6, "g", 7, null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullKeyDisallowed9() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", + 5, "f", 6, "g", 7, "h", null, "i"); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullValueDisallowed9() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", + 5, "f", 6, "g", 7, "h", 8, null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullKeyDisallowed10() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", + 5, "f", 6, "g", 7, "h", 8, "i", null, "j"); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullValueDisallowed10() { + Map map = Map.of(0, "a", 1, "b", 2, "c", 3, "d", 4, "e", + 5, "f", 6, "g", 7, "h", 8, "i", 9, null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullKeyDisallowedN() { + Map.Entry[] entries = genEntries(MAX_ENTRIES); + entries[0] = new AbstractMap.SimpleImmutableEntry(null, "a"); + Map map = Map.ofEntries(entries); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullValueDisallowedN() { + Map.Entry[] entries = genEntries(MAX_ENTRIES); + entries[0] = new AbstractMap.SimpleImmutableEntry(0, null); + Map map = Map.ofEntries(entries); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullEntryDisallowedN() { + Map.Entry[] entries = genEntries(MAX_ENTRIES); + entries[5] = null; + Map map = Map.ofEntries(entries); + } + + @Test(expectedExceptions=NullPointerException.class) + public void nullArrayDisallowed() { + Map.ofEntries(null); + } + + @Test(dataProvider="all") + public void serialEquality(Map act, Map exp) { + // assume that act.equals(exp) tested elsewhere + Map copy = serialClone(act); + assertEquals(act, copy); + assertEquals(copy, exp); + } + + @SuppressWarnings("unchecked") + static T serialClone(T obj) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(obj); + oos.close(); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + ObjectInputStream ois = new ObjectInputStream(bais); + return (T) ois.readObject(); + } catch (IOException | ClassNotFoundException e) { + throw new AssertionError(e); + } + } + + // Map.entry() tests + + @Test(expectedExceptions=NullPointerException.class) + public void entryWithNullKeyDisallowed() { + Map.Entry e = Map.entry(null, "x"); + } + + @Test(expectedExceptions=NullPointerException.class) + public void entryWithNullValueDisallowed() { + Map.Entry e = Map.entry(0, null); + } + + @Test + public void entryBasicTests() { + Map.Entry kvh1 = Map.entry("xyzzy", "plugh"); + Map.Entry kvh2 = Map.entry("foobar", "blurfl"); + Map.Entry sie = new AbstractMap.SimpleImmutableEntry("xyzzy", "plugh"); + + assertTrue(kvh1.equals(sie)); + assertTrue(sie.equals(kvh1)); + assertFalse(kvh2.equals(sie)); + assertFalse(sie.equals(kvh2)); + assertEquals(sie.hashCode(), kvh1.hashCode()); + assertEquals(sie.toString(), kvh1.toString()); + } + +} From 30655cc742f0faffb01cfb996ad0d243baac078a Mon Sep 17 00:00:00 2001 From: Xue-Lei Andrew Fan Date: Wed, 9 Dec 2015 10:36:33 +0000 Subject: [PATCH 27/62] 8141651: Deadlock in sun.security.ssl.SSLSocketImpl Reviewed-by: weijun --- .../classes/sun/security/ssl/SSLSocketImpl.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/jdk/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java b/jdk/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java index e3919c5d043..0162dc3b477 100644 --- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java +++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java @@ -766,17 +766,27 @@ void writeRecord(byte[] source, int offset, int length) throws IOException { // records, so this also increases robustness. // if (length > 0) { + IOException ioe = null; + byte description = 0; // 0: never used, make the compiler happy writeLock.lock(); try { outputRecord.deliver(source, offset, length); } catch (SSLHandshakeException she) { // may be record sequence number overflow - fatal(Alerts.alert_handshake_failure, she); + description = Alerts.alert_handshake_failure; + ioe = she; } catch (IOException e) { - fatal(Alerts.alert_unexpected_message, e); + description = Alerts.alert_unexpected_message; + ioe = e; } finally { writeLock.unlock(); } + + // Be care of deadlock. Please don't place the call to fatal() + // into the writeLock locked block. + if (ioe != null) { + fatal(description, ioe); + } } /* From 6af208a48532af66103cec241250426ee12f8483 Mon Sep 17 00:00:00 2001 From: Rob McKenna Date: Wed, 9 Dec 2015 15:16:00 +0000 Subject: [PATCH 28/62] 8143397: It looks like InetAddress.isReachable(timeout) works incorrectly Reviewed-by: xuelei, msheppar --- .../windows/native/libnet/Inet4AddressImpl.c | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/jdk/src/java.base/windows/native/libnet/Inet4AddressImpl.c b/jdk/src/java.base/windows/native/libnet/Inet4AddressImpl.c index 5213383c1e5..8f70ebe0c12 100644 --- a/jdk/src/java.base/windows/native/libnet/Inet4AddressImpl.c +++ b/jdk/src/java.base/windows/native/libnet/Inet4AddressImpl.c @@ -295,6 +295,7 @@ ping4(JNIEnv *env, char SendData[32] = {0}; LPVOID ReplyBuffer = NULL; DWORD ReplySize = 0; + jboolean ret = JNI_FALSE; hIcmpFile = IcmpCreateFile(); if (hIcmpFile == INVALID_HANDLE_VALUE) { @@ -318,7 +319,11 @@ ping4(JNIEnv *env, NULL, // PIP_OPTION_INFORMATION RequestOptions, ReplyBuffer,// LPVOID ReplyBuffer, ReplySize, // DWORD ReplySize, - timeout); // DWORD Timeout + // Note: IcmpSendEcho and its derivatives + // seem to have an undocumented minimum + // timeout of 1000ms below which the + // api behaves inconsistently. + (timeout < 1000) ? 1000 : timeout); // DWORD Timeout } else { dwRetVal = IcmpSendEcho2Ex(hIcmpFile, // HANDLE IcmpHandle, NULL, // HANDLE Event @@ -331,17 +336,19 @@ ping4(JNIEnv *env, NULL, // PIP_OPTION_INFORMATION RequestOptions, ReplyBuffer,// LPVOID ReplyBuffer, ReplySize, // DWORD ReplySize, - timeout); // DWORD Timeout + (timeout < 1000) ? 1000 : timeout); // DWORD Timeout + } + + if (dwRetVal != 0) { + PICMP_ECHO_REPLY pEchoReply = (PICMP_ECHO_REPLY)ReplyBuffer; + if ((int)pEchoReply->RoundTripTime <= timeout) + ret = JNI_TRUE; } free(ReplyBuffer); IcmpCloseHandle(hIcmpFile); - if (dwRetVal != 0) { - return JNI_TRUE; - } else { - return JNI_FALSE; - } + return ret; } /* From d37bb2422b487bc19e58bf5642a8fbbea4825d5f Mon Sep 17 00:00:00 2001 From: Claes Redestad Date: Wed, 9 Dec 2015 18:25:36 +0100 Subject: [PATCH 29/62] 8143127: InvokerBytecodeGenerator emitConst should handle Byte, Short, Character Reviewed-by: vlivanov, shade, forax --- .../lang/invoke/InvokerBytecodeGenerator.java | 73 ++++++++++++------- 1 file changed, 45 insertions(+), 28 deletions(-) diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java b/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java index 83d66d52299..08c53447a80 100644 --- a/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java +++ b/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java @@ -324,27 +324,54 @@ private void emitConst(Object con) { emitIconstInsn((int) con); return; } + if (con instanceof Byte) { + emitIconstInsn((byte)con); + return; + } + if (con instanceof Short) { + emitIconstInsn((short)con); + return; + } + if (con instanceof Character) { + emitIconstInsn((char)con); + return; + } if (con instanceof Long) { long x = (long) con; - if (x == (short) x) { - emitIconstInsn((int) x); - mv.visitInsn(Opcodes.I2L); + short sx = (short)x; + if (x == sx) { + if (sx >= 0 && sx <= 1) { + mv.visitInsn(Opcodes.LCONST_0 + (int) sx); + } else { + emitIconstInsn((int) x); + mv.visitInsn(Opcodes.I2L); + } return; } } if (con instanceof Float) { float x = (float) con; - if (x == (short) x) { - emitIconstInsn((int) x); - mv.visitInsn(Opcodes.I2F); + short sx = (short)x; + if (x == sx) { + if (sx >= 0 && sx <= 2) { + mv.visitInsn(Opcodes.FCONST_0 + (int) sx); + } else { + emitIconstInsn((int) x); + mv.visitInsn(Opcodes.I2F); + } return; } } if (con instanceof Double) { double x = (double) con; - if (x == (short) x) { - emitIconstInsn((int) x); - mv.visitInsn(Opcodes.I2D); + short sx = (short)x; + if (x == sx) { + if (sx >= 0 && sx <= 1) { + mv.visitInsn(Opcodes.DCONST_0 + (int) sx); + } else { + emitIconstInsn((int) x); + mv.visitInsn(Opcodes.I2D); + } return; } } @@ -356,26 +383,16 @@ private void emitConst(Object con) { mv.visitLdcInsn(con); } - private void emitIconstInsn(int i) { - int opcode; - switch (i) { - case 0: opcode = Opcodes.ICONST_0; break; - case 1: opcode = Opcodes.ICONST_1; break; - case 2: opcode = Opcodes.ICONST_2; break; - case 3: opcode = Opcodes.ICONST_3; break; - case 4: opcode = Opcodes.ICONST_4; break; - case 5: opcode = Opcodes.ICONST_5; break; - default: - if (i == (byte) i) { - mv.visitIntInsn(Opcodes.BIPUSH, i & 0xFF); - } else if (i == (short) i) { - mv.visitIntInsn(Opcodes.SIPUSH, (char) i); - } else { - mv.visitLdcInsn(i); - } - return; + private void emitIconstInsn(final int cst) { + if (cst >= -1 && cst <= 5) { + mv.visitInsn(Opcodes.ICONST_0 + cst); + } else if (cst >= Byte.MIN_VALUE && cst <= Byte.MAX_VALUE) { + mv.visitIntInsn(Opcodes.BIPUSH, cst); + } else if (cst >= Short.MIN_VALUE && cst <= Short.MAX_VALUE) { + mv.visitIntInsn(Opcodes.SIPUSH, cst); + } else { + mv.visitLdcInsn(cst); } - mv.visitInsn(opcode); } /* From 8da753e1be0d4de4cbb71b80a85668c4a141a4ea Mon Sep 17 00:00:00 2001 From: Rob McKenna Date: Wed, 9 Dec 2015 17:34:09 +0000 Subject: [PATCH 30/62] 8141370: com/sun/jndi/ldap/LdapTimeoutTest.java failed intermittently Reviewed-by: vinnie --- jdk/test/ProblemList.txt | 7 + .../sun/jndi/ldap/DeadSSLLdapTimeoutTest.java | 210 ++++++++++++++++++ .../com/sun/jndi/ldap/LdapTimeoutTest.java | 38 ---- 3 files changed, 217 insertions(+), 38 deletions(-) create mode 100644 jdk/test/com/sun/jndi/ldap/DeadSSLLdapTimeoutTest.java diff --git a/jdk/test/ProblemList.txt b/jdk/test/ProblemList.txt index 8d61ebc654e..51abaacbf53 100644 --- a/jdk/test/ProblemList.txt +++ b/jdk/test/ProblemList.txt @@ -407,3 +407,10 @@ sun/tools/jinfo/JInfoRunningProcessFlagTest.java generic-all sun/jvmstat/monitor/MonitoredVm/MonitorVmStartTerminate.java generic-all ############################################################################ + +# jdk_other + +############################################################################ + +# 8141370 +com/sun/jndi/ldap/DeadSSLLdapTimeoutTest.java linux-i586,macosx-all diff --git a/jdk/test/com/sun/jndi/ldap/DeadSSLLdapTimeoutTest.java b/jdk/test/com/sun/jndi/ldap/DeadSSLLdapTimeoutTest.java new file mode 100644 index 00000000000..1301c4c2466 --- /dev/null +++ b/jdk/test/com/sun/jndi/ldap/DeadSSLLdapTimeoutTest.java @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2011, 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. + */ + +/** + * @test + * @run main/othervm DeadSSLLdapTimeoutTest + * @bug 8141370 + * @key intermittent + */ + +import java.net.Socket; +import java.net.ServerSocket; +import java.net.SocketTimeoutException; +import java.io.*; +import javax.naming.*; +import javax.naming.directory.*; +import java.util.List; +import java.util.Hashtable; +import java.util.ArrayList; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.TimeUnit; +import javax.net.ssl.SSLHandshakeException; + +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.NANOSECONDS; + + +class DeadServerTimeoutSSLTest implements Callable { + + Hashtable env; + DeadSSLServer server; + boolean passed = false; + private int HANGING_TEST_TIMEOUT = 20_000; + + public DeadServerTimeoutSSLTest(Hashtable env) throws IOException { + this.server = new DeadSSLServer(); + this.env = env; + } + + public void performOp(InitialContext ctx) throws NamingException {} + + public void handleNamingException(NamingException e, long start, long end) { + if (e.getCause() instanceof SocketTimeoutException) { + // SSL connect will timeout via readReply using + // SocketTimeoutException + e.printStackTrace(); + pass(); + } else if (e.getCause() instanceof SSLHandshakeException + && e.getCause().getCause() instanceof EOFException) { + // test seems to be failing intermittently on some + // platforms. + pass(); + } else { + fail(e); + } + } + + public void pass() { + this.passed = true; + } + + public void fail() { + throw new RuntimeException("Test failed"); + } + + public void fail(Exception e) { + throw new RuntimeException("Test failed", e); + } + + boolean shutItDown(InitialContext ctx) { + try { + if (ctx != null) ctx.close(); + return true; + } catch (NamingException ex) { + return false; + } + } + + public Boolean call() { + InitialContext ctx = null; + ScheduledFuture killer = null; + long start = System.nanoTime(); + + try { + while(!server.accepting()) + Thread.sleep(200); // allow the server to start up + Thread.sleep(200); // to be sure + + env.put(Context.PROVIDER_URL, "ldap://localhost:" + + server.getLocalPort()); + + try { + ctx = new InitialDirContext(env); + performOp(ctx); + fail(); + } catch (NamingException e) { + long end = System.nanoTime(); + System.out.println(this.getClass().toString() + " - elapsed: " + + NANOSECONDS.toMillis(end - start)); + handleNamingException(e, start, end); + } finally { + if (killer != null && !killer.isDone()) + killer.cancel(true); + shutItDown(ctx); + server.close(); + } + return passed; + } catch (IOException|InterruptedException e) { + throw new RuntimeException(e); + } + } +} + +class DeadSSLServer extends Thread { + ServerSocket serverSock; + boolean accepting = false; + + public DeadSSLServer() throws IOException { + this.serverSock = new ServerSocket(0); + start(); + } + + public void run() { + while(true) { + try { + accepting = true; + Socket socket = serverSock.accept(); + } catch (Exception e) { + break; + } + } + } + + public int getLocalPort() { + return serverSock.getLocalPort(); + } + + public boolean accepting() { + return accepting; + } + + public void close() throws IOException { + serverSock.close(); + } +} + +public class DeadSSLLdapTimeoutTest { + + static Hashtable createEnv() { + Hashtable env = new Hashtable(11); + env.put(Context.INITIAL_CONTEXT_FACTORY, + "com.sun.jndi.ldap.LdapCtxFactory"); + return env; + } + + public static void main(String[] args) throws Exception { + + InitialContext ctx = null; + + // + // Running this test serially as it seems to tickle a problem + // on older kernels + // + // run the DeadServerTest with connect / read timeouts set + // and ssl enabled + // this should exit with a SocketTimeoutException as the root cause + // it should also use the connect timeout instead of the read timeout + System.out.println("Running connect timeout test with 10ms connect timeout, 3000ms read timeout & SSL"); + Hashtable sslenv = createEnv(); + sslenv.put("com.sun.jndi.ldap.connect.timeout", "10"); + sslenv.put("com.sun.jndi.ldap.read.timeout", "3000"); + sslenv.put(Context.SECURITY_PROTOCOL, "ssl"); + boolean testFailed = + (new DeadServerTimeoutSSLTest(sslenv).call()) ? false : true; + + if (testFailed) { + throw new AssertionError("some tests failed"); + } + + } + +} + diff --git a/jdk/test/com/sun/jndi/ldap/LdapTimeoutTest.java b/jdk/test/com/sun/jndi/ldap/LdapTimeoutTest.java index fe48073a6b3..5da62ee647e 100644 --- a/jdk/test/com/sun/jndi/ldap/LdapTimeoutTest.java +++ b/jdk/test/com/sun/jndi/ldap/LdapTimeoutTest.java @@ -225,29 +225,6 @@ public void handleNamingException(NamingException e, long start, long end) } } -class DeadServerTimeoutSSLTest extends DeadServerTest { - - public DeadServerTimeoutSSLTest(Hashtable env) throws IOException { - super(env); - } - - public void handleNamingException(NamingException e, long start, long end) { - if (e.getCause() instanceof SocketTimeoutException) { - // SSL connect will timeout via readReply using - // SocketTimeoutException - e.printStackTrace(); - pass(); - } else if (e.getCause() instanceof SSLHandshakeException - && e.getCause().getCause() instanceof EOFException) { - // test seems to be failing intermittently on some - // platforms. - pass(); - } else { - fail(e); - } - } -} - class ReadServerNoTimeoutTest extends ReadServerTest { @@ -454,21 +431,6 @@ public static void main(String[] args) throws Exception { } } - // - // Running this test serially as it seems to tickle a problem - // on older kernels - // - // run the DeadServerTest with connect / read timeouts set - // and ssl enabled - // this should exit with a SocketTimeoutException as the root cause - // it should also use the connect timeout instead of the read timeout - System.out.println("Running connect timeout test with 10ms connect timeout, 3000ms read timeout & SSL"); - Hashtable sslenv = createEnv(); - sslenv.put("com.sun.jndi.ldap.connect.timeout", "10"); - sslenv.put("com.sun.jndi.ldap.read.timeout", "3000"); - sslenv.put(Context.SECURITY_PROTOCOL, "ssl"); - testFailed = (new DeadServerTimeoutSSLTest(sslenv).call()) ? false : true; - if (testFailed) { throw new AssertionError("some tests failed"); } From ee046050d6aaa2aaef4b79d3ae70becff8a0bc5b Mon Sep 17 00:00:00 2001 From: Rachna Goel Date: Wed, 9 Dec 2015 14:20:51 +0530 Subject: [PATCH 31/62] 8025547: Locale.toString() documentation error Updated API doc of Locale.toString method. Reviewed-by: okutsu --- jdk/src/java.base/share/classes/java/util/Locale.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jdk/src/java.base/share/classes/java/util/Locale.java b/jdk/src/java.base/share/classes/java/util/Locale.java index 0a7c02965f6..d01891be0c5 100644 --- a/jdk/src/java.base/share/classes/java/util/Locale.java +++ b/jdk/src/java.base/share/classes/java/util/Locale.java @@ -1248,7 +1248,7 @@ LocaleExtensions getLocaleExtensions() { * object, consisting of language, country, variant, script, * and extensions as below: *
- * language + "_" + country + "_" + (variant + "_#" | "#") + script + "-" + extensions + * language + "_" + country + "_" + (variant + "_#" | "#") + script + "_" + extensions *
* * Language is always lower case, country is always upper case, script is always title @@ -1278,7 +1278,7 @@ LocaleExtensions getLocaleExtensions() { *
  • {@code en_US_WIN}
  • *
  • {@code de__POSIX}
  • *
  • {@code zh_CN_#Hans}
  • - *
  • {@code zh_TW_#Hant-x-java}
  • + *
  • {@code zh_TW_#Hant_x-java}
  • *
  • {@code th_TH_TH_#u-nu-thai}
  • * * @return A string representation of the Locale, for debugging. From f23d71b4f4f446dd99d0a7f70928a633c803a87e Mon Sep 17 00:00:00 2001 From: Ambarish Rapte Date: Wed, 9 Dec 2015 18:12:49 +0300 Subject: [PATCH 32/62] 8144915: TextField throws NPE Reviewed-by: ssadetsky, serb --- jdk/src/java.desktop/share/classes/java/awt/TextField.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/jdk/src/java.desktop/share/classes/java/awt/TextField.java b/jdk/src/java.desktop/share/classes/java/awt/TextField.java index 2dfd19c4128..7c5563088c3 100644 --- a/jdk/src/java.desktop/share/classes/java/awt/TextField.java +++ b/jdk/src/java.desktop/share/classes/java/awt/TextField.java @@ -309,6 +309,9 @@ public void setText(String t) { * @return Returns text after replacing EOL characters. */ private static String replaceEOL(String text) { + if (text == null) { + return text; + } String[] strEOLs = {System.lineSeparator(), "\n"}; for (String eol : strEOLs) { if (text.contains(eol)) { From 3144cc4a273306a9f18d6a7863a5386c97b4915d Mon Sep 17 00:00:00 2001 From: Sergey Bylokhov Date: Wed, 9 Dec 2015 18:56:59 +0300 Subject: [PATCH 33/62] 8143909: Behavior of null arguments not specified in javax.sound.midi.spi The specification change was reviewed by Florian Bomers also Reviewed-by: amenkov --- .../sound/AbstractMidiDeviceProvider.java | 8 +- .../sun/media/sound/JARSoundbankReader.java | 6 +- .../sound/RealTimeSequencerProvider.java | 5 +- .../com/sun/media/sound/SoftProvider.java | 5 +- .../media/sound/StandardMidiFileWriter.java | 34 ++- .../classes/javax/sound/midi/MidiSystem.java | 82 +++++-- .../sound/midi/spi/MidiDeviceProvider.java | 6 +- .../javax/sound/midi/spi/MidiFileReader.java | 8 +- .../javax/sound/midi/spi/MidiFileWriter.java | 8 +- .../javax/sound/midi/spi/SoundbankReader.java | 5 +- .../midi/MidiDeviceProvider/NullInfo.java | 71 ------ .../MidiDeviceProvider/ExpectedNPEOnNull.java | 94 ++++++++ .../MidiDeviceProvider/FakeInfo.java | 0 .../MidiDeviceProvider/UnsupportedInfo.java | 0 .../spi/MidiFileReader/ExpectedNPEOnNull.java | 130 +++++++++++ .../spi/MidiFileWriter/ExpectedNPEOnNull.java | 215 ++++++++++++++++++ .../SoundbankReader/ExpectedNPEOnNull.java | 94 ++++++++ 17 files changed, 647 insertions(+), 124 deletions(-) delete mode 100644 jdk/test/javax/sound/midi/MidiDeviceProvider/NullInfo.java create mode 100644 jdk/test/javax/sound/midi/spi/MidiDeviceProvider/ExpectedNPEOnNull.java rename jdk/test/javax/sound/midi/{ => spi}/MidiDeviceProvider/FakeInfo.java (100%) rename jdk/test/javax/sound/midi/{ => spi}/MidiDeviceProvider/UnsupportedInfo.java (100%) create mode 100644 jdk/test/javax/sound/midi/spi/MidiFileReader/ExpectedNPEOnNull.java create mode 100644 jdk/test/javax/sound/midi/spi/MidiFileWriter/ExpectedNPEOnNull.java create mode 100644 jdk/test/javax/sound/midi/spi/SoundbankReader/ExpectedNPEOnNull.java diff --git a/jdk/src/java.desktop/share/classes/com/sun/media/sound/AbstractMidiDeviceProvider.java b/jdk/src/java.desktop/share/classes/com/sun/media/sound/AbstractMidiDeviceProvider.java index 8d0003cc987..6ff1ccde6b0 100644 --- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/AbstractMidiDeviceProvider.java +++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/AbstractMidiDeviceProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -25,10 +25,11 @@ package com.sun.media.sound; +import java.util.Objects; + import javax.sound.midi.MidiDevice; import javax.sound.midi.spi.MidiDeviceProvider; - /** * Super class for MIDI input or output device provider. * @@ -127,7 +128,8 @@ public final MidiDevice.Info[] getDeviceInfo() { } @Override - public final MidiDevice getDevice(MidiDevice.Info info) { + public final MidiDevice getDevice(final MidiDevice.Info info) { + Objects.requireNonNull(info); if (info instanceof Info) { readDeviceInfos(); MidiDevice[] devices = getDeviceCache(); diff --git a/jdk/src/java.desktop/share/classes/com/sun/media/sound/JARSoundbankReader.java b/jdk/src/java.desktop/share/classes/com/sun/media/sound/JARSoundbankReader.java index 32fc90ffbb7..30c49810771 100644 --- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/JARSoundbankReader.java +++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/JARSoundbankReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 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 @@ -22,6 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package com.sun.media.sound; import java.io.BufferedReader; @@ -32,6 +33,8 @@ import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; +import java.util.Objects; + import javax.sound.midi.InvalidMidiDataException; import javax.sound.midi.Soundbank; import javax.sound.midi.spi.SoundbankReader; @@ -112,6 +115,7 @@ public Soundbank getSoundbank(URL url) public Soundbank getSoundbank(InputStream stream) throws InvalidMidiDataException, IOException { + Objects.requireNonNull(stream); return null; } diff --git a/jdk/src/java.desktop/share/classes/com/sun/media/sound/RealTimeSequencerProvider.java b/jdk/src/java.desktop/share/classes/com/sun/media/sound/RealTimeSequencerProvider.java index fa5bfa1afbf..32ca9ff5331 100644 --- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/RealTimeSequencerProvider.java +++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/RealTimeSequencerProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -25,6 +25,8 @@ package com.sun.media.sound; +import java.util.Objects; + import javax.sound.midi.MidiDevice; import javax.sound.midi.spi.MidiDeviceProvider; @@ -42,6 +44,7 @@ public MidiDevice.Info[] getDeviceInfo() { @Override public MidiDevice getDevice(final MidiDevice.Info info) { + Objects.requireNonNull(info); if (RealTimeSequencer.info.equals(info)) { return new RealTimeSequencer(); } diff --git a/jdk/src/java.desktop/share/classes/com/sun/media/sound/SoftProvider.java b/jdk/src/java.desktop/share/classes/com/sun/media/sound/SoftProvider.java index 89d85362539..e4bc1a7e342 100644 --- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/SoftProvider.java +++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/SoftProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 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 @@ -25,6 +25,8 @@ package com.sun.media.sound; +import java.util.Objects; + import javax.sound.midi.MidiDevice; import javax.sound.midi.spi.MidiDeviceProvider; @@ -42,6 +44,7 @@ public MidiDevice.Info[] getDeviceInfo() { @Override public MidiDevice getDevice(final MidiDevice.Info info) { + Objects.requireNonNull(info); if (SoftSynthesizer.info.equals(info)) { return new SoftSynthesizer(); } diff --git a/jdk/src/java.desktop/share/classes/com/sun/media/sound/StandardMidiFileWriter.java b/jdk/src/java.desktop/share/classes/com/sun/media/sound/StandardMidiFileWriter.java index f03921e72df..cc75e40e251 100644 --- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/StandardMidiFileWriter.java +++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/StandardMidiFileWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -25,21 +25,22 @@ package com.sun.media.sound; -import java.io.DataOutputStream; -import java.io.PipedInputStream; -import java.io.PipedOutputStream; -import java.io.ByteArrayOutputStream; import java.io.ByteArrayInputStream; -import java.io.SequenceInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; import java.io.File; import java.io.FileOutputStream; -import java.io.InputStream; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import java.io.SequenceInputStream; +import java.util.Objects; import javax.sound.midi.InvalidMidiDataException; -import javax.sound.midi.MidiEvent; import javax.sound.midi.MetaMessage; +import javax.sound.midi.MidiEvent; import javax.sound.midi.Sequence; import javax.sound.midi.ShortMessage; import javax.sound.midi.SysexMessage; @@ -115,24 +116,16 @@ public int[] getMidiFileTypes(Sequence sequence){ return typesArray; } - public boolean isFileTypeSupported(int type) { - for(int i=0; i providers = getMidiFileReaders(); MidiFileFormat format = null; @@ -602,11 +613,13 @@ public static MidiFileFormat getMidiFileFormat(InputStream stream) * @throws InvalidMidiDataException if the URL does not point to valid MIDI * file data recognized by the system * @throws IOException if an I/O exception occurs while accessing the URL + * @throws NullPointerException if {@code url} is {@code null} * @see #getMidiFileFormat(InputStream) * @see #getMidiFileFormat(File) */ - public static MidiFileFormat getMidiFileFormat(URL url) - throws InvalidMidiDataException, IOException { + public static MidiFileFormat getMidiFileFormat(final URL url) + throws InvalidMidiDataException, IOException { + Objects.requireNonNull(url); List providers = getMidiFileReaders(); MidiFileFormat format = null; @@ -646,11 +659,13 @@ public static MidiFileFormat getMidiFileFormat(URL url) * @throws InvalidMidiDataException if the {@code File} does not point to * valid MIDI file data recognized by the system * @throws IOException if an I/O exception occurs while accessing the file + * @throws NullPointerException if {@code file} is {@code null} * @see #getMidiFileFormat(InputStream) * @see #getMidiFileFormat(URL) */ - public static MidiFileFormat getMidiFileFormat(File file) - throws InvalidMidiDataException, IOException { + public static MidiFileFormat getMidiFileFormat(final File file) + throws InvalidMidiDataException, IOException { + Objects.requireNonNull(file); List providers = getMidiFileReaders(); MidiFileFormat format = null; @@ -699,11 +714,13 @@ public static MidiFileFormat getMidiFileFormat(File file) * @throws InvalidMidiDataException if the stream does not point to valid * MIDI file data recognized by the system * @throws IOException if an I/O exception occurs while accessing the stream + * @throws NullPointerException if {@code stream} is {@code null} * @see InputStream#markSupported * @see InputStream#mark */ - public static Sequence getSequence(InputStream stream) - throws InvalidMidiDataException, IOException { + public static Sequence getSequence(final InputStream stream) + throws InvalidMidiDataException, IOException { + Objects.requireNonNull(stream); List providers = getMidiFileReaders(); Sequence sequence = null; @@ -743,9 +760,11 @@ public static Sequence getSequence(InputStream stream) * @throws InvalidMidiDataException if the URL does not point to valid MIDI * file data recognized by the system * @throws IOException if an I/O exception occurs while accessing the URL + * @throws NullPointerException if {@code url} is {@code null} */ - public static Sequence getSequence(URL url) - throws InvalidMidiDataException, IOException { + public static Sequence getSequence(final URL url) + throws InvalidMidiDataException, IOException { + Objects.requireNonNull(url); List providers = getMidiFileReaders(); Sequence sequence = null; @@ -787,9 +806,11 @@ public static Sequence getSequence(URL url) * @throws InvalidMidiDataException if the File does not point to valid MIDI * file data recognized by the system * @throws IOException if an I/O exception occurs + * @throws NullPointerException if {@code file} is {@code null} */ - public static Sequence getSequence(File file) - throws InvalidMidiDataException, IOException { + public static Sequence getSequence(final File file) + throws InvalidMidiDataException, IOException { + Objects.requireNonNull(file); List providers = getMidiFileReaders(); Sequence sequence = null; @@ -870,8 +891,10 @@ public static boolean isFileTypeSupported(int fileType) { * @param sequence the sequence for which MIDI file type support is queried * @return the set of unique supported file types. If no file types are * supported, returns an array of length 0. + * @throws NullPointerException if {@code sequence} is {@code null} */ - public static int[] getMidiFileTypes(Sequence sequence) { + public static int[] getMidiFileTypes(final Sequence sequence) { + Objects.requireNonNull(sequence); List providers = getMidiFileWriters(); Set allTypes = new HashSet<>(); @@ -903,8 +926,11 @@ public static int[] getMidiFileTypes(Sequence sequence) { * @param sequence the sequence for which file writing support is queried * @return {@code true} if the file type is supported for this sequence, * otherwise {@code false} + * @throws NullPointerException if {@code sequence} is {@code null} */ - public static boolean isFileTypeSupported(int fileType, Sequence sequence) { + public static boolean isFileTypeSupported(final int fileType, + final Sequence sequence) { + Objects.requireNonNull(sequence); List providers = getMidiFileWriters(); @@ -929,10 +955,15 @@ public static boolean isFileTypeSupported(int fileType, Sequence sequence) { * @throws IOException if an I/O exception occurs * @throws IllegalArgumentException if the file format is not supported by * the system + * @throws NullPointerException if {@code in} or {@code out} are + * {@code null} * @see #isFileTypeSupported(int, Sequence) * @see #getMidiFileTypes(Sequence) */ - public static int write(Sequence in, int fileType, OutputStream out) throws IOException { + public static int write(final Sequence in, final int fileType, + final OutputStream out) throws IOException { + Objects.requireNonNull(in); + Objects.requireNonNull(out); List providers = getMidiFileWriters(); //$$fb 2002-04-17: Fix for 4635287: Standard MidiFileWriter cannot write empty Sequences @@ -963,10 +994,15 @@ public static int write(Sequence in, int fileType, OutputStream out) throws IOEx * @throws IOException if an I/O exception occurs * @throws IllegalArgumentException if the file type is not supported by the * system + * @throws NullPointerException if {@code in} or {@code out} are + * {@code null} * @see #isFileTypeSupported(int, Sequence) * @see #getMidiFileTypes(Sequence) */ - public static int write(Sequence in, int type, File out) throws IOException { + public static int write(final Sequence in, final int type, final File out) + throws IOException { + Objects.requireNonNull(in); + Objects.requireNonNull(out); List providers = getMidiFileWriters(); //$$fb 2002-04-17: Fix for 4635287: Standard MidiFileWriter cannot write empty Sequences diff --git a/jdk/src/java.desktop/share/classes/javax/sound/midi/spi/MidiDeviceProvider.java b/jdk/src/java.desktop/share/classes/javax/sound/midi/spi/MidiDeviceProvider.java index 97c9d92b7f0..4f820b497d7 100644 --- a/jdk/src/java.desktop/share/classes/javax/sound/midi/spi/MidiDeviceProvider.java +++ b/jdk/src/java.desktop/share/classes/javax/sound/midi/spi/MidiDeviceProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -26,6 +26,7 @@ package javax.sound.midi.spi; import java.util.Arrays; +import java.util.Objects; import javax.sound.midi.MidiDevice; @@ -46,8 +47,10 @@ public abstract class MidiDeviceProvider { * is queried * @return {@code true} if the specified device is supported, otherwise * {@code false} + * @throws NullPointerException if {@code info} is {@code null} */ public boolean isDeviceSupported(final MidiDevice.Info info) { + Objects.requireNonNull(info); return Arrays.asList(getDeviceInfo()).contains(info); } @@ -67,6 +70,7 @@ public boolean isDeviceSupported(final MidiDevice.Info info) { * @throws IllegalArgumentException if the info object specified does not * match the info object for a device supported by this * {@code MidiDeviceProvider} + * @throws NullPointerException if {@code info} is {@code null} */ public abstract MidiDevice getDevice(MidiDevice.Info info); } diff --git a/jdk/src/java.desktop/share/classes/javax/sound/midi/spi/MidiFileReader.java b/jdk/src/java.desktop/share/classes/javax/sound/midi/spi/MidiFileReader.java index bbdca9e97bb..f1c3b6f6ff5 100644 --- a/jdk/src/java.desktop/share/classes/javax/sound/midi/spi/MidiFileReader.java +++ b/jdk/src/java.desktop/share/classes/javax/sound/midi/spi/MidiFileReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -60,6 +60,7 @@ public abstract class MidiFileReader { * @throws InvalidMidiDataException if the stream does not point to valid * MIDI file data recognized by the system * @throws IOException if an I/O exception occurs + * @throws NullPointerException if {@code stream} is {@code null} * @see InputStream#markSupported * @see InputStream#mark */ @@ -76,6 +77,7 @@ public abstract MidiFileFormat getMidiFileFormat(InputStream stream) * @throws InvalidMidiDataException if the URL does not point to valid MIDI * file data recognized by the system * @throws IOException if an I/O exception occurs + * @throws NullPointerException if {@code url} is {@code null} */ public abstract MidiFileFormat getMidiFileFormat(URL url) throws InvalidMidiDataException, IOException; @@ -90,6 +92,7 @@ public abstract MidiFileFormat getMidiFileFormat(URL url) * @throws InvalidMidiDataException if the {@code File} does not point to * valid MIDI file data recognized by the system * @throws IOException if an I/O exception occurs + * @throws NullPointerException if {@code file} is {@code null} */ public abstract MidiFileFormat getMidiFileFormat(File file) throws InvalidMidiDataException, IOException; @@ -110,6 +113,7 @@ public abstract MidiFileFormat getMidiFileFormat(File file) * @throws InvalidMidiDataException if the stream does not point to valid * MIDI file data recognized by the system * @throws IOException if an I/O exception occurs + * @throws NullPointerException if {@code stream} is {@code null} * @see InputStream#markSupported * @see InputStream#mark */ @@ -126,6 +130,7 @@ public abstract Sequence getSequence(InputStream stream) * @throws InvalidMidiDataException if the URL does not point to valid MIDI * file data recognized by the system * @throws IOException if an I/O exception occurs + * @throws NullPointerException if {@code url} is {@code null} */ public abstract Sequence getSequence(URL url) throws InvalidMidiDataException, IOException; @@ -141,6 +146,7 @@ public abstract Sequence getSequence(URL url) * @throws InvalidMidiDataException if the {@code File} does not point to * valid MIDI file data recognized by the system * @throws IOException if an I/O exception occurs + * @throws NullPointerException if {@code file} is {@code null} */ public abstract Sequence getSequence(File file) throws InvalidMidiDataException, IOException; diff --git a/jdk/src/java.desktop/share/classes/javax/sound/midi/spi/MidiFileWriter.java b/jdk/src/java.desktop/share/classes/javax/sound/midi/spi/MidiFileWriter.java index a1800f7f701..9a3fdcbc62e 100644 --- a/jdk/src/java.desktop/share/classes/javax/sound/midi/spi/MidiFileWriter.java +++ b/jdk/src/java.desktop/share/classes/javax/sound/midi/spi/MidiFileWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -58,6 +58,7 @@ public abstract class MidiFileWriter { * queried * @return array of file types. If no file types are supported, returns an * array of length 0. + * @throws NullPointerException if {@code sequence} is {@code null} */ public abstract int[] getMidiFileTypes(Sequence sequence); @@ -88,6 +89,7 @@ public boolean isFileTypeSupported(int fileType) { * @param sequence the sequence for which file writing support is queried * @return {@code true} if the file type is supported for this sequence, * otherwise {@code false} + * @throws NullPointerException if {@code sequence} is {@code null} */ public boolean isFileTypeSupported(int fileType, Sequence sequence) { @@ -111,6 +113,8 @@ public boolean isFileTypeSupported(int fileType, Sequence sequence) { * @throws IOException if an I/O exception occurs * @throws IllegalArgumentException if the file type is not supported by * this file writer + * @throws NullPointerException if {@code in} or {@code out} are + * {@code null} * @see #isFileTypeSupported(int, Sequence) * @see #getMidiFileTypes(Sequence) */ @@ -129,6 +133,8 @@ public abstract int write(Sequence in, int fileType, OutputStream out) * @throws IOException if an I/O exception occurs * @throws IllegalArgumentException if the file type is not supported by * this file writer + * @throws NullPointerException if {@code in} or {@code out} are + * {@code null} * @see #isFileTypeSupported(int, Sequence) * @see #getMidiFileTypes(Sequence) */ diff --git a/jdk/src/java.desktop/share/classes/javax/sound/midi/spi/SoundbankReader.java b/jdk/src/java.desktop/share/classes/javax/sound/midi/spi/SoundbankReader.java index 501c18b6ad5..8690b0d18ed 100644 --- a/jdk/src/java.desktop/share/classes/javax/sound/midi/spi/SoundbankReader.java +++ b/jdk/src/java.desktop/share/classes/javax/sound/midi/spi/SoundbankReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -52,6 +52,7 @@ public abstract class SoundbankReader { * @throws InvalidMidiDataException if the URL does not point to valid MIDI * soundbank data recognized by this soundbank reader * @throws IOException if an I/O error occurs + * @throws NullPointerException if {@code url} is {@code null} */ public abstract Soundbank getSoundbank(URL url) throws InvalidMidiDataException, IOException; @@ -64,6 +65,7 @@ public abstract Soundbank getSoundbank(URL url) * @throws InvalidMidiDataException if the stream does not point to valid * MIDI soundbank data recognized by this soundbank reader * @throws IOException if an I/O error occurs + * @throws NullPointerException if {@code stream} is {@code null} */ public abstract Soundbank getSoundbank(InputStream stream) throws InvalidMidiDataException, IOException; @@ -76,6 +78,7 @@ public abstract Soundbank getSoundbank(InputStream stream) * @throws InvalidMidiDataException if the file does not point to valid MIDI * soundbank data recognized by this soundbank reader * @throws IOException if an I/O error occurs + * @throws NullPointerException if {@code file} is {@code null} */ public abstract Soundbank getSoundbank(File file) throws InvalidMidiDataException, IOException; diff --git a/jdk/test/javax/sound/midi/MidiDeviceProvider/NullInfo.java b/jdk/test/javax/sound/midi/MidiDeviceProvider/NullInfo.java deleted file mode 100644 index 9fa00c03172..00000000000 --- a/jdk/test/javax/sound/midi/MidiDeviceProvider/NullInfo.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 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. - */ - -import java.util.Collection; -import java.util.HashSet; - -import javax.sound.midi.MidiDevice; -import javax.sound.midi.MidiSystem; -import javax.sound.midi.MidiUnavailableException; -import javax.sound.midi.spi.MidiDeviceProvider; - -import static java.util.ServiceLoader.load; - -/** - * @test - * @bug 8058115 - * @summary MidiDeviceProvider shouldn't returns incorrect results or throw NPE - * in case of null MidiDevice.Info - * @author Sergey Bylokhov - */ -public final class NullInfo { - - public static void main(final String[] args) { - // MidiSystem API - try { - MidiSystem.getMidiDevice(null); - throw new RuntimeException("IllegalArgumentException expected"); - } catch (final MidiUnavailableException e) { - throw new RuntimeException("IllegalArgumentException expected", e); - } catch (final IllegalArgumentException ignored) { - // expected - } - // MidiDeviceProvider API - final Collection errors = new HashSet<>(); - for (final MidiDeviceProvider mdp : load(MidiDeviceProvider.class)) { - try { - if (mdp.isDeviceSupported(null)) { - throw new RuntimeException("null is supported"); - } - final MidiDevice device = mdp.getDevice(null); - System.err.println("MidiDevice: " + device); - throw new RuntimeException("IllegalArgumentException expected"); - } catch (final IllegalArgumentException e) { - errors.add(e.getMessage()); - } - } - if (errors.size() != 1) { - throw new RuntimeException("Wrong number of messages:" + errors); - } - } -} diff --git a/jdk/test/javax/sound/midi/spi/MidiDeviceProvider/ExpectedNPEOnNull.java b/jdk/test/javax/sound/midi/spi/MidiDeviceProvider/ExpectedNPEOnNull.java new file mode 100644 index 00000000000..efb57eeeae2 --- /dev/null +++ b/jdk/test/javax/sound/midi/spi/MidiDeviceProvider/ExpectedNPEOnNull.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.util.Objects; + +import javax.sound.midi.MidiDevice; +import javax.sound.midi.MidiSystem; +import javax.sound.midi.spi.MidiDeviceProvider; + +import static java.util.ServiceLoader.load; + +/** + * @test + * @bug 8143909 + * @author Sergey Bylokhov + */ +public final class ExpectedNPEOnNull { + + public static void main(final String[] args) throws Exception { + testMS(); + for (final MidiDeviceProvider mdp : load(MidiDeviceProvider.class)) { + testMDP(mdp); + } + testMDP(customMDP); + } + + /** + * Tests the part of MidiSystem API, which implemented via + * MidiDeviceProvider. + */ + private static void testMS() throws Exception { + // MidiSystem#getMidiDevice(MidiDevice.Info) + try { + MidiSystem.getMidiDevice(null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + + /** + * Tests the MidiDeviceProvider API directly. + */ + private static void testMDP(final MidiDeviceProvider mdp) throws Exception { + // MidiDeviceProvider#isDeviceSupported(Info) + try { + mdp.isDeviceSupported(null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + // MidiDeviceProvider#getDevice(Info) + try { + mdp.getDevice(null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + + /** + * Tests some default implementation of MidiDeviceProvider API, using the + * custom {@code MidiDeviceProvider}, which support nothing. + */ + static MidiDeviceProvider customMDP = new MidiDeviceProvider() { + @Override + public MidiDevice.Info[] getDeviceInfo() { + return new MidiDevice.Info[0]; + } + + @Override + public MidiDevice getDevice(MidiDevice.Info info) { + Objects.requireNonNull(info); + return null; + } + }; +} diff --git a/jdk/test/javax/sound/midi/MidiDeviceProvider/FakeInfo.java b/jdk/test/javax/sound/midi/spi/MidiDeviceProvider/FakeInfo.java similarity index 100% rename from jdk/test/javax/sound/midi/MidiDeviceProvider/FakeInfo.java rename to jdk/test/javax/sound/midi/spi/MidiDeviceProvider/FakeInfo.java diff --git a/jdk/test/javax/sound/midi/MidiDeviceProvider/UnsupportedInfo.java b/jdk/test/javax/sound/midi/spi/MidiDeviceProvider/UnsupportedInfo.java similarity index 100% rename from jdk/test/javax/sound/midi/MidiDeviceProvider/UnsupportedInfo.java rename to jdk/test/javax/sound/midi/spi/MidiDeviceProvider/UnsupportedInfo.java diff --git a/jdk/test/javax/sound/midi/spi/MidiFileReader/ExpectedNPEOnNull.java b/jdk/test/javax/sound/midi/spi/MidiFileReader/ExpectedNPEOnNull.java new file mode 100644 index 00000000000..bb4b75f3302 --- /dev/null +++ b/jdk/test/javax/sound/midi/spi/MidiFileReader/ExpectedNPEOnNull.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.File; +import java.io.InputStream; +import java.net.URL; + +import javax.sound.midi.MidiSystem; +import javax.sound.midi.spi.MidiFileReader; + +import static java.util.ServiceLoader.load; + +/** + * @test + * @bug 8143909 + * @author Sergey Bylokhov + */ +public final class ExpectedNPEOnNull { + + public static void main(final String[] args) throws Exception { + testMS(); + for (final MidiFileReader mfr : load(MidiFileReader.class)) { + testMFR(mfr); + } + } + + /** + * Tests the part of MidiSystem API, which implemented via MidiFileReader. + */ + private static void testMS() throws Exception { + // MidiSystem#getMidiFileFormat(InputStream) + try { + MidiSystem.getMidiFileFormat((InputStream) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + // MidiSystem#getMidiFileFormat(URL) + try { + MidiSystem.getMidiFileFormat((URL) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + // MidiSystem#getMidiFileFormat(File) + try { + MidiSystem.getMidiFileFormat((File) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + // MidiSystem#getSequence(InputStream) + try { + MidiSystem.getSequence((InputStream) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + // MidiSystem#getSequence(URL) + try { + MidiSystem.getSequence((URL) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + // MidiSystem#getSequence(File) + try { + MidiSystem.getSequence((File) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + + /** + * Tests the MidiFileReader API directly. + */ + private static void testMFR(final MidiFileReader mfr) throws Exception { + // MidiFileReader#getMidiFileFormat(InputStream) + try { + mfr.getMidiFileFormat((InputStream) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + // MidiFileReader#getMidiFileFormat(URL) + try { + mfr.getMidiFileFormat((URL) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + // MidiFileReader#getMidiFileFormat(File) + try { + mfr.getMidiFileFormat((File) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + // MidiFileReader#getSequence(InputStream) + try { + mfr.getSequence((InputStream) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + // MidiFileReader#getSequence(URL) + try { + mfr.getSequence((URL) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + // MidiFileReader#getSequence(File) + try { + mfr.getSequence((File) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } +} diff --git a/jdk/test/javax/sound/midi/spi/MidiFileWriter/ExpectedNPEOnNull.java b/jdk/test/javax/sound/midi/spi/MidiFileWriter/ExpectedNPEOnNull.java new file mode 100644 index 00000000000..6a96dbe6b52 --- /dev/null +++ b/jdk/test/javax/sound/midi/spi/MidiFileWriter/ExpectedNPEOnNull.java @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.File; +import java.io.OutputStream; +import java.util.Objects; + +import javax.sound.midi.MidiSystem; +import javax.sound.midi.Sequence; +import javax.sound.midi.spi.MidiFileWriter; + +import static java.util.ServiceLoader.load; + +/** + * @test + * @bug 8143909 + * @author Sergey Bylokhov + */ +public final class ExpectedNPEOnNull { + + public static void main(final String[] args) throws Exception { + testMS(); + for (final MidiFileWriter mfw : load(MidiFileWriter.class)) { + testMFW(mfw); + } + testMFW(customMFW); + } + + /** + * Tests the part of MidiSystem API, which implemented via MidiFileWriter. + */ + private static void testMS() throws Exception { + // MidiSystem#getMidiFileTypes(Sequence) + try { + MidiSystem.getMidiFileTypes(null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + + // MidiSystem#isFileTypeSupported(int, Sequence) + for (final int type : MidiSystem.getMidiFileTypes()) { + try { + MidiSystem.isFileTypeSupported(type, null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + // MidiSystem#write(Sequence, int, OutputStream) + for (final int type : MidiSystem.getMidiFileTypes()) { + try { + MidiSystem.write(null, type, new NullOutputStream()); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + for (final int type : MidiSystem.getMidiFileTypes()) { + try { + MidiSystem.write(new Sequence(0, 0), type, (OutputStream) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + for (final int type : MidiSystem.getMidiFileTypes()) { + try { + MidiSystem.write(null, type, (OutputStream) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + // MidiSystem#write(Sequence, int, File) + for (final int type : MidiSystem.getMidiFileTypes()) { + try { + MidiSystem.write(null, type, new File("")); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + for (final int type : MidiSystem.getMidiFileTypes()) { + try { + MidiSystem.write(new Sequence(0, 0), type, (File) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + for (final int type : MidiSystem.getMidiFileTypes()) { + try { + MidiSystem.write(null, type, (File) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + } + + /** + * Tests the MidiFileWriter API directly. + */ + private static void testMFW(final MidiFileWriter mfw) throws Exception { + // MidiFileWriter#getMidiFileTypes(Sequence) + try { + mfw.getMidiFileTypes(null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + // MidiFileWriter#isFileTypeSupported(int, Sequence) + for (final int type : MidiSystem.getMidiFileTypes()) { + try { + mfw.isFileTypeSupported(type, null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + // MidiFileWriter#write(Sequence, int, OutputStream) + for (final int type : MidiSystem.getMidiFileTypes()) { + try { + mfw.write(null, type, new NullOutputStream()); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + for (final int type : MidiSystem.getMidiFileTypes()) { + try { + mfw.write(new Sequence(0, 0), type, (OutputStream) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + for (final int type : MidiSystem.getMidiFileTypes()) { + try { + mfw.write(null, type, (OutputStream) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + // MidiFileWriter#write(Sequence, int, File) + for (final int type : MidiSystem.getMidiFileTypes()) { + try { + mfw.write(null, type, new File("")); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + for (final int type : MidiSystem.getMidiFileTypes()) { + try { + mfw.write(new Sequence(0, 0), type, (File) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + for (final int type : MidiSystem.getMidiFileTypes()) { + try { + mfw.write(null, type, (File) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + } + /** + * Tests some default implementation of MidiFileWriter API, using the custom + * {@code MidiFileWriter}, which support nothing. + */ + static MidiFileWriter customMFW = new MidiFileWriter() { + @Override + public int[] getMidiFileTypes() { + return new int[0]; + } + + @Override + public int[] getMidiFileTypes(Sequence sequence) { + Objects.requireNonNull(sequence); + return new int[0]; + } + + @Override + public int write(Sequence in, int fileType, OutputStream out) { + Objects.requireNonNull(in); + Objects.requireNonNull(out); + return 0; + } + + @Override + public int write(Sequence in, int fileType, File out) { + Objects.requireNonNull(in); + Objects.requireNonNull(out); + return 0; + } + }; + + private static final class NullOutputStream extends OutputStream { + + @Override + public void write(final int b) { + //do nothing + } + } +} diff --git a/jdk/test/javax/sound/midi/spi/SoundbankReader/ExpectedNPEOnNull.java b/jdk/test/javax/sound/midi/spi/SoundbankReader/ExpectedNPEOnNull.java new file mode 100644 index 00000000000..6ce3742fc9e --- /dev/null +++ b/jdk/test/javax/sound/midi/spi/SoundbankReader/ExpectedNPEOnNull.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.File; +import java.io.InputStream; +import java.net.URL; + +import javax.sound.midi.MidiSystem; +import javax.sound.midi.spi.SoundbankReader; + +import static java.util.ServiceLoader.load; + +/** + * @test + * @bug 8143909 + * @author Sergey Bylokhov + */ +public final class ExpectedNPEOnNull { + + public static void main(final String[] args) throws Exception { + testMS(); + for (final SoundbankReader sbr : load(SoundbankReader.class)) { + testSBR(sbr); + } + } + + /** + * Tests the part of MidiSystem API, which implemented via SoundbankReader. + */ + private static void testMS() throws Exception { + // MidiSystem#getSoundbank(InputStream) + try { + MidiSystem.getSoundbank((InputStream) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + // MidiSystem#getSoundbank(URL) + try { + MidiSystem.getSoundbank((URL) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + // MidiSystem#getSoundbank(File) + try { + MidiSystem.getSoundbank((File) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } + + /** + * Tests the SoundbankReader API directly. + */ + private static void testSBR(final SoundbankReader sbr) throws Exception { + // SoundbankReader#getSoundbank(InputStream) + try { + sbr.getSoundbank((InputStream) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + // SoundbankReader#getSoundbank(URL) + try { + sbr.getSoundbank((URL) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + // SoundbankReader#getSoundbank(File) + try { + sbr.getSoundbank((File) null); + throw new RuntimeException("NPE is expected"); + } catch (final NullPointerException ignored) { + } + } +} From 7d2c07c70d55d4fc692f18cfec0c9c0634b26e0b Mon Sep 17 00:00:00 2001 From: Nadeesh TV Date: Wed, 9 Dec 2015 15:27:21 -0500 Subject: [PATCH 34/62] 8142936: Add java.time.Duration methods for days, hours, minutes, seconds, etc Reviewed-by: rriggs, scolebourne --- .../share/classes/java/time/Duration.java | 118 ++++++++++++++- .../java/time/tck/java/time/TCKDuration.java | 141 ++++++++++++++++++ 2 files changed, 254 insertions(+), 5 deletions(-) diff --git a/jdk/src/java.base/share/classes/java/time/Duration.java b/jdk/src/java.base/share/classes/java/time/Duration.java index 9bf25ba8c27..b7060a91ef4 100644 --- a/jdk/src/java.base/share/classes/java/time/Duration.java +++ b/jdk/src/java.base/share/classes/java/time/Duration.java @@ -61,6 +61,7 @@ */ package java.time; +import static java.time.LocalTime.MINUTES_PER_HOUR; import static java.time.LocalTime.NANOS_PER_SECOND; import static java.time.LocalTime.SECONDS_PER_DAY; import static java.time.LocalTime.SECONDS_PER_HOUR; @@ -973,7 +974,7 @@ public Duration multipliedBy(long multiplicand) { if (multiplicand == 1) { return this; } - return create(toSeconds().multiply(BigDecimal.valueOf(multiplicand))); + return create(toBigDecimalSeconds().multiply(BigDecimal.valueOf(multiplicand))); } /** @@ -992,7 +993,7 @@ public Duration dividedBy(long divisor) { if (divisor == 1) { return this; } - return create(toSeconds().divide(BigDecimal.valueOf(divisor), RoundingMode.DOWN)); + return create(toBigDecimalSeconds().divide(BigDecimal.valueOf(divisor), RoundingMode.DOWN)); } /** @@ -1001,7 +1002,7 @@ public Duration dividedBy(long divisor) { * * @return the total length of the duration in seconds, with a scale of 9, not null */ - private BigDecimal toSeconds() { + private BigDecimal toBigDecimalSeconds() { return BigDecimal.valueOf(seconds).add(BigDecimal.valueOf(nanos, 9)); } @@ -1167,6 +1168,19 @@ public long toMinutes() { return seconds / SECONDS_PER_MINUTE; } + /** + * Gets the number of seconds in this duration. + *

    + * This returns the total number of whole seconds in the duration. + *

    + * This instance is immutable and unaffected by this method call. + * + * @return the whole seconds part of the length of the duration, positive or negative + */ + public long toSeconds() { + return seconds; + } + /** * Converts this duration to the total length in milliseconds. *

    @@ -1201,6 +1215,100 @@ public long toNanos() { return totalNanos; } + /** + * Extracts the number of days in the duration. + *

    + * This returns the total number of days in the duration by dividing the + * number of seconds by 86400. + * This is based on the standard definition of a day as 24 hours. + *

    + * This instance is immutable and unaffected by this method call. + * + * @return the number of days in the duration, may be negative + */ + public long toDaysPart(){ + return seconds / SECONDS_PER_DAY; + } + + /** + * Extracts the number of hours part in the duration. + *

    + * This returns the number of remaining hours when dividing {@link #toHours} + * by hours in a day. + * This is based on the standard definition of a day as 24 hours. + *

    + * This instance is immutable and unaffected by this method call. + * + * @return the number of hours part in the duration, may be negative + */ + public int toHoursPart(){ + return (int) (toHours() % 24); + } + + /** + * Extracts the number of minutes part in the duration. + *

    + * This returns the number of remaining minutes when dividing {@link #toMinutes} + * by minutes in an hour. + * This is based on the standard definition of an hour as 60 minutes. + *

    + * This instance is immutable and unaffected by this method call. + * + * @return the number of minutes parts in the duration, may be negative + * may be negative + */ + public int toMinutesPart(){ + return (int) (toMinutes() % MINUTES_PER_HOUR); + } + + /** + * Extracts the number of seconds part in the duration. + *

    + * This returns the remaining seconds when dividing {@link #toSeconds} + * by seconds in a minute. + * This is based on the standard definition of a minute as 60 seconds. + *

    + * This instance is immutable and unaffected by this method call. + * + * @return the number of seconds parts in the duration, may be negative + */ + public int toSecondsPart(){ + return (int) (seconds % SECONDS_PER_MINUTE); + } + + /** + * Extracts the number of milliseconds part of the duration. + *

    + * This returns the milliseconds part by dividing the number of nanoseconds by 1,000,000. + * The length of the duration is stored using two fields - seconds and nanoseconds. + * The nanoseconds part is a value from 0 to 999,999,999 that is an adjustment to + * the length in seconds. + * The total duration is defined by calling {@link #getNano()} and {@link #getSeconds()}. + *

    + * This instance is immutable and unaffected by this method call. + * + * @return the number of milliseconds part of the duration. + */ + public int toMillisPart(){ + return nanos / 1000_000; + } + + /** + * Get the nanoseconds part within seconds of the duration. + *

    + * The length of the duration is stored using two fields - seconds and nanoseconds. + * The nanoseconds part is a value from 0 to 999,999,999 that is an adjustment to + * the length in seconds. + * The total duration is defined by calling {@link #getNano()} and {@link #getSeconds()}. + *

    + * This instance is immutable and unaffected by this method call. + * + * @return the nanoseconds within the second part of the length of the duration, from 0 to 999,999,999 + */ + public int toNanosPart(){ + return nanos; + } + //----------------------------------------------------------------------- /** * Compares this duration to the specified {@code Duration}. @@ -1208,7 +1316,7 @@ public long toNanos() { * The comparison is based on the total length of the durations. * It is "consistent with equals", as defined by {@link Comparable}. * - * @param otherDuration the other duration to compare to, not null + * @param otherDuration the other duration to compare to, not null * @return the comparator value, negative if less, positive if greater */ @Override @@ -1226,7 +1334,7 @@ public int compareTo(Duration otherDuration) { *

    * The comparison is based on the total length of the durations. * - * @param otherDuration the other duration, null returns false + * @param otherDuration the other duration, null returns false * @return true if the other duration is equal to this one */ @Override diff --git a/jdk/test/java/time/tck/java/time/TCKDuration.java b/jdk/test/java/time/tck/java/time/TCKDuration.java index fe6a9f98088..7f70916ac95 100644 --- a/jdk/test/java/time/tck/java/time/TCKDuration.java +++ b/jdk/test/java/time/tck/java/time/TCKDuration.java @@ -2474,6 +2474,147 @@ public void test_toMillis_tooBig() { test.toMillis(); } + //----------------------------------------------------------------------- + // toSeconds() + //----------------------------------------------------------------------- + @DataProvider(name="toSeconds_provider") + Object[][] provider_toSeconds() { + return new Object[][] { + {Duration.ofSeconds(365 * 86400 + 5 * 3600 + 48 * 60 + 46, 123_456_789), 31556926L}, + {Duration.ofSeconds(-365 * 86400 - 5 * 3600 - 48 * 60 - 46, -123_456_789), -31556927L}, + {Duration.ofSeconds(-365 * 86400 - 5 * 3600 - 48 * 60 - 46, 123_456_789), -31556926L}, + {Duration.ofSeconds(0), 0L}, + {Duration.ofSeconds(0, 123_456_789), 0L}, + {Duration.ofSeconds(0, -123_456_789), -1L}, + {Duration.ofSeconds(Long.MAX_VALUE), 9223372036854775807L}, + {Duration.ofSeconds(Long.MIN_VALUE), -9223372036854775808L}, + }; + } + + @Test(dataProvider="toSeconds_provider") + public void test_toSeconds(Duration dur, long seconds) { + assertEquals(dur.toSeconds(), seconds); + } + + //----------------------------------------------------------------------- + // toDaysPart() + //----------------------------------------------------------------------- + @DataProvider(name="toDaysPart_provider") + Object[][] provider_toDaysPart() { + return new Object[][] { + {Duration.ofSeconds(365 * 86400 + 5 * 3600 + 48 * 60 + 46, 123_456_789), 365L}, + {Duration.ofSeconds(-365 * 86400 - 5 * 3600 - 48 * 60 - 46, -123_456_789), -365L}, + {Duration.ofSeconds(5 * 3600 + 48 * 60 + 46, 123_456_789), 0L}, + {Duration.ofDays(365), 365L}, + {Duration.ofHours(2), 0L}, + {Duration.ofHours(-2), 0L}, + }; + } + + @Test(dataProvider="toDaysPart_provider") + public void test_toDaysPart(Duration dur, long days) { + assertEquals(dur.toDaysPart(), days); + } + + //----------------------------------------------------------------------- + // toHoursPart() + //----------------------------------------------------------------------- + @DataProvider(name="toHoursPart_provider") + Object[][] provider_toHoursPart() { + return new Object[][] { + {Duration.ofSeconds(365 * 86400 + 5 * 3600 + 48 * 60 + 46, 123_456_789), 5}, + {Duration.ofSeconds(-365 * 86400 - 5 * 3600 - 48 * 60 - 46, -123_456_789), -5}, + {Duration.ofSeconds(48 * 60 + 46, 123_456_789), 0}, + {Duration.ofHours(2), 2}, + {Duration.ofHours(-2), -2}, + }; + } + + @Test(dataProvider="toHoursPart_provider") + public void test_toHoursPart(Duration dur, int hours) { + assertEquals(dur.toHoursPart(), hours); + } + + //----------------------------------------------------------------------- + // toMinutesPart() + //----------------------------------------------------------------------- + @DataProvider(name="toMinutesPart_provider") + Object[][] provider_toMinutesPart() { + return new Object[][] { + {Duration.ofSeconds(365 * 86400 + 5 * 3600 + 48 * 60 + 46, 123_456_789), 48}, + {Duration.ofSeconds(-365 * 86400 - 5 * 3600 - 48 * 60 - 46, -123_456_789), -48}, + {Duration.ofSeconds(46, 123_456_789),0}, + {Duration.ofMinutes(48), 48}, + {Duration.ofHours(2), 0}, + {Duration.ofHours(-2),0}, + }; + } + + @Test(dataProvider="toMinutesPart_provider") + public void test_toMinutesPart(Duration dur, int minutes) { + assertEquals(dur.toMinutesPart(), minutes); + } + + //----------------------------------------------------------------------- + // toSecondsPart() + //----------------------------------------------------------------------- + @DataProvider(name="toSecondsPart_provider") + Object[][] provider_toSecondsPart() { + return new Object[][] { + {Duration.ofSeconds(365 * 86400 + 5 * 3600 + 48 * 60 + 46, 123_456_789), 46}, + {Duration.ofSeconds(-365 * 86400 - 5 * 3600 - 48 * 60 - 46, -123_456_789), -47}, + {Duration.ofSeconds(0, 123_456_789), 0}, + {Duration.ofSeconds(46), 46}, + {Duration.ofHours(2), 0}, + {Duration.ofHours(-2), 0}, + }; + } + + @Test(dataProvider="toSecondsPart_provider") + public void test_toSecondsPart(Duration dur, int seconds) { + assertEquals(dur.toSecondsPart(), seconds); + } + + //----------------------------------------------------------------------- + // toMillisPart() + //----------------------------------------------------------------------- + @DataProvider(name="toMillisPart_provider") + Object[][] provider_toMillisPart() { + return new Object[][] { + {Duration.ofSeconds(365 * 86400 + 5 * 3600 + 48 * 60 + 46, 123_456_789), 123}, + {Duration.ofSeconds(-365 * 86400 - 5 * 3600 - 48 * 60 - 46, -123_456_789), 876}, + {Duration.ofSeconds(5 * 3600 + 48 * 60 + 46, 0), 0}, + {Duration.ofMillis(123), 123}, + {Duration.ofHours(2), 0}, + {Duration.ofHours(-2), 0}, + }; + } + + @Test(dataProvider="toMillisPart_provider") + public void test_toMillisPart(Duration dur, int millis) { + assertEquals(dur.toMillisPart(), millis); + } + + //----------------------------------------------------------------------- + // toNanosPart() + //----------------------------------------------------------------------- + @DataProvider(name="toNanosPart_provider") + Object[][] provider_toNanosPart() { + return new Object[][] { + {Duration.ofSeconds(365 * 86400 + 5 * 3600 + 48 * 60 + 46, 123_456_789), 123_456_789}, + {Duration.ofSeconds(-365 * 86400 - 5 * 3600 - 48 * 60 - 46, -123_456_789), 876_543_211}, + {Duration.ofSeconds(5 * 3600 + 48 * 60 + 46, 0), 0}, + {Duration.ofNanos(123_456_789), 123_456_789}, + {Duration.ofHours(2), 0}, + {Duration.ofHours(-2), 0}, + }; + } + + @Test(dataProvider="toNanosPart_provider") + public void test_toNanosPart(Duration dur, int nanos) { + assertEquals(dur.toNanosPart(), nanos); + } + //----------------------------------------------------------------------- // compareTo() //----------------------------------------------------------------------- From dcf0684317b79872294bc77c3a668ba237e30d49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laurent=20Bourg=C3=A8s?= Date: Wed, 9 Dec 2015 13:59:45 -0800 Subject: [PATCH 35/62] 8144828: Marlin renderer causes unaligned write accesses Reviewed-by: prr, flar --- .../sun/java2d/marlin/MarlinCache.java | 25 ++++++++++++++----- .../sun/java2d/marlin/MarlinConst.java | 2 ++ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinCache.java b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinCache.java index a281b4e5ecf..9aa35132772 100644 --- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinCache.java +++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinCache.java @@ -156,8 +156,6 @@ void init(int minx, int miny, int maxx, int maxy, int edgeSumDeltaY) // rewritten to avoid division: || (width * heightSubPixel) > ((edgeSumDeltaY - heightSubPixel) << BLOCK_SIZE_LG); -// ((edgeSumDeltaY - heightSubPixel) * RLE_THRESHOLD); -// ((edgeSumDeltaY - heightSubPixel) << BLOCK_TH_LG); if (doTrace && !useRLE) { final float meanCrossings @@ -293,8 +291,10 @@ void copyAARowNoRLE(final int[] alphaRow, final int y, // update row index to current position: rowAAChunkIndex[row] = pos; - // determine need array size (may overflow): - final long needSize = pos + (px_bbox1 - px0); + // determine need array size: + // for RLE encoding, position must be aligned to 4 bytes (int): + // align - 1 = 3 so add +3 and round-off by mask ~3 = -4 + final long needSize = pos + ((px_bbox1 - px0 + 3) & -4); // update next position (bytes): rowAAChunkPos = needSize; @@ -401,8 +401,7 @@ void copyAARowRLE_WithBlockFlags(final int[] blkFlags, final int[] alphaRow, // determine need array size: // pessimistic: max needed size = deltaX x 4 (1 int) - final int maxLen = (to - from); - final long needSize = initialPos + (maxLen << 2); + final long needSize = initialPos + ((to - from) << 2); // update row data: OffHeapArray _rowAAChunk = rowAAChunk; @@ -465,6 +464,13 @@ void copyAARowRLE_WithBlockFlags(final int[] blkFlags, final int[] alphaRow, // note: last pixel exclusive (>= 0) // note: it should check X is smaller than 23bits (overflow)! + // check address alignment to 4 bytes: + if (doCheckUnsafe) { + if ((addr_off & 3) != 0) { + MarlinUtils.logInfo("Misaligned Unsafe address: " + addr_off); + } + } + // special case to encode entries into a single int: if (val == 0) { _unsafe.putInt(addr_off, @@ -521,6 +527,13 @@ void copyAARowRLE_WithBlockFlags(final int[] blkFlags, final int[] alphaRow, // note: last pixel exclusive (>= 0) // note: it should check X is smaller than 23bits (overflow)! + // check address alignment to 4 bytes: + if (doCheckUnsafe) { + if ((addr_off & 3) != 0) { + MarlinUtils.logInfo("Misaligned Unsafe address: " + addr_off); + } + } + // special case to encode entries into a single int: if (val == 0) { _unsafe.putInt(addr_off, diff --git a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinConst.java b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinConst.java index 6ff24a04b64..e799504318d 100644 --- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinConst.java +++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinConst.java @@ -40,6 +40,8 @@ interface MarlinConst { // log misc.Unsafe alloc/realloc/free static final boolean logUnsafeMalloc = enableLogs && MarlinProperties.isLogUnsafeMalloc(); + // do check unsafe alignment: + static final boolean doCheckUnsafe = false; // do statistics static final boolean doStats = enableLogs && MarlinProperties.isDoStats(); From 5999226f522df00e970dbc84a94f11df0350f605 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Wed, 9 Dec 2015 15:20:39 -0800 Subject: [PATCH 36/62] 8137106: EUDC (End User Defined Characters) are not displayed on Windows with Java 8u60+ Reviewed-by: serb, jgodinez --- .../share/classes/sun/font/TrueTypeFont.java | 33 ++++++++++++++----- .../classes/sun/awt/Win32FontManager.java | 2 +- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/jdk/src/java.desktop/share/classes/sun/font/TrueTypeFont.java b/jdk/src/java.desktop/share/classes/sun/font/TrueTypeFont.java index c4893b6dd60..7668b6572a8 100644 --- a/jdk/src/java.desktop/share/classes/sun/font/TrueTypeFont.java +++ b/jdk/src/java.desktop/share/classes/sun/font/TrueTypeFont.java @@ -176,6 +176,13 @@ public synchronized void dispose() { private String localeFamilyName; private String localeFullName; + public TrueTypeFont(String platname, Object nativeNames, int fIndex, + boolean javaRasterizer) + throws FontFormatException + { + this(platname, nativeNames, fIndex, javaRasterizer, true); + } + /** * - does basic verification of the file * - reads the header table for this font (within a collection) @@ -186,14 +193,17 @@ public synchronized void dispose() { * or fails verification, or there's no usable cmap */ public TrueTypeFont(String platname, Object nativeNames, int fIndex, - boolean javaRasterizer) + boolean javaRasterizer, boolean useFilePool) throws FontFormatException { super(platname, nativeNames); useJavaRasterizer = javaRasterizer; fontRank = Font2D.TTF_RANK; try { - verify(); + verify(useFilePool); init(fIndex); + if (!useFilePool) { + close(); + } } catch (Throwable t) { close(); if (t instanceof FontFormatException) { @@ -280,6 +290,10 @@ protected boolean checkUseNatives() { } + private synchronized FileChannel open() throws FontFormatException { + return open(true); + } + /* This is intended to be called, and the returned value used, * from within a block synchronized on this font object. * ie the channel returned may be nulled out at any time by "close()" @@ -287,7 +301,8 @@ protected boolean checkUseNatives() { * Deadlock warning: FontManager.addToPool(..) acquires a global lock, * which means nested locks may be in effect. */ - private synchronized FileChannel open() throws FontFormatException { + private synchronized FileChannel open(boolean usePool) + throws FontFormatException { if (disposerRecord.channel == null) { if (FontUtilities.isLogging()) { FontUtilities.getLogger().info("open TTF: " + platName); @@ -306,9 +321,11 @@ public Object run() { }); disposerRecord.channel = raf.getChannel(); fileSize = (int)disposerRecord.channel.size(); - FontManager fm = FontManagerFactory.getInstance(); - if (fm instanceof SunFontManager) { - ((SunFontManager) fm).addToPool(this); + if (usePool) { + FontManager fm = FontManagerFactory.getInstance(); + if (fm instanceof SunFontManager) { + ((SunFontManager) fm).addToPool(this); + } } } catch (NullPointerException e) { close(); @@ -492,8 +509,8 @@ byte[] readBytes(int offset, int length) { } } - private void verify() throws FontFormatException { - open(); + private void verify(boolean usePool) throws FontFormatException { + open(usePool); } /* sizes, in bytes, of TT/TTC header records */ diff --git a/jdk/src/java.desktop/windows/classes/sun/awt/Win32FontManager.java b/jdk/src/java.desktop/windows/classes/sun/awt/Win32FontManager.java index ee50f24a293..815393eaa1c 100644 --- a/jdk/src/java.desktop/windows/classes/sun/awt/Win32FontManager.java +++ b/jdk/src/java.desktop/windows/classes/sun/awt/Win32FontManager.java @@ -61,7 +61,7 @@ public Object run() { * enumerate (allow direct use) of EUDC fonts. */ eudcFont = new TrueTypeFont(eudcFile, null, 0, - true); + true, false); } catch (FontFormatException e) { } } From e03cf9daf6d006a077fa3ee30c5bac5c6e9d91bf Mon Sep 17 00:00:00 2001 From: Xue-Lei Andrew Fan Date: Thu, 10 Dec 2015 06:09:36 +0000 Subject: [PATCH 37/62] 8136410: AlgorithmDecomposer is not parsing padding correctly Reviewed-by: weijun --- .../security/util/AlgorithmDecomposer.java | 4 +- .../DecomposeAlgorithms.java | 71 +++++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 jdk/test/sun/security/util/AlgorithmConstraints/DecomposeAlgorithms.java diff --git a/jdk/src/java.base/share/classes/sun/security/util/AlgorithmDecomposer.java b/jdk/src/java.base/share/classes/sun/security/util/AlgorithmDecomposer.java index 82e61cbb06b..4410a3102be 100644 --- a/jdk/src/java.base/share/classes/sun/security/util/AlgorithmDecomposer.java +++ b/jdk/src/java.base/share/classes/sun/security/util/AlgorithmDecomposer.java @@ -35,8 +35,10 @@ public class AlgorithmDecomposer { private static final Pattern transPattern = Pattern.compile("/"); + + // '(? parsed = parser.decompose(fullAlgName); + if (parsed.size() != components.length) { + throw new Exception("Not expected components number: " + parsed); + } + + for (String component : components) { + if (!parsed.contains(component)) { + throw new Exception("Not a expected component: " + component); + } + } + + System.out.println("OK: " + fullAlgName); + } +} From edabf4afb36e8fd400f31742aac920a6b2528220 Mon Sep 17 00:00:00 2001 From: Chris Hegarty Date: Thu, 10 Dec 2015 10:04:41 +0000 Subject: [PATCH 38/62] 8145082: Remove sun.misc.Unsafe dependency from sun.nio.cs.StringUTF16 Reviewed-by: psandoz, sherman --- jdk/src/java.base/share/classes/sun/nio/cs/StringUTF16.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jdk/src/java.base/share/classes/sun/nio/cs/StringUTF16.java b/jdk/src/java.base/share/classes/sun/nio/cs/StringUTF16.java index f1608cab5d5..13e1d8cca5a 100644 --- a/jdk/src/java.base/share/classes/sun/nio/cs/StringUTF16.java +++ b/jdk/src/java.base/share/classes/sun/nio/cs/StringUTF16.java @@ -25,8 +25,8 @@ package sun.nio.cs; -import static sun.misc.Unsafe.ARRAY_BYTE_BASE_OFFSET; -import static sun.misc.Unsafe.ARRAY_BYTE_INDEX_SCALE; +import static jdk.internal.misc.Unsafe.ARRAY_BYTE_BASE_OFFSET; +import static jdk.internal.misc.Unsafe.ARRAY_BYTE_INDEX_SCALE; class StringUTF16 { @@ -35,5 +35,5 @@ public static char getChar(byte[] val, int index) { ARRAY_BYTE_BASE_OFFSET + ARRAY_BYTE_INDEX_SCALE * index * 2L); } - private static final sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe(); + private static final jdk.internal.misc.Unsafe unsafe = jdk.internal.misc.Unsafe.getUnsafe(); } From 7d9d3a469949a3874f35ebacb140e0aaf49c12f0 Mon Sep 17 00:00:00 2001 From: Avik Niyogi Date: Thu, 10 Dec 2015 14:21:44 +0300 Subject: [PATCH 39/62] 8139169: [macosx] Action registered for keyboard shortcut is called twice Reviewed-by: serb, alexsch --- .../native/libawt_lwawt/awt/CMenuItem.m | 300 ++++++++++-------- .../8139169/ScreenMenuBarInputTwice.java | 234 ++++++++++++++ 2 files changed, 405 insertions(+), 129 deletions(-) create mode 100644 jdk/test/javax/swing/JMenuItem/8139169/ScreenMenuBarInputTwice.java diff --git a/jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/CMenuItem.m b/jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/CMenuItem.m index 8e483468074..28e4f386d78 100644 --- a/jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/CMenuItem.m +++ b/jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/CMenuItem.m @@ -24,6 +24,7 @@ */ #import +#include #import "CMenuItem.h" #import "CMenu.h" @@ -40,7 +41,7 @@ @implementation CMenuItem - (id) initWithPeer:(jobject)peer asSeparator: (NSNumber *) asSeparator{ -AWT_ASSERT_APPKIT_THREAD; + AWT_ASSERT_APPKIT_THREAD; self = [super initWithPeer:peer]; if (self) { if ([asSeparator boolValue]) { @@ -63,13 +64,48 @@ - (id) initWithPeer:(jobject)peer asSeparator: (NSNumber *) asSeparator{ - (BOOL) worksWhenModal { return YES; } +// This is a method written using Carbon framework methods to remove +// All modifiers including "Shift" modifier. +// Example 1: Shortcut set is "Command Shift m" returns "m" +// Example 2: Shortcut set is "Command m" returns "m" +// Example 3: Shortcut set is "Alt Shift ," returns "," + +CFStringRef createStringForKey(CGKeyCode keyCode) +{ + TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource(); +// currentKeyboard now contains the current input source + CFDataRef layoutData = + TISGetInputSourceProperty(currentKeyboard, + kTISPropertyUnicodeKeyLayoutData); +// the UNICODE keyLayout is fetched from currentKeyboard in layoutData + const UCKeyboardLayout *keyboardLayout = + (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData); +// A read-only data pointer is fetched from layoutData + UInt32 keysDown = 0; + UniChar chars[4]; + UniCharCount realLength; + + UCKeyTranslate(keyboardLayout, + keyCode, + kUCKeyActionDisplay, + 0, + LMGetKbdType(), + kUCKeyTranslateNoDeadKeysBit, + &keysDown, + sizeof(chars) / sizeof(chars[0]), + &realLength, + chars); + CFRelease(currentKeyboard); +// Converts keyCode, modifier and dead-key state into UNICODE characters + return CFStringCreateWithCharacters(kCFAllocatorDefault, chars, 1); +} // Events - (void)handleAction:(NSMenuItem *)sender { -AWT_ASSERT_APPKIT_THREAD; + AWT_ASSERT_APPKIT_THREAD; JNIEnv *env = [ThreadUtilities getJNIEnv]; -JNF_COCOA_ENTER(env); - + JNF_COCOA_ENTER(env); + // If we are called as a result of user pressing a shortcut, do nothing, // because AVTView has already sent corresponding key event to the Java // layer from performKeyEquivalent. @@ -82,31 +118,37 @@ - (void)handleAction:(NSMenuItem *)sender { NSEvent *currEvent = [[NSApplication sharedApplication] currentEvent]; if ([currEvent type] == NSKeyDown) { NSString *menuKey = [sender keyEquivalent]; - NSString *eventKey = [currEvent charactersIgnoringModifiers]; - - // Apple uses characters from private Unicode range for some of the - // keys, so we need to do the same translation here that we do - // for the regular key down events - if ([eventKey length] == 1) { - unichar origChar = [eventKey characterAtIndex:0]; - unichar newChar = NsCharToJavaChar(origChar, 0); - if (newChar == java_awt_event_KeyEvent_CHAR_UNDEFINED) { - newChar = origChar; - } - - eventKey = [NSString stringWithCharacters: &newChar length: 1]; - } - +// If shortcut is "Command Shift ," the menuKey gets the value "," +// But [currEvent charactersIgnoringModifiers]; returns "<" and not "," +// because the charactersIgnoreingModifiers does not ignore "Shift" +// So a shortcut like "Command Shift m" will return "M" where as the +// MenuKey will have the value "m". To remove this issue the below +// createStringForKey is used. + NSString *eventKey = createStringForKey([currEvent keyCode]); + +// Apple uses characters from private Unicode range for some of the +// keys, so we need to do the same translation here that we do +// for the regular key down events + if ([eventKey length] == 1) { + unichar origChar = [eventKey characterAtIndex:0]; + unichar newChar = NsCharToJavaChar(origChar, 0); + if (newChar == java_awt_event_KeyEvent_CHAR_UNDEFINED) { + newChar = origChar; + } + + eventKey = [NSString stringWithCharacters: &newChar length: 1]; + } + NSWindow *keyWindow = [NSApp keyWindow]; if ([menuKey isEqualToString:eventKey] && keyWindow != nil) { return; } } - + if (fIsCheckbox) { static JNF_CLASS_CACHE(jc_CCheckboxMenuItem, "sun/lwawt/macosx/CCheckboxMenuItem"); static JNF_MEMBER_CACHE(jm_ckHandleAction, jc_CCheckboxMenuItem, "handleAction", "(Z)V"); - + // Send the opposite of what's currently checked -- the action // indicates what state we're going to. NSInteger state = [sender state]; @@ -115,26 +157,26 @@ - (void)handleAction:(NSMenuItem *)sender { } else { static JNF_CLASS_CACHE(jc_CMenuItem, "sun/lwawt/macosx/CMenuItem"); static JNF_MEMBER_CACHE(jm_handleAction, jc_CMenuItem, "handleAction", "(JI)V"); // AWT_THREADING Safe (event) - + NSUInteger modifiers = [currEvent modifierFlags]; jint javaModifiers = NsKeyModifiersToJavaModifiers(modifiers, NO); - + JNFCallVoidMethod(env, fPeer, jm_handleAction, UTC(currEvent), javaModifiers); // AWT_THREADING Safe (event) } -JNF_COCOA_EXIT(env); + JNF_COCOA_EXIT(env); } - (void) setJavaLabel:(NSString *)theLabel shortcut:(NSString *)theKeyEquivalent modifierMask:(jint)modifiers { - + NSUInteger modifierMask = 0; - + if (![theKeyEquivalent isEqualToString:@""]) { // Force the key equivalent to lower case if not using the shift key. // Otherwise AppKit will draw a Shift glyph in the menu. if ((modifiers & java_awt_event_KeyEvent_SHIFT_MASK) == 0) { theKeyEquivalent = [theKeyEquivalent lowercaseString]; } - + // Hack for the question mark -- SHIFT and / means use the question mark. if ((modifiers & java_awt_event_KeyEvent_SHIFT_MASK) != 0 && [theKeyEquivalent isEqualToString:@"/"]) @@ -142,10 +184,10 @@ - (void) setJavaLabel:(NSString *)theLabel shortcut:(NSString *)theKeyEquivalent theKeyEquivalent = @"?"; modifiers &= ~java_awt_event_KeyEvent_SHIFT_MASK; } - + modifierMask = JavaModifiersToNsKeyModifiers(modifiers, NO); } - + [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ [fMenuItem setKeyEquivalent:theKeyEquivalent]; [fMenuItem setKeyEquivalentModifierMask:modifierMask]; @@ -154,14 +196,14 @@ - (void) setJavaLabel:(NSString *)theLabel shortcut:(NSString *)theKeyEquivalent } - (void) setJavaImage:(NSImage *)theImage { - + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ [fMenuItem setImage:theImage]; }]; } - (void) setJavaToolTipText:(NSString *)theText { - + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ [fMenuItem setToolTip:theText]; }]; @@ -169,11 +211,11 @@ - (void) setJavaToolTipText:(NSString *)theText { - (void)setJavaEnabled:(BOOL) enabled { - + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ @synchronized(self) { fIsEnabled = enabled; - + // Warning: This won't work if the parent menu is disabled. // See [CMenu syncFromJava]. We still need to call it here so // the NSMenuItem itself gets properly updated. @@ -183,7 +225,7 @@ - (void)setJavaEnabled:(BOOL) enabled { } - (BOOL)isEnabled { - + BOOL enabled = NO; @synchronized(self) { enabled = fIsEnabled; @@ -193,7 +235,7 @@ - (BOOL)isEnabled { - (void)setJavaState:(BOOL)newState { - + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ [fMenuItem setState:(newState ? NSOnState : NSOffState)]; }]; @@ -207,7 +249,7 @@ - (void)cleanup { - (void)dealloc { [fMenuItem release]; fMenuItem = nil; - + [super dealloc]; } @@ -240,7 +282,7 @@ - (NSString *)description { /** Convert a Java keycode for SetMenuItemCmd */ static unichar AWTKeyToMacShortcut(jint awtKey, BOOL doShift) { unichar macKey = 0; - + if ((awtKey >= java_awt_event_KeyEvent_VK_0 && awtKey <= java_awt_event_KeyEvent_VK_9) || (awtKey >= java_awt_event_KeyEvent_VK_A && awtKey <= java_awt_event_KeyEvent_VK_Z)) { @@ -255,68 +297,68 @@ static unichar AWTKeyToMacShortcut(jint awtKey, BOOL doShift) { } else { // Special characters switch (awtKey) { - case java_awt_event_KeyEvent_VK_BACK_QUOTE : macKey = '`'; break; - case java_awt_event_KeyEvent_VK_QUOTE : macKey = '\''; break; - - case java_awt_event_KeyEvent_VK_ESCAPE : macKey = 0x1B; break; - case java_awt_event_KeyEvent_VK_SPACE : macKey = ' '; break; - case java_awt_event_KeyEvent_VK_PAGE_UP : macKey = NSPageUpFunctionKey; break; - case java_awt_event_KeyEvent_VK_PAGE_DOWN : macKey = NSPageDownFunctionKey; break; - case java_awt_event_KeyEvent_VK_END : macKey = NSEndFunctionKey; break; - case java_awt_event_KeyEvent_VK_HOME : macKey = NSHomeFunctionKey; break; - - case java_awt_event_KeyEvent_VK_LEFT : macKey = NSLeftArrowFunctionKey; break; - case java_awt_event_KeyEvent_VK_UP : macKey = NSUpArrowFunctionKey; break; - case java_awt_event_KeyEvent_VK_RIGHT : macKey = NSRightArrowFunctionKey; break; - case java_awt_event_KeyEvent_VK_DOWN : macKey = NSDownArrowFunctionKey; break; - - case java_awt_event_KeyEvent_VK_COMMA : macKey = ','; break; - - // Mac OS doesn't distinguish between the two '-' keys... - case java_awt_event_KeyEvent_VK_MINUS : - case java_awt_event_KeyEvent_VK_SUBTRACT : macKey = '-'; break; - - // or the two '.' keys... - case java_awt_event_KeyEvent_VK_DECIMAL : - case java_awt_event_KeyEvent_VK_PERIOD : macKey = '.'; break; - - // or the two '/' keys. - case java_awt_event_KeyEvent_VK_DIVIDE : - case java_awt_event_KeyEvent_VK_SLASH : macKey = '/'; break; - - case java_awt_event_KeyEvent_VK_SEMICOLON : macKey = ';'; break; - case java_awt_event_KeyEvent_VK_EQUALS : macKey = '='; break; - - case java_awt_event_KeyEvent_VK_OPEN_BRACKET : macKey = '['; break; - case java_awt_event_KeyEvent_VK_BACK_SLASH : macKey = '\\'; break; - case java_awt_event_KeyEvent_VK_CLOSE_BRACKET : macKey = ']'; break; - - case java_awt_event_KeyEvent_VK_MULTIPLY : macKey = '*'; break; - case java_awt_event_KeyEvent_VK_ADD : macKey = '+'; break; - - case java_awt_event_KeyEvent_VK_HELP : macKey = NSHelpFunctionKey; break; - case java_awt_event_KeyEvent_VK_TAB : macKey = NSTabCharacter; break; - case java_awt_event_KeyEvent_VK_ENTER : macKey = NSNewlineCharacter; break; - case java_awt_event_KeyEvent_VK_BACK_SPACE : macKey = NSBackspaceCharacter; break; - case java_awt_event_KeyEvent_VK_DELETE : macKey = NSDeleteCharacter; break; - case java_awt_event_KeyEvent_VK_CLEAR : macKey = NSClearDisplayFunctionKey; break; - case java_awt_event_KeyEvent_VK_AMPERSAND : macKey = '&'; break; - case java_awt_event_KeyEvent_VK_ASTERISK : macKey = '*'; break; - case java_awt_event_KeyEvent_VK_QUOTEDBL : macKey = '\"'; break; - case java_awt_event_KeyEvent_VK_LESS : macKey = '<'; break; - case java_awt_event_KeyEvent_VK_GREATER : macKey = '>'; break; - case java_awt_event_KeyEvent_VK_BRACELEFT : macKey = '{'; break; - case java_awt_event_KeyEvent_VK_BRACERIGHT : macKey = '}'; break; - case java_awt_event_KeyEvent_VK_AT : macKey = '@'; break; - case java_awt_event_KeyEvent_VK_COLON : macKey = ':'; break; - case java_awt_event_KeyEvent_VK_CIRCUMFLEX : macKey = '^'; break; - case java_awt_event_KeyEvent_VK_DOLLAR : macKey = '$'; break; - case java_awt_event_KeyEvent_VK_EXCLAMATION_MARK : macKey = '!'; break; - case java_awt_event_KeyEvent_VK_LEFT_PARENTHESIS : macKey = '('; break; - case java_awt_event_KeyEvent_VK_NUMBER_SIGN : macKey = '#'; break; - case java_awt_event_KeyEvent_VK_PLUS : macKey = '+'; break; - case java_awt_event_KeyEvent_VK_RIGHT_PARENTHESIS: macKey = ')'; break; - case java_awt_event_KeyEvent_VK_UNDERSCORE : macKey = '_'; break; + case java_awt_event_KeyEvent_VK_BACK_QUOTE : macKey = '`'; break; + case java_awt_event_KeyEvent_VK_QUOTE : macKey = '\''; break; + + case java_awt_event_KeyEvent_VK_ESCAPE : macKey = 0x1B; break; + case java_awt_event_KeyEvent_VK_SPACE : macKey = ' '; break; + case java_awt_event_KeyEvent_VK_PAGE_UP : macKey = NSPageUpFunctionKey; break; + case java_awt_event_KeyEvent_VK_PAGE_DOWN : macKey = NSPageDownFunctionKey; break; + case java_awt_event_KeyEvent_VK_END : macKey = NSEndFunctionKey; break; + case java_awt_event_KeyEvent_VK_HOME : macKey = NSHomeFunctionKey; break; + + case java_awt_event_KeyEvent_VK_LEFT : macKey = NSLeftArrowFunctionKey; break; + case java_awt_event_KeyEvent_VK_UP : macKey = NSUpArrowFunctionKey; break; + case java_awt_event_KeyEvent_VK_RIGHT : macKey = NSRightArrowFunctionKey; break; + case java_awt_event_KeyEvent_VK_DOWN : macKey = NSDownArrowFunctionKey; break; + + case java_awt_event_KeyEvent_VK_COMMA : macKey = ','; break; + + // Mac OS doesn't distinguish between the two '-' keys... + case java_awt_event_KeyEvent_VK_MINUS : + case java_awt_event_KeyEvent_VK_SUBTRACT : macKey = '-'; break; + + // or the two '.' keys... + case java_awt_event_KeyEvent_VK_DECIMAL : + case java_awt_event_KeyEvent_VK_PERIOD : macKey = '.'; break; + + // or the two '/' keys. + case java_awt_event_KeyEvent_VK_DIVIDE : + case java_awt_event_KeyEvent_VK_SLASH : macKey = '/'; break; + + case java_awt_event_KeyEvent_VK_SEMICOLON : macKey = ';'; break; + case java_awt_event_KeyEvent_VK_EQUALS : macKey = '='; break; + + case java_awt_event_KeyEvent_VK_OPEN_BRACKET : macKey = '['; break; + case java_awt_event_KeyEvent_VK_BACK_SLASH : macKey = '\\'; break; + case java_awt_event_KeyEvent_VK_CLOSE_BRACKET : macKey = ']'; break; + + case java_awt_event_KeyEvent_VK_MULTIPLY : macKey = '*'; break; + case java_awt_event_KeyEvent_VK_ADD : macKey = '+'; break; + + case java_awt_event_KeyEvent_VK_HELP : macKey = NSHelpFunctionKey; break; + case java_awt_event_KeyEvent_VK_TAB : macKey = NSTabCharacter; break; + case java_awt_event_KeyEvent_VK_ENTER : macKey = NSNewlineCharacter; break; + case java_awt_event_KeyEvent_VK_BACK_SPACE : macKey = NSBackspaceCharacter; break; + case java_awt_event_KeyEvent_VK_DELETE : macKey = NSDeleteCharacter; break; + case java_awt_event_KeyEvent_VK_CLEAR : macKey = NSClearDisplayFunctionKey; break; + case java_awt_event_KeyEvent_VK_AMPERSAND : macKey = '&'; break; + case java_awt_event_KeyEvent_VK_ASTERISK : macKey = '*'; break; + case java_awt_event_KeyEvent_VK_QUOTEDBL : macKey = '\"'; break; + case java_awt_event_KeyEvent_VK_LESS : macKey = '<'; break; + case java_awt_event_KeyEvent_VK_GREATER : macKey = '>'; break; + case java_awt_event_KeyEvent_VK_BRACELEFT : macKey = '{'; break; + case java_awt_event_KeyEvent_VK_BRACERIGHT : macKey = '}'; break; + case java_awt_event_KeyEvent_VK_AT : macKey = '@'; break; + case java_awt_event_KeyEvent_VK_COLON : macKey = ':'; break; + case java_awt_event_KeyEvent_VK_CIRCUMFLEX : macKey = '^'; break; + case java_awt_event_KeyEvent_VK_DOLLAR : macKey = '$'; break; + case java_awt_event_KeyEvent_VK_EXCLAMATION_MARK : macKey = '!'; break; + case java_awt_event_KeyEvent_VK_LEFT_PARENTHESIS : macKey = '('; break; + case java_awt_event_KeyEvent_VK_NUMBER_SIGN : macKey = '#'; break; + case java_awt_event_KeyEvent_VK_PLUS : macKey = '+'; break; + case java_awt_event_KeyEvent_VK_RIGHT_PARENTHESIS: macKey = ')'; break; + case java_awt_event_KeyEvent_VK_UNDERSCORE : macKey = '_'; break; } } return macKey; @@ -330,27 +372,27 @@ static unichar AWTKeyToMacShortcut(jint awtKey, BOOL doShift) { JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CMenuItem_nativeSetLabel (JNIEnv *env, jobject peer, - jlong menuItemObj, jstring label, - jchar shortcutKey, jint shortcutKeyCode, jint mods) + jlong menuItemObj, jstring label, + jchar shortcutKey, jint shortcutKeyCode, jint mods) { -JNF_COCOA_ENTER(env); + JNF_COCOA_ENTER(env); NSString *theLabel = JNFJavaToNSString(env, label); NSString *theKeyEquivalent = nil; unichar macKey = shortcutKey; - + if (macKey == 0) { macKey = AWTKeyToMacShortcut(shortcutKeyCode, (mods & java_awt_event_KeyEvent_SHIFT_MASK) != 0); } - + if (macKey != 0) { unichar equivalent[1] = {macKey}; theKeyEquivalent = [NSString stringWithCharacters:equivalent length:1]; } else { theKeyEquivalent = @""; } - + [((CMenuItem *)jlong_to_ptr(menuItemObj)) setJavaLabel:theLabel shortcut:theKeyEquivalent modifierMask:mods]; -JNF_COCOA_EXIT(env); + JNF_COCOA_EXIT(env); } /* @@ -362,10 +404,10 @@ static unichar AWTKeyToMacShortcut(jint awtKey, BOOL doShift) { Java_sun_lwawt_macosx_CMenuItem_nativeSetTooltip (JNIEnv *env, jobject peer, jlong menuItemObj, jstring tooltip) { -JNF_COCOA_ENTER(env); + JNF_COCOA_ENTER(env); NSString *theTooltip = JNFJavaToNSString(env, tooltip); [((CMenuItem *)jlong_to_ptr(menuItemObj)) setJavaToolTipText:theTooltip]; -JNF_COCOA_EXIT(env); + JNF_COCOA_EXIT(env); } /* @@ -377,9 +419,9 @@ static unichar AWTKeyToMacShortcut(jint awtKey, BOOL doShift) { Java_sun_lwawt_macosx_CMenuItem_nativeSetImage (JNIEnv *env, jobject peer, jlong menuItemObj, jlong image) { -JNF_COCOA_ENTER(env); + JNF_COCOA_ENTER(env); [((CMenuItem *)jlong_to_ptr(menuItemObj)) setJavaImage:(NSImage*)jlong_to_ptr(image)]; -JNF_COCOA_EXIT(env); + JNF_COCOA_EXIT(env); } /* @@ -389,38 +431,38 @@ static unichar AWTKeyToMacShortcut(jint awtKey, BOOL doShift) { */ JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CMenuItem_nativeCreate - (JNIEnv *env, jobject peer, jlong parentCMenuObj, jboolean isSeparator) +(JNIEnv *env, jobject peer, jlong parentCMenuObj, jboolean isSeparator) { - + CMenuItem *aCMenuItem = nil; CMenu *parentCMenu = (CMenu *)jlong_to_ptr(parentCMenuObj); -JNF_COCOA_ENTER(env); - + JNF_COCOA_ENTER(env); + jobject cPeerObjGlobal = (*env)->NewGlobalRef(env, peer); - + NSMutableArray *args = nil; - + // Create a new item.... if (isSeparator == JNI_TRUE) { args = [[NSMutableArray alloc] initWithObjects:[NSValue valueWithBytes:&cPeerObjGlobal objCType:@encode(jobject)], [NSNumber numberWithBool:YES], nil]; } else { args = [[NSMutableArray alloc] initWithObjects:[NSValue valueWithBytes:&cPeerObjGlobal objCType:@encode(jobject)], [NSNumber numberWithBool:NO], nil]; } - + [ThreadUtilities performOnMainThread:@selector(_createMenuItem_OnAppKitThread:) on:[CMenuItem alloc] withObject:args waitUntilDone:YES]; - + aCMenuItem = (CMenuItem *)[args objectAtIndex: 0]; - + if (aCMenuItem == nil) { return 0L; } - + // and add it to the parent item. [parentCMenu addJavaMenuItem: aCMenuItem]; - + // setLabel will be called after creation completes. - -JNF_COCOA_EXIT(env); + + JNF_COCOA_EXIT(env); return ptr_to_jlong(aCMenuItem); } @@ -433,10 +475,10 @@ static unichar AWTKeyToMacShortcut(jint awtKey, BOOL doShift) { Java_sun_lwawt_macosx_CMenuItem_nativeSetEnabled (JNIEnv *env, jobject peer, jlong menuItemObj, jboolean enable) { -JNF_COCOA_ENTER(env); + JNF_COCOA_ENTER(env); CMenuItem *item = (CMenuItem *)jlong_to_ptr(menuItemObj); [item setJavaEnabled: (enable == JNI_TRUE)]; -JNF_COCOA_EXIT(env); + JNF_COCOA_EXIT(env); } /* @@ -448,10 +490,10 @@ static unichar AWTKeyToMacShortcut(jint awtKey, BOOL doShift) { Java_sun_lwawt_macosx_CCheckboxMenuItem_nativeSetState (JNIEnv *env, jobject peer, jlong menuItemObj, jboolean state) { -JNF_COCOA_ENTER(env); + JNF_COCOA_ENTER(env); CMenuItem *item = (CMenuItem *)jlong_to_ptr(menuItemObj); [item setJavaState: (state == JNI_TRUE)]; -JNF_COCOA_EXIT(env); + JNF_COCOA_EXIT(env); } /* @@ -463,8 +505,8 @@ static unichar AWTKeyToMacShortcut(jint awtKey, BOOL doShift) { Java_sun_lwawt_macosx_CCheckboxMenuItem_nativeSetIsCheckbox (JNIEnv *env, jobject peer, jlong menuItemObj) { -JNF_COCOA_ENTER(env); + JNF_COCOA_ENTER(env); CMenuItem *item = (CMenuItem *)jlong_to_ptr(menuItemObj); [item setIsCheckbox]; -JNF_COCOA_EXIT(env); + JNF_COCOA_EXIT(env); } diff --git a/jdk/test/javax/swing/JMenuItem/8139169/ScreenMenuBarInputTwice.java b/jdk/test/javax/swing/JMenuItem/8139169/ScreenMenuBarInputTwice.java new file mode 100644 index 00000000000..b591e6bc585 --- /dev/null +++ b/jdk/test/javax/swing/JMenuItem/8139169/ScreenMenuBarInputTwice.java @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8139169 + * @summary verifies if TextArea gets input twice due to Apple's Screen Menubar + * @requires (os.family=="mac") + * @library ../../regtesthelpers + * @build Util + * @run main ScreenMenuBarInputTwice + */ +import java.awt.BorderLayout; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import static java.awt.event.KeyEvent.VK_COMMA; +import static java.awt.event.KeyEvent.VK_META; +import static java.awt.event.KeyEvent.VK_SHIFT; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.KeyStroke; +import javax.swing.SwingUtilities; +import javax.swing.text.BadLocationException; + +public class ScreenMenuBarInputTwice { + + public static final String TEST_STRING = "Check string"; + + private static Robot robot; + private static JFrame frame; + private static JPanel content; + private static JTextArea textArea; + private static JMenuBar menuBar; + private static JMenu menu; + private static JMenuItem menuItem; + + public static void main(String[] args) throws Exception { + robot = new Robot(); + createUIWithSeperateMenuBar(); + robot.delay(2000); + shortcutTestCase(); + robot.delay(2000); + cleanUp(); + createUIWithIntegratedMenuBar(); + robot.delay(2000); + menuTestCase(); + robot.delay(2000); + cleanUp(); + } + + private static void createUIWithSeperateMenuBar() throws Exception { + SwingUtilities.invokeAndWait(new Runnable() { + + @Override + public void run() { + System.setProperty( + "com.apple.mrj.application.apple.menu.about.name", + "A test frame"); + System.setProperty("apple.laf.useScreenMenuBar", "true"); + frame = new JFrame("Text input twice check"); + content = new JPanel(new BorderLayout()); + textArea = new JTextArea(); + content.add(new JScrollPane(textArea, + JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, + JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED), + BorderLayout.CENTER); + menuBar = new JMenuBar(); + frame.setJMenuBar(menuBar); + Action a = new AbstractAction("Insert some text") { + @Override + public void actionPerformed(ActionEvent arg0) { + try { + + textArea.getDocument() + .insertString(0, TEST_STRING, null); + } catch (BadLocationException e) { + frame.dispose(); + throw new RuntimeException("Bad location: ", e); + } + } + }; + KeyStroke keyStroke = KeyStroke.getKeyStroke( + "meta shift COMMA"); + a.putValue(Action.ACCELERATOR_KEY, keyStroke); + textArea.getInputMap().put(keyStroke, "myAction"); + textArea.getActionMap().put("myAction", a); + menu = new JMenu("The Menu"); + menuItem = new JMenuItem(a); + menuItem.setAccelerator((KeyStroke) a.getValue( + Action.ACCELERATOR_KEY)); + menu.add(menuItem); + menuBar.add(menu); + frame.getContentPane().add(content); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setLocationRelativeTo(null); + frame.setSize(500, 500); + frame.setVisible(true); + frame.toFront(); + } + }); + } + + private static void createUIWithIntegratedMenuBar() throws Exception { + SwingUtilities.invokeAndWait(new Runnable() { + + @Override + public void run() { + System.setProperty( + "com.apple.mrj.application.apple.menu.about.name", + "A test frame"); + System.setProperty("apple.laf.useScreenMenuBar", "false"); + frame = new JFrame("Text input twice check"); + content = new JPanel(new BorderLayout()); + textArea = new JTextArea(); + content.add(new JScrollPane(textArea, + JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, + JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED), + BorderLayout.CENTER); + menuBar = new JMenuBar(); + frame.setJMenuBar(menuBar); + Action a = new AbstractAction("Insert some text") { + @Override + public void actionPerformed(ActionEvent arg0) { + try { + + textArea.getDocument() + .insertString(0, TEST_STRING, null); + } catch (BadLocationException e) { + frame.dispose(); + throw new RuntimeException("Bad location: ", e); + } + } + }; + KeyStroke keyStroke = KeyStroke.getKeyStroke( + "meta shift COMMA"); + a.putValue(Action.ACCELERATOR_KEY, keyStroke); + textArea.getInputMap().put(keyStroke, "myAction"); + textArea.getActionMap().put("myAction", a); + menu = new JMenu("The Menu"); + menuItem = new JMenuItem(a); + menuItem.setAccelerator((KeyStroke) a.getValue( + Action.ACCELERATOR_KEY)); + menu.add(menuItem); + menuBar.add(menu); + frame.getContentPane().add(content); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setSize(500, 500); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + frame.toFront(); + } + }); + } + + private static void shortcutTestCase() throws Exception { + robot.keyPress(KeyEvent.VK_META); + robot.keyPress(KeyEvent.VK_SHIFT); + robot.keyPress(KeyEvent.VK_COMMA); + robot.keyRelease(VK_COMMA); + robot.keyRelease(VK_SHIFT); + robot.keyRelease(VK_META); + robot.delay(2000); + checkText(textArea.getText()); + } + + private static void menuTestCase() throws Exception { + Point mousePoint; + mousePoint = Util.getCenterPoint(menu); + robot.mouseMove(mousePoint.x, mousePoint.y); + robot.mousePress(InputEvent.BUTTON1_MASK); + robot.mouseRelease(InputEvent.BUTTON1_MASK); + robot.delay(2000); + mousePoint = Util.getCenterPoint(menuItem); + robot.mouseMove(mousePoint.x, mousePoint.y); + robot.delay(2000); + robot.mousePress(InputEvent.BUTTON1_MASK); + robot.mouseRelease(InputEvent.BUTTON1_MASK); + robot.delay(2000); + checkText(textArea.getText()); + } + + private static void checkText(String text) throws Exception { + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + if (TEST_STRING.equals(text)) { + textArea.setText(""); + } else { + frame.dispose(); + throw new RuntimeException("Failed. " + + " Menu item shortcut invoked twice"); + } + } + }); + } + + private static void cleanUp() throws Exception { + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + frame.dispose(); + } + }); + } +} From 946e0bc9cc01c228cb3c9a6b86aa145904788b04 Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Thu, 10 Dec 2015 16:09:42 +0300 Subject: [PATCH 40/62] 8040139: Test closed/javax/print/attribute/Services_getDocFl.java fails with NullpointerException Reviewed-by: jdv, prr --- .../classes/sun/print/IPPPrintService.java | 5 +- .../print/attribute/Services_getDocFl.java | 84 +++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 jdk/test/javax/print/attribute/Services_getDocFl.java diff --git a/jdk/src/java.desktop/unix/classes/sun/print/IPPPrintService.java b/jdk/src/java.desktop/unix/classes/sun/print/IPPPrintService.java index 7782d543219..9587067a750 100644 --- a/jdk/src/java.desktop/unix/classes/sun/print/IPPPrintService.java +++ b/jdk/src/java.desktop/unix/classes/sun/print/IPPPrintService.java @@ -926,7 +926,10 @@ public synchronized DocFlavor[] getSupportedDocFlavors() { return copyflavors; } } - return null; + DocFlavor[] flavor = new DocFlavor[2]; + flavor[0] = DocFlavor.SERVICE_FORMATTED.PAGEABLE; + flavor[1] = DocFlavor.SERVICE_FORMATTED.PRINTABLE; + return flavor; } diff --git a/jdk/test/javax/print/attribute/Services_getDocFl.java b/jdk/test/javax/print/attribute/Services_getDocFl.java new file mode 100644 index 00000000000..e3f0a850e3f --- /dev/null +++ b/jdk/test/javax/print/attribute/Services_getDocFl.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.print.DocFlavor; +import javax.print.PrintService; +import javax.print.PrintServiceLookup; +import javax.print.attribute.HashPrintRequestAttributeSet; + +/* + * @test + * @bug 4901243 8040139 + * @summary JPG, GIF, and PNG DocFlavors (URL) should be supported if Postscript is supported. + * @run main Services_getDocFl +*/ + + +public class Services_getDocFl { + public static void main (String [] args) { + + HashPrintRequestAttributeSet prSet = null; + boolean psSupported = false, + pngImagesSupported = false, + gifImagesSupported = false, + jpgImagesSupported = false; + String mimeType; + + PrintService[] serv = PrintServiceLookup.lookupPrintServices(null, null); + if (serv.length==0) { + System.out.println("no PrintService found"); + } else { + System.out.println("number of Services "+serv.length); + } + + + for (int i = 0; i Date: Thu, 10 Dec 2015 08:17:08 -0800 Subject: [PATCH 41/62] Added tag jdk-9+96 for changeset 25e9d31417fa --- jdk/.hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/jdk/.hgtags b/jdk/.hgtags index e1f07ad834d..e4b1c1f57c3 100644 --- a/jdk/.hgtags +++ b/jdk/.hgtags @@ -338,3 +338,4 @@ b433e4dfb830fea60e5187e4580791b62cc362d2 jdk9-b90 2f12392d0dde768150c83087cdbdd0d33a4d866c jdk9-b93 559b626b01179420a94feb9c3d0f246970d2e3fa jdk9-b94 8581faf0d474472e32f589bbc16db7eec912d83f jdk-9+95 +c021b855f51e572e63982654b17742cb1f814fb4 jdk-9+96 From eb2d3e3a7b73d31c8fe1c856480acf1ced980943 Mon Sep 17 00:00:00 2001 From: Peter Brunet Date: Thu, 10 Dec 2015 12:16:29 -0600 Subject: [PATCH 42/62] 8071334: Investigate JAB changes required to support the version string change Remove use of java.version property; no longer needed Reviewed-by: van, alexsch --- .../accessibility/util/AWTEventMonitor.java | 20 ++----------------- .../accessibility/internal/AccessBridge.java | 10 ++-------- 2 files changed, 4 insertions(+), 26 deletions(-) diff --git a/jdk/src/jdk.accessibility/share/classes/com/sun/java/accessibility/util/AWTEventMonitor.java b/jdk/src/jdk.accessibility/share/classes/com/sun/java/accessibility/util/AWTEventMonitor.java index 7b0f8c29e06..257bbb72953 100644 --- a/jdk/src/jdk.accessibility/share/classes/com/sun/java/accessibility/util/AWTEventMonitor.java +++ b/jdk/src/jdk.accessibility/share/classes/com/sun/java/accessibility/util/AWTEventMonitor.java @@ -48,8 +48,6 @@ @jdk.Exported public class AWTEventMonitor { - static private boolean runningOnJDK1_4 = false; - /** * The current component with keyboard focus. * @@ -638,15 +636,9 @@ static class AWTEventsListener implements TopLevelWindowListener, * @see AWTEventMonitor */ public AWTEventsListener() { - String version = System.getProperty("java.version"); - if (version != null) { - runningOnJDK1_4 = (version.compareTo("1.4") >= 0); - } initializeIntrospection(); installListeners(); - if (runningOnJDK1_4) { - MenuSelectionManager.defaultManager().addChangeListener(this); - } + MenuSelectionManager.defaultManager().addChangeListener(this); EventQueueMonitor.addTopLevelWindowListener(this); } @@ -848,15 +840,7 @@ protected void installListeners(Component c, int eventID) { case EventID.FOCUS: c.removeFocusListener(this); c.addFocusListener(this); - - if (runningOnJDK1_4) { - processFocusGained(); - - } else { // not runningOnJDK1_4 - if ((c != componentWithFocus_private) && c.hasFocus()) { - componentWithFocus_private = c; - } - } + processFocusGained(); break; case EventID.ITEM: diff --git a/jdk/src/jdk.accessibility/windows/classes/com/sun/java/accessibility/internal/AccessBridge.java b/jdk/src/jdk.accessibility/windows/classes/com/sun/java/accessibility/internal/AccessBridge.java index ee1a63f5ec9..47832a4455b 100644 --- a/jdk/src/jdk.accessibility/windows/classes/com/sun/java/accessibility/internal/AccessBridge.java +++ b/jdk/src/jdk.accessibility/windows/classes/com/sun/java/accessibility/internal/AccessBridge.java @@ -140,10 +140,6 @@ public AccessBridge() { // initialize AccessibleRole map initAccessibleRoleMap(); - // determine which version of the JDK is running - String version = getJavaVersionProperty(); - debugString("JDK version = "+version); - // initialize the methods that map HWNDs and Java top-level // windows initHWNDcalls(); @@ -215,9 +211,7 @@ private void initAccessibleRoleMap() { } catch (Exception e) {} /* - Build the extendedVirtualNameSearchRoles array list. I chose this method - because some of the Accessible Roles that need to be added to it are not - available in all versions of the J2SE that we want to support. + Build the extendedVirtualNameSearchRoles array list. */ extendedVirtualNameSearchRoles.add (AccessibleRole.COMBO_BOX); try { @@ -5919,7 +5913,7 @@ public void run(){ */ AccessibleRole.UNKNOWN, - // These roles are only available in JDK 1.4 + // These roles are available since JDK 1.4 /** * A STATUS_BAR is an simple component that can contain From c588dd42dd368d2ef8b6350ec6766c5837c3b39a Mon Sep 17 00:00:00 2001 From: Phil Race Date: Thu, 10 Dec 2015 12:51:08 -0800 Subject: [PATCH 43/62] 8144858: HBShaper.c does not compiler with VS2010 Reviewed-by: serb, simonis --- .../share/native/libfontmanager/HBShaper.c | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/jdk/src/java.desktop/share/native/libfontmanager/HBShaper.c b/jdk/src/java.desktop/share/native/libfontmanager/HBShaper.c index 422575cb165..00c6ee1fa44 100644 --- a/jdk/src/java.desktop/share/native/libfontmanager/HBShaper.c +++ b/jdk/src/java.desktop/share/native/libfontmanager/HBShaper.c @@ -80,15 +80,18 @@ int storeGVData(JNIEnv* env, float scale = 1.0f/64.0f; unsigned int* glyphs; float* positions; + int initialCount, glyphArrayLen, posArrayLen, maxGlyphs, storeadv; + unsigned int* indices; + jarray glyphArray, posArray, inxArray; if (!init_JNI_IDs(env)) { return 0; } - int initialCount = (*env)->GetIntField(env, gvdata, gvdCountFID); - jarray glyphArray = + initialCount = (*env)->GetIntField(env, gvdata, gvdCountFID); + glyphArray = (jarray)(*env)->GetObjectField(env, gvdata, gvdGlyphsFID); - jarray posArray = + posArray = (jarray)(*env)->GetObjectField(env, gvdata, gvdPositionsFID); if (glyphArray == NULL || posArray == NULL) @@ -101,9 +104,9 @@ int storeGVData(JNIEnv* env, // and re-invokes layout. I suppose this is expected to be rare // because at least in a single threaded case there should be // re-use of the same container, but it is a little wasteful/distateful. - int glyphArrayLen = (*env)->GetArrayLength(env, glyphArray); - int posArrayLen = (*env)->GetArrayLength(env, posArray); - int maxGlyphs = glyphCount + initialCount; + glyphArrayLen = (*env)->GetArrayLength(env, glyphArray); + posArrayLen = (*env)->GetArrayLength(env, posArray); + maxGlyphs = glyphCount + initialCount; if ((maxGlyphs > glyphArrayLen) || (maxGlyphs * 2 + 2 > posArrayLen)) { @@ -125,7 +128,7 @@ int storeGVData(JNIEnv* env, x += glyphPos[i].x_advance * scale; y += glyphPos[i].y_advance * scale; } - int storeadv = initialCount+glyphCount; + storeadv = initialCount+glyphCount; // The final slot in the positions array is important // because when the GlyphVector is created from this // data it determines the overall advance of the glyphvector @@ -138,11 +141,10 @@ int storeGVData(JNIEnv* env, (*env)->ReleasePrimitiveArrayCritical(env, glyphArray, glyphs, 0); (*env)->ReleasePrimitiveArrayCritical(env, posArray, positions, 0); putFloat(env, startPt,positions[(storeadv*2)],positions[(storeadv*2)+1] ); - jarray inxArray = + inxArray = (jarray)(*env)->GetObjectField(env, gvdata, gvdIndicesFID); - unsigned int* indices = + indices = (unsigned int*)(*env)->GetPrimitiveArrayCritical(env, inxArray, NULL); - int prevCluster = -1; for (i = 0; i < glyphCount; i++) { int cluster = glyphInfo[i].cluster; if (direction == HB_DIRECTION_LTR) { From bb10c3f0de78a4672b09296c90d3e54fe5fd22ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laurent=20Bourg=C3=A8s?= Date: Thu, 10 Dec 2015 15:45:18 -0800 Subject: [PATCH 44/62] 8144446: Automate the Marlin crash test Reviewed-by: prr, flar --- .../classes/sun/java2d/marlin/Renderer.java | 7 +++ jdk/test/sun/java2d/marlin/CrashTest.java | 59 ++++++++++--------- 2 files changed, 38 insertions(+), 28 deletions(-) diff --git a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/Renderer.java b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/Renderer.java index d4aa005eadb..f59785cd92f 100644 --- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/Renderer.java +++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/Renderer.java @@ -629,6 +629,13 @@ void dispose() { } if (edgeMinY != Float.POSITIVE_INFINITY) { + // if context is maked as DIRTY: + if (rdrCtx.dirty) { + // may happen if an exception if thrown in the pipeline processing: + // clear completely buckets arrays: + buckets_minY = 0; + buckets_maxY = boundsMaxY - boundsMinY; + } // clear used part if (edgeBuckets == edgeBuckets_initial) { // fill only used part diff --git a/jdk/test/sun/java2d/marlin/CrashTest.java b/jdk/test/sun/java2d/marlin/CrashTest.java index b5f760a65e3..b5283fdf332 100644 --- a/jdk/test/sun/java2d/marlin/CrashTest.java +++ b/jdk/test/sun/java2d/marlin/CrashTest.java @@ -31,31 +31,44 @@ import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; -import sun.java2d.pipe.RenderingEngine; /** - * Simple crash rendering test using huge GeneralPaths with marlin renderer - * - * run it with large heap (2g): - * java -Dsun.java2d.renderer=sun.java2d.marlin.MarlinRenderingEngine marlin.CrashTest - * - * @author bourgesl - */ + * @test + * @summary Simple crash rendering test using huge GeneralPaths with the Marlin renderer + * @run main/othervm -mx512m CrashTest + * @ignore tests that take a long time and consumes 5Gb memory + * @run main/othervm -ms4g -mx4g CrashTest -slow +*/ public class CrashTest { static final boolean SAVE_IMAGE = false; static boolean USE_ROUND_CAPS_AND_JOINS = true; public static void main(String[] args) { + boolean runSlowTests = (args.length != 0 && "-slow".equals(args[0])); + + // First display which renderer is tested: + System.setProperty("sun.java2d.renderer.verbose", "true"); + // try insane image sizes: // subpixel coords may overflow: -// testHugeImage((Integer.MAX_VALUE >> 3) + 1, 6); + // check MAX_VALUE / (8 * 2); overflow may happen due to orientation flag + // But as it is impossible to allocate an image larger than 2Gb (byte) then + // it is also impossible to have rowAAChunk larger than 2Gb ! + + // Disabled test as it consumes 4GB heap + offheap (2Gb) ie > 6Gb ! + if (runSlowTests) { + testHugeImage((Integer.MAX_VALUE >> 4) - 100, 16); + } + // larger than 23 bits: (RLE) testHugeImage(8388608 + 1, 10); - test(0.1f, false, 0); - test(0.1f, true, 7f); + if (runSlowTests) { + test(0.1f, false, 0); + test(0.1f, true, 7f); + } // Exceed 2Gb OffHeap buffer for edges: try { @@ -67,17 +80,15 @@ public static void main(String[] args) { if (th instanceof ArrayIndexOutOfBoundsException) { System.out.println("ArrayIndexOutOfBoundsException expected."); } else { - System.out.println("Exception occured:"); - th.printStackTrace(); + throw new RuntimeException("Unexpected exception", th); } } - } private static void test(final float lineStroke, final boolean useDashes, final float dashMinLen) - throws ArrayIndexOutOfBoundsException + throws ArrayIndexOutOfBoundsException { System.out.println("---\n" + "test: " + "lineStroke=" + lineStroke @@ -85,9 +96,6 @@ private static void test(final float lineStroke, +", dashMinLen=" + dashMinLen ); - final String renderer = RenderingEngine.getInstance().getClass().getSimpleName(); - System.out.println("Testing renderer = " + renderer); - final BasicStroke stroke = createStroke(lineStroke, useDashes, dashMinLen); // TODO: test Dasher.firstSegmentsBuffer resizing ? @@ -135,7 +143,7 @@ private static void test(final float lineStroke, if (SAVE_IMAGE) { try { - final File file = new File("CrashTest-" + renderer + "-dash-" + useDashes + ".bmp"); + final File file = new File("CrashTest-dash-" + useDashes + ".bmp"); System.out.println("Writing file: " + file.getAbsolutePath()); ImageIO.write(image, "BMP", file); @@ -150,15 +158,10 @@ private static void test(final float lineStroke, } private static void testHugeImage(final int width, final int height) - throws ArrayIndexOutOfBoundsException + throws ArrayIndexOutOfBoundsException { System.out.println("---\n" + "testHugeImage: " - + "width=" + width - + ", height=" + height - ); - - final String renderer = RenderingEngine.getInstance().getClass().getSimpleName(); - System.out.println("Testing renderer = " + renderer); + + "width=" + width + ", height=" + height); final BasicStroke stroke = createStroke(2.5f, false, 0); @@ -195,8 +198,8 @@ private static void testHugeImage(final int width, final int height) if (SAVE_IMAGE) { try { - final File file = new File("CrashTest-" + renderer + - "-huge-" + width + "x" +height + ".bmp"); + final File file = new File("CrashTest-huge-" + + width + "x" +height + ".bmp"); System.out.println("Writing file: " + file.getAbsolutePath()); ImageIO.write(image, "BMP", file); From 80222f5b67383332c7c7ba63eaa6f40bbc30181f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laurent=20Bourg=C3=A8s?= Date: Thu, 10 Dec 2015 15:52:14 -0800 Subject: [PATCH 45/62] 8144445: Maximum size checking in Marlin ArrayCache utility methods is not optimal Reviewed-by: prr, flar --- .../classes/sun/java2d/marlin/ArrayCache.java | 38 +++-- .../classes/sun/java2d/marlin/Stroker.java | 5 +- .../sun/java2d/marlin/ArrayCacheSizeTest.java | 131 ++++++++++++++++++ 3 files changed, 161 insertions(+), 13 deletions(-) create mode 100644 jdk/test/sun/java2d/marlin/ArrayCacheSizeTest.java diff --git a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/ArrayCache.java b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/ArrayCache.java index bc4bc54d0d5..e518c4d2261 100644 --- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/ArrayCache.java +++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/ArrayCache.java @@ -166,18 +166,31 @@ static int getBucketDirtyBytes(final int length) { * @return new array size */ public static int getNewSize(final int curSize, final int needSize) { + // check if needSize is negative or integer overflow: + if (needSize < 0) { + // hard overflow failure - we can't even accommodate + // new items without overflowing + throw new ArrayIndexOutOfBoundsException( + "array exceeds maximum capacity !"); + } + assert curSize >= 0; final int initial = (curSize & MASK_CLR_1); int size; if (initial > THRESHOLD_ARRAY_SIZE) { size = initial + (initial >> 1); // x(3/2) } else { - size = (initial) << 1; // x2 + size = (initial << 1); // x2 } // ensure the new size is >= needed size: if (size < needSize) { - // align to 4096: + // align to 4096 (may overflow): size = ((needSize >> 12) + 1) << 12; } + // check integer overflow: + if (size < 0) { + // resize to maximum capacity: + size = Integer.MAX_VALUE; + } return size; } @@ -188,26 +201,29 @@ public static int getNewSize(final int curSize, final int needSize) { * @return new array size */ public static long getNewLargeSize(final long curSize, final long needSize) { + // check if needSize is negative or integer overflow: + if ((needSize >> 31L) != 0L) { + // hard overflow failure - we can't even accommodate + // new items without overflowing + throw new ArrayIndexOutOfBoundsException( + "array exceeds maximum capacity !"); + } + assert curSize >= 0L; long size; if (curSize > THRESHOLD_HUGE_ARRAY_SIZE) { size = curSize + (curSize >> 2L); // x(5/4) } else if (curSize > THRESHOLD_LARGE_ARRAY_SIZE) { size = curSize + (curSize >> 1L); // x(3/2) } else { - size = curSize << 1L; // x2 + size = (curSize << 1L); // x2 } // ensure the new size is >= needed size: if (size < needSize) { // align to 4096: - size = ((needSize >> 12) + 1) << 12; + size = ((needSize >> 12L) + 1L) << 12L; } - if (size >= Integer.MAX_VALUE) { - if (curSize >= Integer.MAX_VALUE) { - // hard overflow failure - we can't even accommodate - // new items without overflowing - throw new ArrayIndexOutOfBoundsException( - "array exceeds maximum capacity !"); - } + // check integer overflow: + if (size > Integer.MAX_VALUE) { // resize to maximum capacity: size = Integer.MAX_VALUE; } diff --git a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/Stroker.java b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/Stroker.java index 76c93494d57..1aa48411f04 100644 --- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/Stroker.java +++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/Stroker.java @@ -1265,14 +1265,15 @@ void dispose() { } private void ensureSpace(final int n) { - if (end + n > curves.length) { + // use substraction to avoid integer overflow: + if (curves.length - end < n) { if (doStats) { RendererContext.stats.stat_array_stroker_polystack_curves .add(end + n); } curves = rdrCtx.widenDirtyFloatArray(curves, end, end + n); } - if (numCurves + 1 > curveTypes.length) { + if (curveTypes.length <= numCurves) { if (doStats) { RendererContext.stats.stat_array_stroker_polystack_curveTypes .add(numCurves + 1); diff --git a/jdk/test/sun/java2d/marlin/ArrayCacheSizeTest.java b/jdk/test/sun/java2d/marlin/ArrayCacheSizeTest.java new file mode 100644 index 00000000000..8c40fe4cfae --- /dev/null +++ b/jdk/test/sun/java2d/marlin/ArrayCacheSizeTest.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import sun.java2d.marlin.ArrayCache; + +/** + * @test + * @bug 8144445 + * @summary Check the ArrayCache getNewLargeSize() method + * @run main ArrayCacheSizeTest + */ +public class ArrayCacheSizeTest { + + public static void main(String[] args) { + testNewSize(); + testNewLargeSize(); + } + + private static void testNewSize() { + testNewSize(0, 1); + testNewSize(0, 100000); + + testNewSize(4096, 4097); + testNewSize(4096 * 16, 4096 * 16 + 1); + + testNewSize(4096 * 4096 * 4, 4096 * 4096 * 4 + 1); + + testNewSize(4096 * 4096 * 4, Integer.MAX_VALUE); + + testNewSize(Integer.MAX_VALUE - 1000, Integer.MAX_VALUE); + + testNewSizeExpectAIOB(Integer.MAX_VALUE - 1000, Integer.MAX_VALUE + 1); + testNewSizeExpectAIOB(1, -1); + testNewSizeExpectAIOB(Integer.MAX_VALUE, -1); + } + + private static void testNewSizeExpectAIOB(final int curSize, + final int needSize) { + try { + testNewSize(curSize, needSize); + throw new RuntimeException("ArrayIndexOutOfBoundsException not thrown"); + } catch (ArrayIndexOutOfBoundsException aiobe) { + System.out.println("ArrayIndexOutOfBoundsException expected."); + } catch (RuntimeException re) { + throw re; + } catch (Throwable th) { + throw new RuntimeException("Unexpected exception", th); + } + } + + private static void testNewSize(final int curSize, + final int needSize) { + + int size = ArrayCache.getNewSize(curSize, needSize); + + System.out.println("getNewSize(" + curSize + ", " + needSize + + ") = " + size); + + if (size < 0 || size < needSize) { + throw new IllegalStateException("Invalid getNewSize(" + + curSize + ", " + needSize + ") = " + size + " !"); + } + } + + private static void testNewLargeSize() { + testNewLargeSize(0, 1); + testNewLargeSize(0, 100000); + + testNewLargeSize(4096, 4097); + testNewLargeSize(4096 * 16, 4096 * 16 + 1); + + testNewLargeSize(4096 * 4096 * 4, 4096 * 4096 * 4 + 1); + + testNewLargeSize(4096 * 4096 * 4, Integer.MAX_VALUE); + + testNewLargeSize(Integer.MAX_VALUE - 1000, Integer.MAX_VALUE); + + testNewLargeSizeExpectAIOB(Integer.MAX_VALUE - 1000, Integer.MAX_VALUE + 1L); + testNewLargeSizeExpectAIOB(1, -1L); + testNewLargeSizeExpectAIOB(Integer.MAX_VALUE, -1L); + } + + private static void testNewLargeSizeExpectAIOB(final long curSize, + final long needSize) { + try { + testNewLargeSize(curSize, needSize); + throw new RuntimeException("ArrayIndexOutOfBoundsException not thrown"); + } catch (ArrayIndexOutOfBoundsException aiobe) { + System.out.println("ArrayIndexOutOfBoundsException expected."); + } catch (RuntimeException re) { + throw re; + } catch (Throwable th) { + throw new RuntimeException("Unexpected exception", th); + } + } + + private static void testNewLargeSize(final long curSize, + final long needSize) { + + long size = ArrayCache.getNewLargeSize(curSize, needSize); + + System.out.println("getNewLargeSize(" + curSize + ", " + needSize + + ") = " + size); + + if (size < 0 || size < needSize || size > Integer.MAX_VALUE) { + throw new IllegalStateException("Invalid getNewLargeSize(" + + curSize + ", " + needSize + ") = " + size + " !"); + } + } + +} From 583937011af76bd244566791f598991fbd7cd995 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laurent=20Bourg=C3=A8s?= Date: Thu, 10 Dec 2015 15:58:01 -0800 Subject: [PATCH 46/62] 8144654: Improve Marlin logging Reviewed-by: prr, flar --- .../sun/java2d/marlin/ByteArrayCache.java | 4 ++-- .../sun/java2d/marlin/FloatArrayCache.java | 4 ++-- .../sun/java2d/marlin/IntArrayCache.java | 4 ++-- .../sun/java2d/marlin/MarlinConst.java | 9 ++++---- .../sun/java2d/marlin/MarlinProperties.java | 4 ++++ .../sun/java2d/marlin/MarlinUtils.java | 22 ++++--------------- .../sun/java2d/marlin/RendererContext.java | 22 +++++++------------ 7 files changed, 27 insertions(+), 42 deletions(-) diff --git a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/ByteArrayCache.java b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/ByteArrayCache.java index cd6ebee89e8..226a3d2e30d 100644 --- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/ByteArrayCache.java +++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/ByteArrayCache.java @@ -74,7 +74,7 @@ byte[] getArray() { void putDirtyArray(final byte[] array, final int length) { if (length != arraySize) { if (doChecks) { - System.out.println("ArrayCache: bad length = " + length); + MarlinUtils.logInfo("ArrayCache: bad length = " + length); } return; } @@ -98,7 +98,7 @@ void putArray(final byte[] array, final int length, { if (length != arraySize) { if (doChecks) { - System.out.println("ArrayCache: bad length = " + length); + MarlinUtils.logInfo("ArrayCache: bad length = " + length); } return; } diff --git a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/FloatArrayCache.java b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/FloatArrayCache.java index a068ad80fbc..06d7f351e28 100644 --- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/FloatArrayCache.java +++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/FloatArrayCache.java @@ -75,7 +75,7 @@ float[] getArray() { void putDirtyArray(final float[] array, final int length) { if (length != arraySize) { if (doChecks) { - System.out.println("ArrayCache: bad length = " + length); + MarlinUtils.logInfo("ArrayCache: bad length = " + length); } return; } @@ -99,7 +99,7 @@ void putArray(final float[] array, final int length, { if (length != arraySize) { if (doChecks) { - System.out.println("ArrayCache: bad length = " + length); + MarlinUtils.logInfo("ArrayCache: bad length = " + length); } return; } diff --git a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/IntArrayCache.java b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/IntArrayCache.java index ccd239cb534..11c5aae84f6 100644 --- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/IntArrayCache.java +++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/IntArrayCache.java @@ -74,7 +74,7 @@ int[] getArray() { void putDirtyArray(final int[] array, final int length) { if (length != arraySize) { if (doChecks) { - System.out.println("ArrayCache: bad length = " + length); + MarlinUtils.logInfo("ArrayCache: bad length = " + length); } return; } @@ -98,7 +98,7 @@ void putArray(final int[] array, final int length, { if (length != arraySize) { if (doChecks) { - System.out.println("ArrayCache: bad length = " + length); + MarlinUtils.logInfo("ArrayCache: bad length = " + length); } return; } diff --git a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinConst.java b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinConst.java index e799504318d..72993ebfd7c 100644 --- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinConst.java +++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinConst.java @@ -30,8 +30,8 @@ */ interface MarlinConst { // enable Logs (logger or stdout) - static final boolean enableLogs = false; - // enable Logger + static final boolean enableLogs = MarlinProperties.isLoggingEnabled(); + // use Logger instead of stdout static final boolean useLogger = enableLogs && MarlinProperties.isUseLogger(); // log new RendererContext @@ -47,9 +47,10 @@ interface MarlinConst { static final boolean doStats = enableLogs && MarlinProperties.isDoStats(); // do monitors // disabled to reduce byte-code size a bit... - static final boolean doMonitors = enableLogs && false; // MarlinProperties.isDoMonitors(); + static final boolean doMonitors = false; +// static final boolean doMonitors = enableLogs && MarlinProperties.isDoMonitors(); // do checks - static final boolean doChecks = false; // MarlinProperties.isDoChecks(); + static final boolean doChecks = enableLogs && MarlinProperties.isDoChecks(); // do AA range checks: disable when algorithm / code is stable static final boolean DO_AA_RANGE_CHECK = false; diff --git a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinProperties.java b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinProperties.java index 002f16d9d5b..bbee15a13fb 100644 --- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinProperties.java +++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinProperties.java @@ -136,6 +136,10 @@ public static boolean isDoChecks() { // logging parameters + public static boolean isLoggingEnabled() { + return getBoolean("sun.java2d.renderer.log", "false"); + } + public static boolean isUseLogger() { return getBoolean("sun.java2d.renderer.useLogger", "false"); } diff --git a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinUtils.java b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinUtils.java index d218d06f545..aeeacca57bd 100644 --- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinUtils.java +++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinUtils.java @@ -27,12 +27,12 @@ public final class MarlinUtils { - // TODO: use sun.util.logging.PlatformLogger once in JDK9 - private static final java.util.logging.Logger log; + // Marlin logger + private static final sun.util.logging.PlatformLogger log; static { if (MarlinConst.useLogger) { - log = java.util.logging.Logger.getLogger("sun.java2d.marlin"); + log = sun.util.logging.PlatformLogger.getLogger("sun.java2d.marlin"); } else { log = null; } @@ -53,25 +53,11 @@ public static void logInfo(final String msg) { public static void logException(final String msg, final Throwable th) { if (MarlinConst.useLogger) { -// log.warning(msg, th); - log.log(java.util.logging.Level.WARNING, msg, th); + log.warning(msg, th); } else if (MarlinConst.enableLogs) { System.out.print("WARNING: "); System.out.println(msg); th.printStackTrace(System.err); } } - - // Returns the caller's class and method's name; best effort - // if cannot infer, return the logger's name. - static String getCallerInfo(String className) { - String sourceClassName = null; - String sourceMethodName = null; - - if (sourceClassName != null) { - return sourceClassName + " " + sourceMethodName; - } else { - return "unknown"; - } - } } diff --git a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/RendererContext.java b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/RendererContext.java index 7af675b16b7..a767651f5d5 100644 --- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/RendererContext.java +++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/RendererContext.java @@ -31,7 +31,6 @@ import java.util.concurrent.atomic.AtomicInteger; import static sun.java2d.marlin.ArrayCache.*; import sun.java2d.marlin.MarlinRenderingEngine.NormalizingPathIterator; -import static sun.java2d.marlin.MarlinUtils.getCallerInfo; import static sun.java2d.marlin.MarlinUtils.logInfo; /** @@ -39,7 +38,6 @@ */ final class RendererContext implements MarlinConst { - private static final String className = RendererContext.class.getName(); // RendererContext creation counter private static final AtomicInteger contextCount = new AtomicInteger(1); // RendererContext statistics @@ -214,8 +212,7 @@ byte[] getDirtyByteArray(final int length) { } if (doLogOverSize) { - logInfo("getDirtyByteArray[oversize]: length=\t" + length - + "\tfrom=\t" + getCallerInfo(className)); + logInfo("getDirtyByteArray[oversize]: length=\t" + length); } return new byte[length]; @@ -254,7 +251,7 @@ byte[] widenDirtyByteArray(final byte[] in, if (doLogWidenArray) { logInfo("widenDirtyByteArray[" + res.length + "]: usedSize=\t" + usedSize + "\tlength=\t" + length + "\tneeded length=\t" - + needSize + "\tfrom=\t" + getCallerInfo(className)); + + needSize); } return res; } @@ -275,8 +272,7 @@ int[] getIntArray(final int length) { } if (doLogOverSize) { - logInfo("getIntArray[oversize]: length=\t" + length + "\tfrom=\t" - + getCallerInfo(className)); + logInfo("getIntArray[oversize]: length=\t" + length); } return new int[length]; @@ -306,7 +302,7 @@ int[] widenIntArray(final int[] in, final int usedSize, if (doLogWidenArray) { logInfo("widenIntArray[" + res.length + "]: usedSize=\t" + usedSize + "\tlength=\t" + length + "\tneeded length=\t" - + needSize + "\tfrom=\t" + getCallerInfo(className)); + + needSize); } return res; } @@ -338,8 +334,7 @@ int[] getDirtyIntArray(final int length) { } if (doLogOverSize) { - logInfo("getDirtyIntArray[oversize]: length=\t" + length - + "\tfrom=\t" + getCallerInfo(className)); + logInfo("getDirtyIntArray[oversize]: length=\t" + length); } return new int[length]; @@ -369,7 +364,7 @@ int[] widenDirtyIntArray(final int[] in, if (doLogWidenArray) { logInfo("widenDirtyIntArray[" + res.length + "]: usedSize=\t" + usedSize + "\tlength=\t" + length + "\tneeded length=\t" - + needSize + "\tfrom=\t" + getCallerInfo(className)); + + needSize); } return res; } @@ -399,8 +394,7 @@ float[] getDirtyFloatArray(final int length) { } if (doLogOverSize) { - logInfo("getDirtyFloatArray[oversize]: length=\t" + length - + "\tfrom=\t" + getCallerInfo(className)); + logInfo("getDirtyFloatArray[oversize]: length=\t" + length); } return new float[length]; @@ -430,7 +424,7 @@ float[] widenDirtyFloatArray(final float[] in, if (doLogWidenArray) { logInfo("widenDirtyFloatArray[" + res.length + "]: usedSize=\t" + usedSize + "\tlength=\t" + length + "\tneeded length=\t" - + needSize + "\tfrom=\t" + getCallerInfo(className)); + + needSize); } return res; } From 5b1765f25dc24d89046d2acf08859b68301d145e Mon Sep 17 00:00:00 2001 From: Brian Burkhalter Date: Fri, 11 Dec 2015 11:38:18 -0800 Subject: [PATCH 47/62] 8144245: [PIT] javax/imageio/plugins/shared/WriteAfterAbort.java Reset stream position after abort; change IAEs to NPEs. Reviewed-by: prr, serb --- .../com/sun/imageio/plugins/tiff/TIFFIFD.java | 9 +- .../imageio/plugins/tiff/TIFFImageWriter.java | 56 ++- jdk/test/ProblemList.txt | 2 - .../plugins/shared/WriteAfterAbort.java | 16 +- .../tiff/WriteToSequenceAfterAbort.java | 376 ++++++++++++++++++ 5 files changed, 449 insertions(+), 10 deletions(-) create mode 100644 jdk/test/javax/imageio/plugins/tiff/WriteToSequenceAfterAbort.java diff --git a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFIFD.java b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFIFD.java index 6803f634762..7c14f8daf85 100644 --- a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFIFD.java +++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFIFD.java @@ -472,6 +472,13 @@ public void initialize(ImageInputStream stream, boolean isPrimaryIFD, // Read tag number, value type, and value count. int tagNumber = stream.readUnsignedShort(); int type = stream.readUnsignedShort(); + int sizeOfType; + try { + sizeOfType = TIFFTag.getSizeOfType(type); + } catch (IllegalArgumentException e) { + throw new IIOException("Illegal type " + type + + " for tag number " + tagNumber, e); + } int count = (int)stream.readUnsignedInt(); // Get the associated TIFFTag. @@ -510,7 +517,7 @@ public void initialize(ImageInputStream stream, boolean isPrimaryIFD, } } - int size = count*TIFFTag.getSizeOfType(type); + int size = count*sizeOfType; if (size > 4 || tag.isIFDPointer()) { // The IFD entry value is a pointer to the actual field value. long offset = stream.readUnsignedInt(); diff --git a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFImageWriter.java b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFImageWriter.java index 08c1f0ecb9a..6a90e0677c9 100644 --- a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFImageWriter.java +++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFImageWriter.java @@ -205,6 +205,10 @@ public class TIFFImageWriter extends ImageWriter { // Next available space. private long nextSpace = 0L; + private long prevStreamPosition; + private long prevHeaderPosition; + private long prevNextSpace; + // Whether a sequence is being written. private boolean isWritingSequence = false; private boolean isInsertingEmpty = false; @@ -2300,7 +2304,14 @@ private void initializeScaleTables(int[] sampleSize) { public void write(IIOMetadata sm, IIOImage iioimage, ImageWriteParam p) throws IOException { + if (stream == null) { + throw new IllegalStateException("output == null!"); + } + markPositions(); write(sm, iioimage, p, true, true); + if (abortRequested()) { + resetPositions(); + } } private void writeHeader() throws IOException { @@ -2333,7 +2344,7 @@ private void write(IIOMetadata sm, throw new IllegalStateException("output == null!"); } if (iioimage == null) { - throw new NullPointerException("image == null!"); + throw new IllegalArgumentException("image == null!"); } if(iioimage.hasRaster() && !canWriteRasters()) { throw new UnsupportedOperationException @@ -2767,7 +2778,7 @@ private void insert(int imageIndex, throw new IllegalStateException("Output not set!"); } if (image == null) { - throw new NullPointerException("image == null!"); + throw new IllegalArgumentException("image == null!"); } // Locate the position of the old IFD (ifd) and the location @@ -2779,9 +2790,16 @@ private void insert(int imageIndex, // imageIndex is < -1 or is too big thereby satisfying the spec. locateIFD(imageIndex, ifdpos, ifd); + markPositions(); + // Seek to the position containing the pointer to the old IFD. stream.seek(ifdpos[0]); + // Save the previous pointer value in case of abort. + stream.mark(); + long prevPointerValue = stream.readUnsignedInt(); + stream.reset(); + // Update next space pointer in anticipation of next write. if(ifdpos[0] + 4 > nextSpace) { nextSpace = ifdpos[0] + 4; @@ -2805,6 +2823,12 @@ private void insert(int imageIndex, // Update the new IFD to point to the old IFD. stream.writeInt((int)ifd[0]); // Don't need to update nextSpace here as already done in write(). + + if (abortRequested()) { + stream.seek(ifdpos[0]); + stream.writeInt((int)prevPointerValue); + resetPositions(); + } } // ----- BEGIN insert/writeEmpty methods ----- @@ -2834,7 +2858,7 @@ private void checkParamsEmpty(ImageTypeSpecifier imageType, } if(imageType == null) { - throw new NullPointerException("imageType == null!"); + throw new IllegalArgumentException("imageType == null!"); } if(width < 1 || height < 1) { @@ -2891,6 +2915,10 @@ public void prepareWriteEmpty(IIOMetadata streamMetadata, IIOMetadata imageMetadata, List thumbnails, ImageWriteParam param) throws IOException { + if (stream == null) { + throw new IllegalStateException("output == null!"); + } + checkParamsEmpty(imageType, width, height, thumbnails); this.isWritingEmpty = true; @@ -2901,8 +2929,12 @@ public void prepareWriteEmpty(IIOMetadata streamMetadata, 0, 0, emptySM.getWidth(), emptySM.getHeight(), emptySM, imageType.getColorModel()); + markPositions(); write(streamMetadata, new IIOImage(emptyImage, null, imageMetadata), param, true, false); + if (abortRequested()) { + resetPositions(); + } } public void endInsertEmpty() throws IOException { @@ -3015,7 +3047,7 @@ public void prepareReplacePixels(int imageIndex, throw new IllegalStateException("Output not set!"); } if (region == null) { - throw new NullPointerException("region == null!"); + throw new IllegalArgumentException("region == null!"); } if (region.getWidth() < 1) { throw new IllegalArgumentException("region.getWidth() < 1!"); @@ -3200,7 +3232,7 @@ public void replacePixels(RenderedImage image, ImageWriteParam param) } if (image == null) { - throw new NullPointerException("image == null!"); + throw new IllegalArgumentException("image == null!"); } if (!inReplacePixelsNest) { @@ -3559,6 +3591,20 @@ public void endReplacePixels() throws IOException { // ----- END replacePixels methods ----- + // Save stream positions for use when aborted. + private void markPositions() throws IOException { + prevStreamPosition = stream.getStreamPosition(); + prevHeaderPosition = headerPosition; + prevNextSpace = nextSpace; + } + + // Reset to positions saved by markPositions(). + private void resetPositions() throws IOException { + stream.seek(prevStreamPosition); + headerPosition = prevHeaderPosition; + nextSpace = prevNextSpace; + } + public void reset() { super.reset(); diff --git a/jdk/test/ProblemList.txt b/jdk/test/ProblemList.txt index 51abaacbf53..2a18c6adef2 100644 --- a/jdk/test/ProblemList.txt +++ b/jdk/test/ProblemList.txt @@ -310,8 +310,6 @@ javax/sound/midi/Gervill/SoftProvider/GetDevice.java generic-all # jdk_imageio -javax/imageio/plugins/shared/WriteAfterAbort.java generic-all - ############################################################################ # jdk_swing diff --git a/jdk/test/javax/imageio/plugins/shared/WriteAfterAbort.java b/jdk/test/javax/imageio/plugins/shared/WriteAfterAbort.java index 4b503d2fe6f..f1be068cdfe 100644 --- a/jdk/test/javax/imageio/plugins/shared/WriteAfterAbort.java +++ b/jdk/test/javax/imageio/plugins/shared/WriteAfterAbort.java @@ -130,13 +130,25 @@ public static void main(final String[] args) throws IOException { ImageWriterSpi.class, provider -> true, true); // Validates all supported ImageWriters + int numFailures = 0; while (iter.hasNext()) { final WriteAfterAbort writeAfterAbort = new WriteAfterAbort(); final ImageWriter writer = iter.next().createWriterInstance(); System.out.println("ImageWriter = " + writer); - writeAfterAbort.test(writer); + try { + writeAfterAbort.test(writer); + } catch (Exception e) { + System.err.println("Test failed for \"" + + writer.getOriginatingProvider().getFormatNames()[0] + + "\" format."); + numFailures++; + } + } + if (numFailures == 0) { + System.out.println("Test passed."); + } else { + throw new RuntimeException("Test failed."); } - System.out.println("Test passed"); } // Callbacks diff --git a/jdk/test/javax/imageio/plugins/tiff/WriteToSequenceAfterAbort.java b/jdk/test/javax/imageio/plugins/tiff/WriteToSequenceAfterAbort.java new file mode 100644 index 00000000000..55461f36f24 --- /dev/null +++ b/jdk/test/javax/imageio/plugins/tiff/WriteToSequenceAfterAbort.java @@ -0,0 +1,376 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; + +import javax.imageio.ImageIO; +import javax.imageio.ImageWriter; +import javax.imageio.event.IIOWriteProgressListener; +import javax.imageio.stream.ImageOutputStream; + +import static java.awt.image.BufferedImage.TYPE_BYTE_BINARY; +import java.awt.image.ColorModel; +import java.awt.image.Raster; +import java.awt.image.RenderedImage; +import java.awt.image.SampleModel; +import java.awt.image.WritableRaster; +import java.util.Vector; +import javax.imageio.IIOImage; +import javax.imageio.ImageReader; +import javax.imageio.stream.ImageInputStream; + +/** + * @test + * @bug 8144245 + * @summary Ensure aborting write works properly for a TIFF sequence. + */ +public final class WriteToSequenceAfterAbort implements IIOWriteProgressListener { + + private volatile boolean abortFlag = true; + private volatile boolean isAbortCalled; + private volatile boolean isCompleteCalled; + private volatile boolean isProgressCalled; + private volatile boolean isStartedCalled; + private static final int WIDTH = 100; + private static final int HEIGHT = 100; + private static final int NUM_TILES_XY = 3; + + private class TiledImage implements RenderedImage { + private final BufferedImage tile; + private final BufferedImage image; + private final int numXTiles, numYTiles; + private boolean isImageInitialized = false; + + TiledImage(BufferedImage tile, int numXTiles, int numYTiles) { + this.tile = tile; + this.numXTiles = numXTiles; + this.numYTiles = numYTiles; + image = new BufferedImage(getWidth(), getHeight(), tile.getType()); + } + + @Override + public Vector getSources() { + return null; + } + + @Override + public Object getProperty(String string) { + return java.awt.Image.UndefinedProperty; + } + + @Override + public String[] getPropertyNames() { + return new String[0]; + } + + @Override + public ColorModel getColorModel() { + return tile.getColorModel(); + } + + @Override + public SampleModel getSampleModel() { + return tile.getSampleModel(); + } + + @Override + public int getWidth() { + return numXTiles*tile.getWidth(); + } + + @Override + public int getHeight() { + return numYTiles*tile.getHeight(); + } + + @Override + public int getMinX() { + return 0; + } + + @Override + public int getMinY() { + return 0; + } + + @Override + public int getNumXTiles() { + return numXTiles; + } + + @Override + public int getNumYTiles() { + return numYTiles; + } + + @Override + public int getMinTileX() { + return 0; + } + + @Override + public int getMinTileY() { + return 0; + } + + @Override + public int getTileWidth() { + return tile.getWidth(); + } + + @Override + public int getTileHeight() { + return tile.getHeight(); + } + + @Override + public int getTileGridXOffset() { + return 0; + } + + @Override + public int getTileGridYOffset() { + return 0; + } + + @Override + public Raster getTile(int x, int y) { + WritableRaster r = tile.getRaster(); + return r.createWritableTranslatedChild(x*tile.getWidth(), + y*tile.getHeight()); + } + + @Override + public Raster getData() { + return getAsBufferedImage().getData(); + } + + @Override + public Raster getData(Rectangle r) { + return getAsBufferedImage().getData(r); + } + + @Override + public WritableRaster copyData(WritableRaster wr) { + return getAsBufferedImage().copyData(wr); + } + + public BufferedImage getAsBufferedImage() { + synchronized (image) { + if (!isImageInitialized) { + int tx0 = getMinTileX(), ty0 = getMinTileY(); + int txN = tx0 + getNumXTiles(), tyN = ty0 + getNumYTiles(); + for (int j = ty0; j < tyN; j++) { + for (int i = tx0; i < txN; i++) { + image.setData(getTile(i, j)); + } + } + } + isImageInitialized = true; + } + return image; + } + } + + private void test(final ImageWriter writer) throws IOException { + String suffix = writer.getOriginatingProvider().getFileSuffixes()[0]; + + // Image initialization + BufferedImage imageUpperLeft = + new BufferedImage(WIDTH, HEIGHT, TYPE_BYTE_BINARY); + Graphics2D g = imageUpperLeft.createGraphics(); + g.setColor(Color.WHITE); + g.fillRect(0, 0, WIDTH/2, HEIGHT/2); + g.dispose(); + BufferedImage imageLowerRight = + new BufferedImage(WIDTH, HEIGHT, TYPE_BYTE_BINARY); + g = imageLowerRight.createGraphics(); + g.setColor(Color.WHITE); + g.fillRect(WIDTH/2, HEIGHT/2, WIDTH/2, HEIGHT/2); + g.dispose(); + TiledImage[] images = new TiledImage[] { + new TiledImage(imageUpperLeft, NUM_TILES_XY, NUM_TILES_XY), + new TiledImage(imageUpperLeft, NUM_TILES_XY, NUM_TILES_XY), + new TiledImage(imageLowerRight, NUM_TILES_XY, NUM_TILES_XY), + new TiledImage(imageLowerRight, NUM_TILES_XY, NUM_TILES_XY) + }; + + // File initialization + File file = File.createTempFile("temp", "." + suffix); + file.deleteOnExit(); + FileOutputStream fos = new SkipWriteOnAbortOutputStream(file); + ImageOutputStream ios = ImageIO.createImageOutputStream(fos); + writer.setOutput(ios); + writer.addIIOWriteProgressListener(this); + + writer.prepareWriteSequence(null); + boolean[] abortions = new boolean[] {true, false, true, false}; + for (int i = 0; i < 4; i++) { + abortFlag = abortions[i]; + isAbortCalled = false; + isCompleteCalled = false; + isProgressCalled = false; + isStartedCalled = false; + + TiledImage image = images[i]; + if (abortFlag) { + // This write will be aborted, and file will not be touched + writer.writeToSequence(new IIOImage(image, null, null), null); + if (!isStartedCalled) { + throw new RuntimeException("Started should be called"); + } + if (!isProgressCalled) { + throw new RuntimeException("Progress should be called"); + } + if (!isAbortCalled) { + throw new RuntimeException("Abort should be called"); + } + if (isCompleteCalled) { + throw new RuntimeException("Complete should not be called"); + } + } else { + // This write should be completed successfully and the file should + // contain correct image data. + writer.writeToSequence(new IIOImage(image, null, null), null); + if (!isStartedCalled) { + throw new RuntimeException("Started should be called"); + } + if (!isProgressCalled) { + throw new RuntimeException("Progress should be called"); + } + if (isAbortCalled) { + throw new RuntimeException("Abort should not be called"); + } + if (!isCompleteCalled) { + throw new RuntimeException("Complete should be called"); + } + } + } + + writer.endWriteSequence(); + writer.dispose(); + ios.close(); + + // Validates content of the file. + ImageReader reader = ImageIO.getImageReader(writer); + ImageInputStream iis = ImageIO.createImageInputStream(file); + reader.setInput(iis); + for (int i = 0; i < 2; i++) { + System.out.println("Testing image " + i); + BufferedImage imageRead = reader.read(i); + BufferedImage imageWrite = images[2 * i].getAsBufferedImage(); + for (int x = 0; x < WIDTH; ++x) { + for (int y = 0; y < HEIGHT; ++y) { + if (imageRead.getRGB(x, y) != imageWrite.getRGB(x, y)) { + throw new RuntimeException("Test failed for image " + i); + } + } + } + } + } + + public static void main(final String[] args) throws IOException { + WriteToSequenceAfterAbort writeAfterAbort = new WriteToSequenceAfterAbort(); + ImageWriter writer = ImageIO.getImageWritersByFormatName("TIFF").next(); + writeAfterAbort.test(writer); + System.out.println("Test passed."); + } + + // Callbacks + + @Override + public void imageComplete(ImageWriter source) { + isCompleteCalled = true; + } + + @Override + public void imageProgress(ImageWriter source, float percentageDone) { + isProgressCalled = true; + if (percentageDone > 50 && abortFlag) { + source.abort(); + } + } + + @Override + public void imageStarted(ImageWriter source, int imageIndex) { + isStartedCalled = true; + } + + @Override + public void writeAborted(final ImageWriter source) { + isAbortCalled = true; + } + + @Override + public void thumbnailComplete(ImageWriter source) { + } + + @Override + public void thumbnailProgress(ImageWriter source, float percentageDone) { + } + + @Override + public void thumbnailStarted(ImageWriter source, int imageIndex, + int thumbnailIndex) { + } + + /** + * We need to skip writes on abort, because content of the file after abort + * is undefined. + */ + private class SkipWriteOnAbortOutputStream extends FileOutputStream { + + SkipWriteOnAbortOutputStream(File file) throws FileNotFoundException { + super(file); + } + + @Override + public void write(int b) throws IOException { + if (!abortFlag) { + super.write(b); + } + } + + @Override + public void write(byte[] b) throws IOException { + if (!abortFlag) { + super.write(b); + } + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + if (!abortFlag) { + super.write(b, off, len); + } + } + } +} + From a3e2ce543cd9e7c283ef7a44905bbf829e913969 Mon Sep 17 00:00:00 2001 From: Brian Burkhalter Date: Fri, 11 Dec 2015 15:07:35 -0800 Subject: [PATCH 48/62] 8144997: "IIOException: Field data is past end-of-stream" when calling TIFFImageReader.read() Instead of failing for an IFD entry with bad type or offset, continue with the next entry. Reviewed-by: prr --- .../classes/com/sun/imageio/plugins/tiff/TIFFIFD.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFIFD.java b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFIFD.java index 7c14f8daf85..e8f4a5d05b3 100644 --- a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFIFD.java +++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFIFD.java @@ -475,9 +475,10 @@ public void initialize(ImageInputStream stream, boolean isPrimaryIFD, int sizeOfType; try { sizeOfType = TIFFTag.getSizeOfType(type); - } catch (IllegalArgumentException e) { - throw new IIOException("Illegal type " + type - + " for tag number " + tagNumber, e); + } catch (IllegalArgumentException ignored) { + // Continue with the next IFD entry. + stream.skipBytes(4); + continue; } int count = (int)stream.readUnsignedInt(); @@ -524,7 +525,7 @@ public void initialize(ImageInputStream stream, boolean isPrimaryIFD, // Check whether the the field value is within the stream. if (haveStreamLength && offset + size > streamLength) { - throw new IIOException("Field data is past end-of-stream"); + continue; } // Add a TIFFIFDEntry as a placeholder. This avoids a mark, From fb053862e58eb8380bb65f96e04afa99cf6342d8 Mon Sep 17 00:00:00 2001 From: Sebastian Sickelmann Date: Tue, 15 Dec 2015 17:35:27 +0100 Subject: [PATCH 49/62] 4906983: java.net.URL constructors throw MalformedURLException in undocumented way Added some testcases for the corner-cases in question and clarified javadoc Reviewed-by: chegar, msheppar --- .../java.base/share/classes/java/net/URL.java | 24 ++++++++++++------- jdk/test/java/net/URL/TestPort.java | 23 +++++++++++------- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/jdk/src/java.base/share/classes/java/net/URL.java b/jdk/src/java.base/share/classes/java/net/URL.java index 1a32a236ba0..73e488831a5 100644 --- a/jdk/src/java.base/share/classes/java/net/URL.java +++ b/jdk/src/java.base/share/classes/java/net/URL.java @@ -310,7 +310,8 @@ public final class URL implements java.io.Serializable { * @param host the name of the host. * @param port the port number on the host. * @param file the file on the host - * @exception MalformedURLException if an unknown protocol is specified. + * @exception MalformedURLException if an unknown protocol or the port + * is a negative number other than -1 * @see java.lang.System#getProperty(java.lang.String) * @see java.net.URL#setURLStreamHandlerFactory( * java.net.URLStreamHandlerFactory) @@ -329,9 +330,9 @@ public URL(String protocol, String host, int port, String file) * name, {@code host} name, and {@code file} name. The * default port for the specified protocol is used. *

    - * This method is equivalent to calling the four-argument - * constructor with the arguments being {@code protocol}, - * {@code host}, {@code -1}, and {@code file}. + * This constructor is equivalent to the four-argument + * constructor with the only difference of using the + * default port for the specified protocol. * * No validation of the inputs is performed by this constructor. * @@ -372,7 +373,8 @@ public URL(String protocol, String host, String file) * @param port the port number on the host. * @param file the file on the host * @param handler the stream handler for the URL. - * @exception MalformedURLException if an unknown protocol is specified. + * @exception MalformedURLException if an unknown protocol or the port + is a negative number other than -1 * @exception SecurityException * if a security manager exists and its * {@code checkPermission} method doesn't allow @@ -446,7 +448,9 @@ public URL(String protocol, String host, int port, String file, * * @param spec the {@code String} to parse as a URL. * @exception MalformedURLException if no protocol is specified, or an - * unknown protocol is found, or {@code spec} is {@code null}. + * unknown protocol is found, or {@code spec} is {@code null}, + * or the parsed URL fails to comply with the specific syntax + * of the associated protocol. * @see java.net.URL#URL(java.net.URL, java.lang.String) */ public URL(String spec) throws MalformedURLException { @@ -493,7 +497,9 @@ public URL(String spec) throws MalformedURLException { * @param context the context in which to parse the specification. * @param spec the {@code String} to parse as a URL. * @exception MalformedURLException if no protocol is specified, or an - * unknown protocol is found, or {@code spec} is {@code null}. + * unknown protocol is found, or {@code spec} is {@code null}, + * or the parsed URL fails to comply with the specific syntax + * of the associated protocol. * @see java.net.URL#URL(java.lang.String, java.lang.String, * int, java.lang.String) * @see java.net.URLStreamHandler @@ -513,7 +519,9 @@ public URL(URL context, String spec) throws MalformedURLException { * @param spec the {@code String} to parse as a URL. * @param handler the stream handler for the URL. * @exception MalformedURLException if no protocol is specified, or an - * unknown protocol is found, or {@code spec} is {@code null}. + * unknown protocol is found, or {@code spec} is {@code null}, + * or the parsed URL fails to comply with the specific syntax + * of the associated protocol. * @exception SecurityException * if a security manager exists and its * {@code checkPermission} method doesn't allow diff --git a/jdk/test/java/net/URL/TestPort.java b/jdk/test/java/net/URL/TestPort.java index ece89b48194..e018b658a19 100644 --- a/jdk/test/java/net/URL/TestPort.java +++ b/jdk/test/java/net/URL/TestPort.java @@ -23,28 +23,33 @@ /* * @test - * @bug 4101492 4444213 + * @bug 4101492 4444213 4906983 * @summary The java.net.URL constructor allows port < 0 and port > 65535 */ import java.net.*; public class TestPort { - public static void main(String[] args) { - URL url = null; + public static void main(String[] args) throws MalformedURLException { + // URLs are able to have port bigger than TCP-Port 65535 and + // to have no port (-1) at all.: + URL url = new URL("http","server",Integer.MAX_VALUE,"/path"); + url = new URL("http://server:"+Integer.MAX_VALUE+"/path"); + url = new URL("http://server/path"); + url = new URL("http","server",-1,"/path"); + try { url = new URL("ftp", "java.sun.com", -20, "/pub/"); + throw new RuntimeException("MalformedURLException not thrown!"); } catch (MalformedURLException e) { - url = null; + // Everything fine. MalformedURLException expected } - if (url != null) - throw new RuntimeException("MalformedURLException not thrown!"); + try { url = new URL("ftp://java.sun.com:-20/pub/"); + throw new RuntimeException("MalformedURLException not thrown!"); } catch (MalformedURLException e) { - url = null; + // Everything fine. MalformedURLException expected } - if (url != null) - throw new RuntimeException("MalformedURLException not thrown!"); } } From 5f1918e76e199d1eec5d5e3cc89c5a43079093f3 Mon Sep 17 00:00:00 2001 From: Claes Redestad Date: Mon, 21 Dec 2015 20:54:00 +0100 Subject: [PATCH 50/62] 8145680: Remove unnecessary explicit initialization of volatile variables in java.base Reviewed-by: alanb, chegar, jfranck, shade --- .../classes/sun/nio/ch/EPollSelectorImpl.java | 3 +- .../com/sun/crypto/provider/SunJCE.java | 2 +- .../classes/java/io/PipedInputStream.java | 6 ++-- .../share/classes/java/lang/Class.java | 27 ++++++++++-------- .../share/classes/java/lang/System.java | 11 ++++---- .../share/classes/java/lang/Thread.java | 8 ++---- .../classes/java/lang/ref/ReferenceQueue.java | 2 +- .../classes/java/lang/reflect/Parameter.java | 4 +-- .../java.base/share/classes/java/net/URI.java | 13 ++++----- .../share/classes/java/nio/Bits.java | 5 ++-- .../java/nio/channels/SelectionKey.java | 4 +-- .../spi/AbstractInterruptibleChannel.java | 18 +++++------- .../classes/java/nio/charset/Charset.java | 6 ++-- .../classes/java/security/SecureRandom.java | 10 ++++--- .../classes/java/text/DateFormatSymbols.java | 8 ++++-- .../java/text/DecimalFormatSymbols.java | 8 +----- .../share/classes/java/util/Locale.java | 28 ++++++++++--------- .../classes/java/util/regex/Pattern.java | 16 +++++------ .../share/classes/java/util/zip/ZipFile.java | 6 ++-- .../jdk/internal/logger/LazyLoggers.java | 10 ++++--- .../java.base/share/classes/sun/misc/VM.java | 7 ++--- .../classes/sun/net/www/http/HttpCapture.java | 10 +++---- .../classes/sun/net/www/http/HttpClient.java | 2 +- .../AsynchronousServerSocketChannelImpl.java | 10 +++---- .../nio/ch/AsynchronousSocketChannelImpl.java | 12 ++++---- .../sun/nio/ch/DatagramChannelImpl.java | 4 +-- .../sun/nio/ch/DatagramSocketAdaptor.java | 2 +- .../classes/sun/nio/ch/FileLockImpl.java | 10 +++---- .../classes/sun/nio/ch/MembershipKeyImpl.java | 7 ++--- .../share/classes/sun/nio/ch/Net.java | 3 +- .../sun/nio/ch/ServerSocketAdaptor.java | 2 +- .../sun/nio/ch/ServerSocketChannelImpl.java | 2 +- .../classes/sun/nio/ch/SocketAdaptor.java | 4 +-- .../classes/sun/nio/ch/SocketChannelImpl.java | 4 +-- .../share/classes/sun/nio/ch/Util.java | 9 ++---- .../classes/sun/nio/cs/StreamDecoder.java | 10 +++---- .../classes/sun/nio/cs/StreamEncoder.java | 10 +++---- .../sun/reflect/MethodAccessorGenerator.java | 6 ++-- .../AnnotationInvocationHandler.java | 2 +- .../sun/security/ssl/SSLSessionImpl.java | 2 +- .../sun/security/x509/X509CRLImpl.java | 17 ++++++----- .../sun/util/calendar/CalendarSystem.java | 9 +----- .../classes/sun/util/locale/BaseLocale.java | 6 ++-- .../provider/JRELocaleProviderAdapter.java | 28 +++++++++---------- .../provider/LocaleProviderAdapter.java | 2 +- .../resources/OpenListResourceBundle.java | 2 +- .../classes/sun/nio/ch/SinkChannelImpl.java | 2 +- .../classes/sun/nio/ch/SourceChannelImpl.java | 2 +- .../sun/nio/fs/MimeTypesFileTypeDetector.java | 2 +- .../sun/nio/ch/WindowsSelectorImpl.java | 2 +- 50 files changed, 183 insertions(+), 202 deletions(-) diff --git a/jdk/src/java.base/linux/classes/sun/nio/ch/EPollSelectorImpl.java b/jdk/src/java.base/linux/classes/sun/nio/ch/EPollSelectorImpl.java index 1528b9e16b8..d9ae5ebe334 100644 --- a/jdk/src/java.base/linux/classes/sun/nio/ch/EPollSelectorImpl.java +++ b/jdk/src/java.base/linux/classes/sun/nio/ch/EPollSelectorImpl.java @@ -29,7 +29,6 @@ import java.nio.channels.*; import java.nio.channels.spi.*; import java.util.*; -import sun.misc.*; /** * An implementation of Selector for Linux 2.6+ kernels that uses @@ -50,7 +49,7 @@ class EPollSelectorImpl private Map fdToKey; // True if this Selector has been closed - private volatile boolean closed = false; + private volatile boolean closed; // Lock for interrupt triggering and clearing private final Object interruptLock = new Object(); diff --git a/jdk/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java b/jdk/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java index b2352b48603..089a662dce5 100644 --- a/jdk/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java +++ b/jdk/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java @@ -93,7 +93,7 @@ public final class SunJCE extends Provider { // Instance of this provider, so we don't have to call the provider list // to find ourselves or run the risk of not being in the list. - private static volatile SunJCE instance = null; + private static volatile SunJCE instance; // lazy initialize SecureRandom to avoid potential recursion if Sun // provider has not been installed yet diff --git a/jdk/src/java.base/share/classes/java/io/PipedInputStream.java b/jdk/src/java.base/share/classes/java/io/PipedInputStream.java index 7f27fde0893..b55c1b08b75 100644 --- a/jdk/src/java.base/share/classes/java/io/PipedInputStream.java +++ b/jdk/src/java.base/share/classes/java/io/PipedInputStream.java @@ -48,9 +48,9 @@ * @since 1.0 */ public class PipedInputStream extends InputStream { - boolean closedByWriter = false; - volatile boolean closedByReader = false; - boolean connected = false; + boolean closedByWriter; + volatile boolean closedByReader; + boolean connected; /* REMIND: identification of the read and write sides needs to be more sophisticated. Either using thread groups (but what about diff --git a/jdk/src/java.base/share/classes/java/lang/Class.java b/jdk/src/java.base/share/classes/java/lang/Class.java index 77913c3187d..013b5843a1e 100644 --- a/jdk/src/java.base/share/classes/java/lang/Class.java +++ b/jdk/src/java.base/share/classes/java/lang/Class.java @@ -2518,7 +2518,7 @@ private static class ReflectionData { // Incremented by the VM on each call to JVM TI RedefineClasses() // that redefines this class or a superclass. - private transient volatile int classRedefinedCount = 0; + private transient volatile int classRedefinedCount; // Lazily create and cache ReflectionData private ReflectionData reflectionData() { @@ -3331,7 +3331,8 @@ public T[] getEnumConstants() { * uncloned, cached, and shared by all callers. */ T[] getEnumConstantsShared() { - if (enumConstants == null) { + T[] constants = enumConstants; + if (constants == null) { if (!isEnum()) return null; try { final Method values = getMethod("values"); @@ -3344,16 +3345,16 @@ public Void run() { }); @SuppressWarnings("unchecked") T[] temporaryConstants = (T[])values.invoke(null); - enumConstants = temporaryConstants; + enumConstants = constants = temporaryConstants; } // These can happen when users concoct enum-like classes // that don't comply with the enum spec. catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException ex) { return null; } } - return enumConstants; + return constants; } - private transient volatile T[] enumConstants = null; + private transient volatile T[] enumConstants; /** * Returns a map from simple name to enum constant. This package-private @@ -3363,19 +3364,21 @@ public Void run() { * created lazily on first use. Typically it won't ever get created. */ Map enumConstantDirectory() { - if (enumConstantDirectory == null) { + Map directory = enumConstantDirectory; + if (directory == null) { T[] universe = getEnumConstantsShared(); if (universe == null) throw new IllegalArgumentException( getName() + " is not an enum type"); - Map m = new HashMap<>(2 * universe.length); - for (T constant : universe) - m.put(((Enum)constant).name(), constant); - enumConstantDirectory = m; + directory = new HashMap<>(2 * universe.length); + for (T constant : universe) { + directory.put(((Enum)constant).name(), constant); + } + enumConstantDirectory = directory; } - return enumConstantDirectory; + return directory; } - private transient volatile Map enumConstantDirectory = null; + private transient volatile Map enumConstantDirectory; /** * Casts an object to the class or interface represented diff --git a/jdk/src/java.base/share/classes/java/lang/System.java b/jdk/src/java.base/share/classes/java/lang/System.java index cf44391082c..ea3ad405cfc 100644 --- a/jdk/src/java.base/share/classes/java/lang/System.java +++ b/jdk/src/java.base/share/classes/java/lang/System.java @@ -132,7 +132,7 @@ private System() { /* The security manager for the system. */ - private static volatile SecurityManager security = null; + private static volatile SecurityManager security; /** * Reassigns the "standard" input stream. @@ -206,7 +206,7 @@ public static void setErr(PrintStream err) { setErr0(err); } - private static volatile Console cons = null; + private static volatile Console cons; /** * Returns the unique {@link java.io.Console Console} object associated * with the current Java virtual machine, if any. @@ -216,12 +216,13 @@ public static void setErr(PrintStream err) { * @since 1.6 */ public static Console console() { - if (cons == null) { + Console c = cons; + if (c == null) { synchronized (System.class) { - cons = SharedSecrets.getJavaIOAccess().console(); + cons = c = SharedSecrets.getJavaIOAccess().console(); } } - return cons; + return c; } /** diff --git a/jdk/src/java.base/share/classes/java/lang/Thread.java b/jdk/src/java.base/share/classes/java/lang/Thread.java index e40aa6f0d51..56935eabd34 100644 --- a/jdk/src/java.base/share/classes/java/lang/Thread.java +++ b/jdk/src/java.base/share/classes/java/lang/Thread.java @@ -207,12 +207,10 @@ private static synchronized int nextThreadNum() { /* For generating thread ID */ private static long threadSeqNumber; - /* Java thread status for tools, - * initialized to indicate thread 'not yet started' + /* + * Java thread status for tools, default indicates thread 'not yet started' */ - - private volatile int threadStatus = 0; - + private volatile int threadStatus; private static synchronized long nextThreadID() { return ++threadSeqNumber; diff --git a/jdk/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java b/jdk/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java index d23491906c1..dcd50ae8575 100644 --- a/jdk/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java +++ b/jdk/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java @@ -53,7 +53,7 @@ boolean enqueue(Reference r) { private static class Lock { }; private Lock lock = new Lock(); - private volatile Reference head = null; + private volatile Reference head; private long queueLength = 0; boolean enqueue(Reference r) { /* Called only by Reference class */ diff --git a/jdk/src/java.base/share/classes/java/lang/reflect/Parameter.java b/jdk/src/java.base/share/classes/java/lang/reflect/Parameter.java index 5c178503fa8..005f0ddf5f3 100644 --- a/jdk/src/java.base/share/classes/java/lang/reflect/Parameter.java +++ b/jdk/src/java.base/share/classes/java/lang/reflect/Parameter.java @@ -205,7 +205,7 @@ public Type getParameterizedType() { return tmp; } - private transient volatile Type parameterTypeCache = null; + private transient volatile Type parameterTypeCache; /** * Returns a {@code Class} object that identifies the @@ -237,7 +237,7 @@ public AnnotatedType getAnnotatedType() { return executable.getAnnotatedParameterTypes()[index]; } - private transient volatile Class parameterClassCache = null; + private transient volatile Class parameterClassCache; /** * Returns {@code true} if this parameter is implicitly declared diff --git a/jdk/src/java.base/share/classes/java/net/URI.java b/jdk/src/java.base/share/classes/java/net/URI.java index 134a8b4380f..93fb98d906c 100644 --- a/jdk/src/java.base/share/classes/java/net/URI.java +++ b/jdk/src/java.base/share/classes/java/net/URI.java @@ -33,7 +33,6 @@ import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.CharsetDecoder; -import java.nio.charset.CharsetEncoder; import java.nio.charset.CoderResult; import java.nio.charset.CodingErrorAction; import java.nio.charset.CharacterCodingException; @@ -495,12 +494,12 @@ public final class URI private transient volatile String schemeSpecificPart; private transient volatile int hash; // Zero ==> undefined - private transient volatile String decodedUserInfo = null; - private transient volatile String decodedAuthority = null; - private transient volatile String decodedPath = null; - private transient volatile String decodedQuery = null; - private transient volatile String decodedFragment = null; - private transient volatile String decodedSchemeSpecificPart = null; + private transient volatile String decodedUserInfo; + private transient volatile String decodedAuthority; + private transient volatile String decodedPath; + private transient volatile String decodedQuery; + private transient volatile String decodedFragment; + private transient volatile String decodedSchemeSpecificPart; /** * The string form of this URI. diff --git a/jdk/src/java.base/share/classes/java/nio/Bits.java b/jdk/src/java.base/share/classes/java/nio/Bits.java index 526c71d3f8b..724fb0c01f5 100644 --- a/jdk/src/java.base/share/classes/java/nio/Bits.java +++ b/jdk/src/java.base/share/classes/java/nio/Bits.java @@ -25,9 +25,7 @@ package java.nio; -import java.security.AccessController; import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.atomic.LongAdder; import jdk.internal.misc.JavaNioAccess; import jdk.internal.misc.JavaLangRefAccess; @@ -603,7 +601,8 @@ static boolean unaligned() { private static final AtomicLong reservedMemory = new AtomicLong(); private static final AtomicLong totalCapacity = new AtomicLong(); private static final AtomicLong count = new AtomicLong(); - private static volatile boolean memoryLimitSet = false; + private static volatile boolean memoryLimitSet; + // max. number of sleeps during try-reserving with exponentially // increasing delay before throwing OutOfMemoryError: // 1, 2, 4, 8, 16, 32, 64, 128, 256 (total 511 ms ~ 0.5 s) diff --git a/jdk/src/java.base/share/classes/java/nio/channels/SelectionKey.java b/jdk/src/java.base/share/classes/java/nio/channels/SelectionKey.java index 86c133d192f..bd7c74de52a 100644 --- a/jdk/src/java.base/share/classes/java/nio/channels/SelectionKey.java +++ b/jdk/src/java.base/share/classes/java/nio/channels/SelectionKey.java @@ -26,8 +26,6 @@ package java.nio.channels; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; -import java.io.IOException; - /** * A token representing the registration of a {@link SelectableChannel} with a @@ -363,7 +361,7 @@ public final boolean isAcceptable() { // -- Attachments -- - private volatile Object attachment = null; + private volatile Object attachment; private static final AtomicReferenceFieldUpdater attachmentUpdater = AtomicReferenceFieldUpdater.newUpdater( diff --git a/jdk/src/java.base/share/classes/java/nio/channels/spi/AbstractInterruptibleChannel.java b/jdk/src/java.base/share/classes/java/nio/channels/spi/AbstractInterruptibleChannel.java index dcf54cb7d0c..cc82cb7a83c 100644 --- a/jdk/src/java.base/share/classes/java/nio/channels/spi/AbstractInterruptibleChannel.java +++ b/jdk/src/java.base/share/classes/java/nio/channels/spi/AbstractInterruptibleChannel.java @@ -29,11 +29,7 @@ package java.nio.channels.spi; import java.io.IOException; -import java.lang.reflect.Method; -import java.lang.reflect.InvocationTargetException; import java.nio.channels.*; -import java.security.AccessController; -import java.security.PrivilegedAction; import jdk.internal.misc.SharedSecrets; import sun.nio.ch.Interruptible; @@ -90,7 +86,7 @@ public abstract class AbstractInterruptibleChannel { private final Object closeLock = new Object(); - private volatile boolean open = true; + private volatile boolean closed; /** * Initializes a new instance of this class. @@ -110,9 +106,9 @@ protected AbstractInterruptibleChannel() { } */ public final void close() throws IOException { synchronized (closeLock) { - if (!open) + if (closed) return; - open = false; + closed = true; implCloseChannel(); } } @@ -136,7 +132,7 @@ public final void close() throws IOException { protected abstract void implCloseChannel() throws IOException; public final boolean isOpen() { - return open; + return !closed; } @@ -158,9 +154,9 @@ protected final void begin() { interruptor = new Interruptible() { public void interrupt(Thread target) { synchronized (closeLock) { - if (!open) + if (closed) return; - open = false; + closed = true; interrupted = target; try { AbstractInterruptibleChannel.this.implCloseChannel(); @@ -202,7 +198,7 @@ protected final void end(boolean completed) this.interrupted = null; throw new ClosedByInterruptException(); } - if (!completed && !open) + if (!completed && closed) throw new AsynchronousCloseException(); } diff --git a/jdk/src/java.base/share/classes/java/nio/charset/Charset.java b/jdk/src/java.base/share/classes/java/nio/charset/Charset.java index 498ed365213..d48f5a972db 100644 --- a/jdk/src/java.base/share/classes/java/nio/charset/Charset.java +++ b/jdk/src/java.base/share/classes/java/nio/charset/Charset.java @@ -276,7 +276,7 @@ public abstract class Charset /* -- Static methods -- */ - private static volatile String bugLevel = null; + private static volatile String bugLevel; static boolean atBugLevel(String bl) { // package-private String level = bugLevel; @@ -324,8 +324,8 @@ private static void checkName(String s) { // Cache of the most-recently-returned charsets, // along with the names that were used to find them // - private static volatile Object[] cache1 = null; // "Level 1" cache - private static volatile Object[] cache2 = null; // "Level 2" cache + private static volatile Object[] cache1; // "Level 1" cache + private static volatile Object[] cache2; // "Level 2" cache private static void cache(String charsetName, Charset cs) { cache2 = cache1; diff --git a/jdk/src/java.base/share/classes/java/security/SecureRandom.java b/jdk/src/java.base/share/classes/java/security/SecureRandom.java index a2a246f4a14..543b7abd98f 100644 --- a/jdk/src/java.base/share/classes/java/security/SecureRandom.java +++ b/jdk/src/java.base/share/classes/java/security/SecureRandom.java @@ -124,7 +124,7 @@ public class SecureRandom extends java.util.Random { private String algorithm; // Seed Generator - private static volatile SecureRandom seedGenerator = null; + private static volatile SecureRandom seedGenerator; /** * Constructs a secure random number generator (RNG) implementing the @@ -522,10 +522,12 @@ protected final int next(int numBits) { * @see #setSeed */ public static byte[] getSeed(int numBytes) { - if (seedGenerator == null) { - seedGenerator = new SecureRandom(); + SecureRandom seedGen = seedGenerator; + if (seedGen == null) { + seedGen = new SecureRandom(); + seedGenerator = seedGen; } - return seedGenerator.generateSeed(numBytes); + return seedGen.generateSeed(numBytes); } /** diff --git a/jdk/src/java.base/share/classes/java/text/DateFormatSymbols.java b/jdk/src/java.base/share/classes/java/text/DateFormatSymbols.java index c1f951d5bf8..955415af8b6 100644 --- a/jdk/src/java.base/share/classes/java/text/DateFormatSymbols.java +++ b/jdk/src/java.base/share/classes/java/text/DateFormatSymbols.java @@ -630,7 +630,9 @@ public int hashCode() { hashCode = 11 * hashCode + Arrays.hashCode(ampms); hashCode = 11 * hashCode + Arrays.deepHashCode(getZoneStringsWrapper()); hashCode = 11 * hashCode + Objects.hashCode(localPatternChars); - cachedHashCode = hashCode; + if (hashCode != 0) { + cachedHashCode = hashCode; + } } return hashCode; @@ -670,12 +672,12 @@ public boolean equals(Object obj) private static final ConcurrentMap> cachedInstances = new ConcurrentHashMap<>(3); - private transient int lastZoneIndex = 0; + private transient int lastZoneIndex; /** * Cached hash code */ - transient volatile int cachedHashCode = 0; + transient volatile int cachedHashCode; private void initializeData(Locale desiredLocale) { locale = desiredLocale; diff --git a/jdk/src/java.base/share/classes/java/text/DecimalFormatSymbols.java b/jdk/src/java.base/share/classes/java/text/DecimalFormatSymbols.java index 6727197ef81..cf7e7e5ffc7 100644 --- a/jdk/src/java.base/share/classes/java/text/DecimalFormatSymbols.java +++ b/jdk/src/java.base/share/classes/java/text/DecimalFormatSymbols.java @@ -42,14 +42,8 @@ import java.io.ObjectInputStream; import java.io.Serializable; import java.text.spi.DecimalFormatSymbolsProvider; -import java.util.ArrayList; import java.util.Currency; -import java.util.List; import java.util.Locale; -import java.util.MissingResourceException; -import java.util.ResourceBundle; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; import sun.util.locale.provider.LocaleProviderAdapter; import sun.util.locale.provider.LocaleServiceProviderPool; import sun.util.locale.provider.ResourceBundleBasedAdapter; @@ -875,7 +869,7 @@ private void readObject(ObjectInputStream stream) // currency; only the ISO code is serialized. private transient Currency currency; - private transient volatile boolean currencyInitialized = false; + private transient volatile boolean currencyInitialized; // Proclaim JDK 1.1 FCS compatibility static final long serialVersionUID = 5772796243397350300L; diff --git a/jdk/src/java.base/share/classes/java/util/Locale.java b/jdk/src/java.base/share/classes/java/util/Locale.java index d01891be0c5..25697ca0c9d 100644 --- a/jdk/src/java.base/share/classes/java/util/Locale.java +++ b/jdk/src/java.base/share/classes/java/util/Locale.java @@ -62,7 +62,6 @@ import sun.util.locale.provider.LocaleProviderAdapter; import sun.util.locale.provider.LocaleResources; import sun.util.locale.provider.LocaleServiceProviderPool; -import sun.util.locale.provider.ResourceBundleBasedAdapter; /** * A Locale object represents a specific geographical, political, @@ -2016,11 +2015,11 @@ public boolean equals(Object obj) { /** * Calculated hashcode */ - private transient volatile int hashCodeValue = 0; + private transient volatile int hashCodeValue; private static volatile Locale defaultLocale = initDefault(); - private static volatile Locale defaultDisplayLocale = null; - private static volatile Locale defaultFormatLocale = null; + private static volatile Locale defaultDisplayLocale; + private static volatile Locale defaultFormatLocale; private transient volatile String languageTag; @@ -2207,9 +2206,9 @@ private Object readResolve() throws java.io.ObjectStreamException { baseLocale.getRegion(), baseLocale.getVariant(), localeExtensions); } - private static volatile String[] isoLanguages = null; + private static volatile String[] isoLanguages; - private static volatile String[] isoCountries = null; + private static volatile String[] isoCountries; private static String convertOldISOCodes(String language) { // we accept both the old and the new ISO codes for the languages whose ISO @@ -2851,7 +2850,7 @@ public static final class LanguageRange { private final String range; private final double weight; - private volatile int hash = 0; + private volatile int hash; /** * Constructs a {@code LanguageRange} using the given {@code range}. @@ -3108,14 +3107,17 @@ public static List mapEquivalents( */ @Override public int hashCode() { - if (hash == 0) { - int result = 17; - result = 37*result + range.hashCode(); + int h = hash; + if (h == 0) { + h = 17; + h = 37*h + range.hashCode(); long bitsWeight = Double.doubleToLongBits(weight); - result = 37*result + (int)(bitsWeight ^ (bitsWeight >>> 32)); - hash = result; + h = 37*h + (int)(bitsWeight ^ (bitsWeight >>> 32)); + if (h != 0) { + hash = h; + } } - return hash; + return h; } /** diff --git a/jdk/src/java.base/share/classes/java/util/regex/Pattern.java b/jdk/src/java.base/share/classes/java/util/regex/Pattern.java index 3a6ac6f56a8..511611f574b 100644 --- a/jdk/src/java.base/share/classes/java/util/regex/Pattern.java +++ b/jdk/src/java.base/share/classes/java/util/regex/Pattern.java @@ -950,7 +950,7 @@ public final class Pattern * Boolean indicating this Pattern is compiled; this is necessary in order * to lazily compile deserialized Patterns. */ - private transient volatile boolean compiled = false; + private transient volatile boolean compiled; /** * The normalized pattern string. @@ -1332,7 +1332,6 @@ private void readObject(java.io.ObjectInputStream s) localCount = 0; // if length > 0, the Pattern is lazily compiled - compiled = false; if (pattern.length() == 0) { root = new Start(lastAccept); matchRoot = lastAccept; @@ -1377,7 +1376,6 @@ private Pattern(String p, int f) { * equivalences of the characters. */ private void normalize() { - boolean inCharClass = false; int lastCodePoint = -1; // Convert pattern into normalized form @@ -1551,7 +1549,6 @@ private String[] producePermutations(String input) { // offset maintains the index in code units. loop: for(int x=0, offset=0; x=0; y--) { if (combClass[y] == combClass[x]) { continue loop; @@ -1566,8 +1563,7 @@ private String[] producePermutations(String input) { temp[index++] = prefix + sre; } String[] result = new String[index]; - for (int x=0; x namedGroups() { - if (namedGroups == null) - namedGroups = new HashMap<>(2); - return namedGroups; + Map groups = namedGroups; + if (groups == null) { + namedGroups = groups = new HashMap<>(2); + } + return groups; } /** diff --git a/jdk/src/java.base/share/classes/java/util/zip/ZipFile.java b/jdk/src/java.base/share/classes/java/util/zip/ZipFile.java index a25db018b78..9d9a776a010 100644 --- a/jdk/src/java.base/share/classes/java/util/zip/ZipFile.java +++ b/jdk/src/java.base/share/classes/java/util/zip/ZipFile.java @@ -72,7 +72,7 @@ class ZipFile implements ZipConstants, Closeable { private final String name; // zip file name - private volatile boolean closeRequested = false; + private volatile boolean closeRequested; private Source zsrc; private ZipCoder zc; @@ -366,7 +366,7 @@ public InputStream getInputStream(ZipEntry entry) throws IOException { } private class ZipFileInflaterInputStream extends InflaterInputStream { - private volatile boolean closeRequested = false; + private volatile boolean closeRequested; private boolean eof = false; private final ZipFileInputStream zfin; @@ -653,7 +653,7 @@ private void ensureOpenOrZipException() throws IOException { * (possibly compressed) zip file entry. */ private class ZipFileInputStream extends InputStream { - private volatile boolean closeRequested = false; + private volatile boolean closeRequested; private long pos; // current position within entry data protected long rem; // number of remaining bytes within entry protected long size; // uncompressed size of this entry diff --git a/jdk/src/java.base/share/classes/jdk/internal/logger/LazyLoggers.java b/jdk/src/java.base/share/classes/jdk/internal/logger/LazyLoggers.java index c59ff195cc4..c54fc1a55ff 100644 --- a/jdk/src/java.base/share/classes/jdk/internal/logger/LazyLoggers.java +++ b/jdk/src/java.base/share/classes/jdk/internal/logger/LazyLoggers.java @@ -326,20 +326,22 @@ PlatformLogger.Bridge platformProxy() { } // Do not expose this outside of this package. - private static volatile LoggerFinder provider = null; + private static volatile LoggerFinder provider; private static LoggerFinder accessLoggerFinder() { - if (provider == null) { + LoggerFinder prov = provider; + if (prov == null) { // no need to lock: it doesn't matter if we call // getLoggerFinder() twice - since LoggerFinder already caches // the result. // This is just an optimization to avoid the cost of calling // doPrivileged every time. final SecurityManager sm = System.getSecurityManager(); - provider = sm == null ? LoggerFinder.getLoggerFinder() : + prov = sm == null ? LoggerFinder.getLoggerFinder() : AccessController.doPrivileged( (PrivilegedAction)LoggerFinder::getLoggerFinder); + provider = prov; } - return provider; + return prov; } // Avoid using lambda here as lazy loggers could be created early diff --git a/jdk/src/java.base/share/classes/sun/misc/VM.java b/jdk/src/java.base/share/classes/sun/misc/VM.java index 37dc3b38fa1..4c83dfaf049 100644 --- a/jdk/src/java.base/share/classes/sun/misc/VM.java +++ b/jdk/src/java.base/share/classes/sun/misc/VM.java @@ -27,9 +27,6 @@ import static java.lang.Thread.State.*; import java.util.Properties; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; public class VM { @@ -288,10 +285,10 @@ public static void initializeOSEnvironment() { } /* Current count of objects pending for finalization */ - private static volatile int finalRefCount = 0; + private static volatile int finalRefCount; /* Peak count of objects pending for finalization */ - private static volatile int peakFinalRefCount = 0; + private static volatile int peakFinalRefCount; /* * Gets the number of objects pending for finalization. diff --git a/jdk/src/java.base/share/classes/sun/net/www/http/HttpCapture.java b/jdk/src/java.base/share/classes/sun/net/www/http/HttpCapture.java index 43eff0435f4..13ede95ac41 100644 --- a/jdk/src/java.base/share/classes/sun/net/www/http/HttpCapture.java +++ b/jdk/src/java.base/share/classes/sun/net/www/http/HttpCapture.java @@ -54,12 +54,12 @@ * @author jccollet */ public class HttpCapture { - private File file = null; + private File file; private boolean incoming = true; - private BufferedWriter out = null; - private static boolean initialized = false; - private static volatile ArrayList patterns = null; - private static volatile ArrayList capFiles = null; + private BufferedWriter out; + private static boolean initialized; + private static volatile ArrayList patterns; + private static volatile ArrayList capFiles; private static synchronized void init() { initialized = true; diff --git a/jdk/src/java.base/share/classes/sun/net/www/http/HttpClient.java b/jdk/src/java.base/share/classes/sun/net/www/http/HttpClient.java index c0b0d629d42..920b48d4b64 100644 --- a/jdk/src/java.base/share/classes/sun/net/www/http/HttpClient.java +++ b/jdk/src/java.base/share/classes/sun/net/www/http/HttpClient.java @@ -98,7 +98,7 @@ private static int getDefaultPort(String proto) { // from previous releases. private static boolean retryPostProp = true; - volatile boolean keepingAlive = false; /* this is a keep-alive connection */ + volatile boolean keepingAlive; /* this is a keep-alive connection */ int keepAliveConnections = -1; /* number of keep-alives left */ /**Idle timeout value, in milliseconds. Zero means infinity, diff --git a/jdk/src/java.base/share/classes/sun/nio/ch/AsynchronousServerSocketChannelImpl.java b/jdk/src/java.base/share/classes/sun/nio/ch/AsynchronousServerSocketChannelImpl.java index 2d0ef217e3c..8ed2c9f1317 100644 --- a/jdk/src/java.base/share/classes/sun/nio/ch/AsynchronousServerSocketChannelImpl.java +++ b/jdk/src/java.base/share/classes/sun/nio/ch/AsynchronousServerSocketChannelImpl.java @@ -51,14 +51,14 @@ abstract class AsynchronousServerSocketChannelImpl protected final FileDescriptor fd; // the local address to which the channel's socket is bound - protected volatile InetSocketAddress localAddress = null; + protected volatile InetSocketAddress localAddress; // need this lock to set local address private final Object stateLock = new Object(); // close support private ReadWriteLock closeLock = new ReentrantReadWriteLock(); - private volatile boolean open = true; + private volatile boolean closed; // set true when accept operation is cancelled private volatile boolean acceptKilled; @@ -73,7 +73,7 @@ abstract class AsynchronousServerSocketChannelImpl @Override public final boolean isOpen() { - return open; + return !closed; } /** @@ -102,9 +102,9 @@ public final void close() throws IOException { // synchronize with any threads using file descriptor/handle closeLock.writeLock().lock(); try { - if (!open) + if (closed) return; // already closed - open = false; + closed = true; } finally { closeLock.writeLock().unlock(); } diff --git a/jdk/src/java.base/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java b/jdk/src/java.base/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java index 16a4d391881..3122b96a29f 100644 --- a/jdk/src/java.base/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java +++ b/jdk/src/java.base/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java @@ -54,8 +54,8 @@ abstract class AsynchronousSocketChannelImpl // protects state, localAddress, and remoteAddress protected final Object stateLock = new Object(); - protected volatile InetSocketAddress localAddress = null; - protected volatile InetSocketAddress remoteAddress = null; + protected volatile InetSocketAddress localAddress; + protected volatile InetSocketAddress remoteAddress; // State, increases monotonically static final int ST_UNINITIALIZED = -1; @@ -78,7 +78,7 @@ abstract class AsynchronousSocketChannelImpl // close support private final ReadWriteLock closeLock = new ReentrantReadWriteLock(); - private volatile boolean open = true; + private volatile boolean closed; // set true when exclusive binding is on and SO_REUSEADDR is emulated private boolean isReuseAddress; @@ -106,7 +106,7 @@ abstract class AsynchronousSocketChannelImpl @Override public final boolean isOpen() { - return open; + return !closed; } /** @@ -135,9 +135,9 @@ public final void close() throws IOException { // synchronize with any threads initiating asynchronous operations closeLock.writeLock().lock(); try { - if (!open) + if (closed) return; // already closed - open = false; + closed = true; } finally { closeLock.writeLock().unlock(); } diff --git a/jdk/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java b/jdk/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java index 9d4a2e828c1..7eb987991c9 100644 --- a/jdk/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java +++ b/jdk/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java @@ -58,8 +58,8 @@ class DatagramChannelImpl private final ProtocolFamily family; // IDs of native threads doing reads and writes, for signalling - private volatile long readerThread = 0; - private volatile long writerThread = 0; + private volatile long readerThread; + private volatile long writerThread; // Cached InetAddress and port for unconnected DatagramChannels // used by receive0 diff --git a/jdk/src/java.base/share/classes/sun/nio/ch/DatagramSocketAdaptor.java b/jdk/src/java.base/share/classes/sun/nio/ch/DatagramSocketAdaptor.java index fe2ed230002..fa1eae73d59 100644 --- a/jdk/src/java.base/share/classes/sun/nio/ch/DatagramSocketAdaptor.java +++ b/jdk/src/java.base/share/classes/sun/nio/ch/DatagramSocketAdaptor.java @@ -46,7 +46,7 @@ public class DatagramSocketAdaptor private final DatagramChannelImpl dc; // Timeout "option" value for receives - private volatile int timeout = 0; + private volatile int timeout; // ## super will create a useless impl private DatagramSocketAdaptor(DatagramChannelImpl dc) throws IOException { diff --git a/jdk/src/java.base/share/classes/sun/nio/ch/FileLockImpl.java b/jdk/src/java.base/share/classes/sun/nio/ch/FileLockImpl.java index 05bc5764062..1b00761f9f2 100644 --- a/jdk/src/java.base/share/classes/sun/nio/ch/FileLockImpl.java +++ b/jdk/src/java.base/share/classes/sun/nio/ch/FileLockImpl.java @@ -31,7 +31,7 @@ public class FileLockImpl extends FileLock { - private volatile boolean valid = true; + private volatile boolean invalid; FileLockImpl(FileChannel channel, long position, long size, boolean shared) { @@ -44,25 +44,25 @@ public class FileLockImpl } public boolean isValid() { - return valid; + return !invalid; } void invalidate() { assert Thread.holdsLock(this); - valid = false; + invalid = true; } public synchronized void release() throws IOException { Channel ch = acquiredBy(); if (!ch.isOpen()) throw new ClosedChannelException(); - if (valid) { + if (isValid()) { if (ch instanceof FileChannelImpl) ((FileChannelImpl)ch).release(this); else if (ch instanceof AsynchronousFileChannelImpl) ((AsynchronousFileChannelImpl)ch).release(this); else throw new AssertionError(); - valid = false; + invalidate(); } } } diff --git a/jdk/src/java.base/share/classes/sun/nio/ch/MembershipKeyImpl.java b/jdk/src/java.base/share/classes/sun/nio/ch/MembershipKeyImpl.java index 4d47b41ad9b..abe0e7ad277 100644 --- a/jdk/src/java.base/share/classes/sun/nio/ch/MembershipKeyImpl.java +++ b/jdk/src/java.base/share/classes/sun/nio/ch/MembershipKeyImpl.java @@ -43,8 +43,7 @@ class MembershipKeyImpl private final NetworkInterface interf; private final InetAddress source; - // true when key is valid - private volatile boolean valid = true; + private volatile boolean invalid; // lock used when creating or accessing blockedSet private Object stateLock = new Object(); @@ -134,12 +133,12 @@ byte[] source() { } public boolean isValid() { - return valid; + return !invalid; } // package-private void invalidate() { - valid = false; + invalid = true; } public void drop() { diff --git a/jdk/src/java.base/share/classes/sun/nio/ch/Net.java b/jdk/src/java.base/share/classes/sun/nio/ch/Net.java index 525d3a1224c..27c46a9ca2c 100644 --- a/jdk/src/java.base/share/classes/sun/nio/ch/Net.java +++ b/jdk/src/java.base/share/classes/sun/nio/ch/Net.java @@ -32,7 +32,6 @@ import java.util.*; import java.security.AccessController; import java.security.PrivilegedAction; -import java.security.PrivilegedExceptionAction; import sun.net.ExtendedOptionsImpl; @@ -55,7 +54,7 @@ public String name() { // -- Miscellaneous utilities -- - private static volatile boolean checkedIPv6 = false; + private static volatile boolean checkedIPv6; private static volatile boolean isIPv6Available; /** diff --git a/jdk/src/java.base/share/classes/sun/nio/ch/ServerSocketAdaptor.java b/jdk/src/java.base/share/classes/sun/nio/ch/ServerSocketAdaptor.java index e7a2cbb73be..11d16b6068f 100644 --- a/jdk/src/java.base/share/classes/sun/nio/ch/ServerSocketAdaptor.java +++ b/jdk/src/java.base/share/classes/sun/nio/ch/ServerSocketAdaptor.java @@ -45,7 +45,7 @@ public class ServerSocketAdaptor // package-private private final ServerSocketChannelImpl ssc; // Timeout "option" value for accepts - private volatile int timeout = 0; + private volatile int timeout; public static ServerSocket create(ServerSocketChannelImpl ssc) { try { diff --git a/jdk/src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java b/jdk/src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java index 0274c2e0753..2a427f1a352 100644 --- a/jdk/src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java +++ b/jdk/src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java @@ -54,7 +54,7 @@ class ServerSocketChannelImpl private int fdVal; // ID of native thread currently blocked in this channel, for signalling - private volatile long thread = 0; + private volatile long thread; // Lock held by thread currently blocked in this channel private final Object lock = new Object(); diff --git a/jdk/src/java.base/share/classes/sun/nio/ch/SocketAdaptor.java b/jdk/src/java.base/share/classes/sun/nio/ch/SocketAdaptor.java index d115b7aaf60..bf43c8b2317 100644 --- a/jdk/src/java.base/share/classes/sun/nio/ch/SocketAdaptor.java +++ b/jdk/src/java.base/share/classes/sun/nio/ch/SocketAdaptor.java @@ -26,13 +26,11 @@ package sun.nio.ch; import java.io.*; -import java.lang.ref.*; import java.net.*; import java.nio.*; import java.nio.channels.*; import java.security.AccessController; import java.security.PrivilegedExceptionAction; -import java.util.*; // Make a socket channel look like a socket. @@ -55,7 +53,7 @@ public class SocketAdaptor private final SocketChannelImpl sc; // Timeout "option" value for reads - private volatile int timeout = 0; + private volatile int timeout; private SocketAdaptor(SocketChannelImpl sc) throws SocketException { super((SocketImpl) null); diff --git a/jdk/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java b/jdk/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java index 0b3a3828ef3..c4965e1111b 100644 --- a/jdk/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java +++ b/jdk/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java @@ -56,8 +56,8 @@ class SocketChannelImpl private final int fdVal; // IDs of native threads doing reads and writes, for signalling - private volatile long readerThread = 0; - private volatile long writerThread = 0; + private volatile long readerThread; + private volatile long writerThread; // Lock held by current reading or connecting thread private final Object readLock = new Object(); diff --git a/jdk/src/java.base/share/classes/sun/nio/ch/Util.java b/jdk/src/java.base/share/classes/sun/nio/ch/Util.java index fd428149999..bb8df722927 100644 --- a/jdk/src/java.base/share/classes/sun/nio/ch/Util.java +++ b/jdk/src/java.base/share/classes/sun/nio/ch/Util.java @@ -25,13 +25,10 @@ package sun.nio.ch; -import java.lang.ref.SoftReference; import java.lang.reflect.*; -import java.io.IOException; import java.io.FileDescriptor; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; -import java.nio.channels.*; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.*; @@ -295,7 +292,7 @@ static int pageSize() { return pageSize; } - private static volatile Constructor directByteBufferConstructor = null; + private static volatile Constructor directByteBufferConstructor; private static void initDBBConstructor() { AccessController.doPrivileged(new PrivilegedAction() { @@ -340,7 +337,7 @@ static MappedByteBuffer newMappedByteBuffer(int size, long addr, return dbb; } - private static volatile Constructor directByteBufferRConstructor = null; + private static volatile Constructor directByteBufferRConstructor; private static void initDBBRConstructor() { AccessController.doPrivileged(new PrivilegedAction() { @@ -388,7 +385,7 @@ static MappedByteBuffer newMappedByteBufferR(int size, long addr, // -- Bug compatibility -- - private static volatile String bugLevel = null; + private static volatile String bugLevel; static boolean atBugLevel(String bl) { // package-private if (bugLevel == null) { diff --git a/jdk/src/java.base/share/classes/sun/nio/cs/StreamDecoder.java b/jdk/src/java.base/share/classes/sun/nio/cs/StreamDecoder.java index e878d6d4891..eedf00480aa 100644 --- a/jdk/src/java.base/share/classes/sun/nio/cs/StreamDecoder.java +++ b/jdk/src/java.base/share/classes/sun/nio/cs/StreamDecoder.java @@ -39,10 +39,10 @@ public class StreamDecoder extends Reader private static final int MIN_BYTE_BUFFER_SIZE = 32; private static final int DEFAULT_BYTE_BUFFER_SIZE = 8192; - private volatile boolean isOpen = true; + private volatile boolean closed; private void ensureOpen() throws IOException { - if (!isOpen) + if (closed) throw new IOException("Stream closed"); } @@ -188,15 +188,15 @@ public boolean ready() throws IOException { public void close() throws IOException { synchronized (lock) { - if (!isOpen) + if (closed) return; implClose(); - isOpen = false; + closed = true; } } private boolean isOpen() { - return isOpen; + return !closed; } diff --git a/jdk/src/java.base/share/classes/sun/nio/cs/StreamEncoder.java b/jdk/src/java.base/share/classes/sun/nio/cs/StreamEncoder.java index b41f93c038c..ccf3c63dbd0 100644 --- a/jdk/src/java.base/share/classes/sun/nio/cs/StreamEncoder.java +++ b/jdk/src/java.base/share/classes/sun/nio/cs/StreamEncoder.java @@ -38,10 +38,10 @@ public class StreamEncoder extends Writer private static final int DEFAULT_BYTE_BUFFER_SIZE = 8192; - private volatile boolean isOpen = true; + private volatile boolean closed; private void ensureOpen() throws IOException { - if (!isOpen) + if (closed) throw new IOException("Stream closed"); } @@ -156,15 +156,15 @@ public void flush() throws IOException { public void close() throws IOException { synchronized (lock) { - if (!isOpen) + if (closed) return; implClose(); - isOpen = false; + closed = true; } } private boolean isOpen() { - return isOpen; + return !closed; } diff --git a/jdk/src/java.base/share/classes/sun/reflect/MethodAccessorGenerator.java b/jdk/src/java.base/share/classes/sun/reflect/MethodAccessorGenerator.java index dfecd17423e..68e1a7c0c43 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/MethodAccessorGenerator.java +++ b/jdk/src/java.base/share/classes/sun/reflect/MethodAccessorGenerator.java @@ -44,9 +44,9 @@ class MethodAccessorGenerator extends AccessorGenerator { // Only used if forSerialization is true private static final short NUM_SERIALIZATION_CPOOL_ENTRIES = (short) 2; - private static volatile int methodSymnum = 0; - private static volatile int constructorSymnum = 0; - private static volatile int serializationConstructorSymnum = 0; + private static volatile int methodSymnum; + private static volatile int constructorSymnum; + private static volatile int serializationConstructorSymnum; private Class declaringClass; private Class[] parameterTypes; diff --git a/jdk/src/java.base/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java b/jdk/src/java.base/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java index da509b6081a..2f6bdee2f93 100644 --- a/jdk/src/java.base/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java +++ b/jdk/src/java.base/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java @@ -299,7 +299,7 @@ public Method[] run() { }}); } - private transient volatile Method[] memberMethods = null; + private transient volatile Method[] memberMethods; /** * Validates that a method is structurally appropriate for an diff --git a/jdk/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java b/jdk/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java index d021ec17ef4..b566b892eb9 100644 --- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java +++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java @@ -130,7 +130,7 @@ final class SSLSessionImpl extends ExtendedSSLSession { * also since counters make shorter debugging IDs than the big ones * we use in the protocol for uniqueness-over-time. */ - private static volatile int counter = 0; + private static volatile int counter; /* * Use of session caches is globally enabled/disabled. diff --git a/jdk/src/java.base/share/classes/sun/security/x509/X509CRLImpl.java b/jdk/src/java.base/share/classes/sun/security/x509/X509CRLImpl.java index 8b716798f64..812778c02c0 100644 --- a/jdk/src/java.base/share/classes/sun/security/x509/X509CRLImpl.java +++ b/jdk/src/java.base/share/classes/sun/security/x509/X509CRLImpl.java @@ -1290,7 +1290,7 @@ private static final class X509IssuerSerial implements Comparable { final X500Principal issuer; final BigInteger serial; - volatile int hashcode = 0; + volatile int hashcode; /** * Create an X509IssuerSerial. @@ -1358,13 +1358,16 @@ public boolean equals(Object o) { * @return the hash code value */ public int hashCode() { - if (hashcode == 0) { - int result = 17; - result = 37*result + issuer.hashCode(); - result = 37*result + serial.hashCode(); - hashcode = result; + int h = hashcode; + if (h == 0) { + h = 17; + h = 37*h + issuer.hashCode(); + h = 37*h + serial.hashCode(); + if (h != 0) { + hashcode = h; + } } - return hashcode; + return h; } @Override diff --git a/jdk/src/java.base/share/classes/sun/util/calendar/CalendarSystem.java b/jdk/src/java.base/share/classes/sun/util/calendar/CalendarSystem.java index 535f9159293..59fae3b7c0e 100644 --- a/jdk/src/java.base/share/classes/sun/util/calendar/CalendarSystem.java +++ b/jdk/src/java.base/share/classes/sun/util/calendar/CalendarSystem.java @@ -25,13 +25,6 @@ package sun.util.calendar; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.security.AccessController; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; -import java.util.Properties; import java.util.TimeZone; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -76,7 +69,7 @@ public abstract class CalendarSystem { /////////////////////// Calendar Factory Methods ///////////////////////// - private static volatile boolean initialized = false; + private static volatile boolean initialized; // Map of calendar names and calendar class names private static ConcurrentMap names; diff --git a/jdk/src/java.base/share/classes/sun/util/locale/BaseLocale.java b/jdk/src/java.base/share/classes/sun/util/locale/BaseLocale.java index 2f4da3d5960..62420d23351 100644 --- a/jdk/src/java.base/share/classes/sun/util/locale/BaseLocale.java +++ b/jdk/src/java.base/share/classes/sun/util/locale/BaseLocale.java @@ -46,7 +46,7 @@ public final class BaseLocale { private final String region; private final String variant; - private volatile int hash = 0; + private volatile int hash; // This method must be called only when creating the Locale.* constants. private BaseLocale(String language, String region) { @@ -147,7 +147,9 @@ public int hashCode() { h = 31 * h + script.hashCode(); h = 31 * h + region.hashCode(); h = 31 * h + variant.hashCode(); - hash = h; + if (h != 0) { + hash = h; + } } return h; } diff --git a/jdk/src/java.base/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java b/jdk/src/java.base/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java index 5cb48a97d36..c9cdba78edc 100644 --- a/jdk/src/java.base/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java +++ b/jdk/src/java.base/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java @@ -114,20 +114,20 @@ public

    P getLocaleServiceProvider(Class

    c) } } - private volatile BreakIteratorProvider breakIteratorProvider = null; - private volatile CollatorProvider collatorProvider = null; - private volatile DateFormatProvider dateFormatProvider = null; - private volatile DateFormatSymbolsProvider dateFormatSymbolsProvider = null; - private volatile DecimalFormatSymbolsProvider decimalFormatSymbolsProvider = null; - private volatile NumberFormatProvider numberFormatProvider = null; - - private volatile CurrencyNameProvider currencyNameProvider = null; - private volatile LocaleNameProvider localeNameProvider = null; - private volatile TimeZoneNameProvider timeZoneNameProvider = null; - private volatile CalendarDataProvider calendarDataProvider = null; - private volatile CalendarNameProvider calendarNameProvider = null; - - private volatile CalendarProvider calendarProvider = null; + private volatile BreakIteratorProvider breakIteratorProvider; + private volatile CollatorProvider collatorProvider; + private volatile DateFormatProvider dateFormatProvider; + private volatile DateFormatSymbolsProvider dateFormatSymbolsProvider; + private volatile DecimalFormatSymbolsProvider decimalFormatSymbolsProvider; + private volatile NumberFormatProvider numberFormatProvider; + + private volatile CurrencyNameProvider currencyNameProvider; + private volatile LocaleNameProvider localeNameProvider; + private volatile TimeZoneNameProvider timeZoneNameProvider; + private volatile CalendarDataProvider calendarDataProvider; + private volatile CalendarNameProvider calendarNameProvider; + + private volatile CalendarProvider calendarProvider; /* * Getter methods for java.text.spi.* providers diff --git a/jdk/src/java.base/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java b/jdk/src/java.base/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java index c17856ffe9a..e19a6c8c531 100644 --- a/jdk/src/java.base/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java +++ b/jdk/src/java.base/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java @@ -107,7 +107,7 @@ public String getTextResourcesPackage() { * Default fallback adapter type, which should return something meaningful in any case. * This is either CLDR or FALLBACK. */ - static volatile LocaleProviderAdapter.Type defaultLocaleProviderAdapter = null; + static volatile LocaleProviderAdapter.Type defaultLocaleProviderAdapter; /** * Adapter lookup cache. diff --git a/jdk/src/java.base/share/classes/sun/util/resources/OpenListResourceBundle.java b/jdk/src/java.base/share/classes/sun/util/resources/OpenListResourceBundle.java index ae1dce93f6b..4d31cf2834d 100644 --- a/jdk/src/java.base/share/classes/sun/util/resources/OpenListResourceBundle.java +++ b/jdk/src/java.base/share/classes/sun/util/resources/OpenListResourceBundle.java @@ -164,6 +164,6 @@ protected Set createSet() { return new HashSet<>(); } - private volatile Map lookup = null; + private volatile Map lookup; private volatile Set keyset; } diff --git a/jdk/src/java.base/unix/classes/sun/nio/ch/SinkChannelImpl.java b/jdk/src/java.base/unix/classes/sun/nio/ch/SinkChannelImpl.java index 8e437684f33..69e71c05f57 100644 --- a/jdk/src/java.base/unix/classes/sun/nio/ch/SinkChannelImpl.java +++ b/jdk/src/java.base/unix/classes/sun/nio/ch/SinkChannelImpl.java @@ -47,7 +47,7 @@ class SinkChannelImpl int fdVal; // ID of native thread doing write, for signalling - private volatile long thread = 0; + private volatile long thread; // Lock held by current reading thread private final Object lock = new Object(); diff --git a/jdk/src/java.base/unix/classes/sun/nio/ch/SourceChannelImpl.java b/jdk/src/java.base/unix/classes/sun/nio/ch/SourceChannelImpl.java index b1b4f9e36f0..7eea3ca0b72 100644 --- a/jdk/src/java.base/unix/classes/sun/nio/ch/SourceChannelImpl.java +++ b/jdk/src/java.base/unix/classes/sun/nio/ch/SourceChannelImpl.java @@ -47,7 +47,7 @@ class SourceChannelImpl int fdVal; // ID of native thread doing read, for signalling - private volatile long thread = 0; + private volatile long thread; // Lock held by current reading thread private final Object lock = new Object(); diff --git a/jdk/src/java.base/unix/classes/sun/nio/fs/MimeTypesFileTypeDetector.java b/jdk/src/java.base/unix/classes/sun/nio/fs/MimeTypesFileTypeDetector.java index 9eb683b2bd7..d58677502d2 100644 --- a/jdk/src/java.base/unix/classes/sun/nio/fs/MimeTypesFileTypeDetector.java +++ b/jdk/src/java.base/unix/classes/sun/nio/fs/MimeTypesFileTypeDetector.java @@ -52,7 +52,7 @@ class MimeTypesFileTypeDetector extends AbstractFileTypeDetector { private Map mimeTypeMap; // set to true when file loaded - private volatile boolean loaded = false; + private volatile boolean loaded; public MimeTypesFileTypeDetector(Path filePath) { mimeTypesFile = filePath; diff --git a/jdk/src/java.base/windows/classes/sun/nio/ch/WindowsSelectorImpl.java b/jdk/src/java.base/windows/classes/sun/nio/ch/WindowsSelectorImpl.java index 6fa5efb1cbd..cb74298d3dc 100644 --- a/jdk/src/java.base/windows/classes/sun/nio/ch/WindowsSelectorImpl.java +++ b/jdk/src/java.base/windows/classes/sun/nio/ch/WindowsSelectorImpl.java @@ -119,7 +119,7 @@ private static final class MapEntry { // Lock for interrupt triggering and clearing private final Object interruptLock = new Object(); - private volatile boolean interruptTriggered = false; + private volatile boolean interruptTriggered; WindowsSelectorImpl(SelectorProvider sp) throws IOException { super(sp); From aebd8a6c3256786598c53e0626fc303542194819 Mon Sep 17 00:00:00 2001 From: Roger Riggs Date: Mon, 21 Dec 2015 15:26:56 -0500 Subject: [PATCH 51/62] 8136597: java/lang/ProcessHandle/InfoTest.java fails Correct for truncated command line Reviewed-by: redestad --- jdk/test/java/lang/ProcessHandle/InfoTest.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/jdk/test/java/lang/ProcessHandle/InfoTest.java b/jdk/test/java/lang/ProcessHandle/InfoTest.java index e3182edb3f7..38b1546acc0 100644 --- a/jdk/test/java/lang/ProcessHandle/InfoTest.java +++ b/jdk/test/java/lang/ProcessHandle/InfoTest.java @@ -49,7 +49,7 @@ /* * @test - * @bug 8077350 8081566 8081567 8098852 + * @bug 8077350 8081566 8081567 8098852 8136597 * @build jdk.testlibrary.* * @library /lib/testlibrary * @summary Functions of ProcessHandle.Info @@ -210,10 +210,12 @@ public static void test2() { Assert.assertTrue(commandLine.contains(allArgs.get(i)), "commandLine() must contain argument: " + allArgs.get(i)); } - } else if (info.commandLine().isPresent()) { + } else if (info.commandLine().isPresent() && + command.isPresent() && + command.get().length() < info.commandLine().get().length()) { // If we only have the commandLine() we can only do some basic checks... String commandLine = info.commandLine().get(); - String javaExe = "java" + (Platform.isWindows() ? ".exe": ""); + String javaExe = "java" + (Platform.isWindows() ? ".exe" : ""); int pos = commandLine.indexOf(javaExe); Assert.assertTrue(pos > 0, "commandLine() should at least contain 'java'"); From 69bd68385cc378fea9d913eff9dab68a4ea9cbc7 Mon Sep 17 00:00:00 2001 From: Amy Lu Date: Tue, 22 Dec 2015 11:07:17 +0800 Subject: [PATCH 52/62] 8145869: Mark test JMXStartStopTest.java and TestJstatdServer.java as intermittently failing Reviewed-by: darcy --- .../sun/management/jmxremote/startstop/JMXStartStopTest.java | 2 +- jdk/test/sun/tools/jstatd/TestJstatdServer.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/jdk/test/sun/management/jmxremote/startstop/JMXStartStopTest.java b/jdk/test/sun/management/jmxremote/startstop/JMXStartStopTest.java index ca988cdea47..6348de21ef7 100644 --- a/jdk/test/sun/management/jmxremote/startstop/JMXStartStopTest.java +++ b/jdk/test/sun/management/jmxremote/startstop/JMXStartStopTest.java @@ -58,7 +58,7 @@ * @run main/othervm/timeout=600 -XX:+UsePerfData JMXStartStopTest * @summary Makes sure that enabling/disabling the management agent through JCMD * achieves the desired results - * @key randomness + * @key randomness intermittent */ public class JMXStartStopTest { private static final String TEST_APP_NAME = "TestApp"; diff --git a/jdk/test/sun/tools/jstatd/TestJstatdServer.java b/jdk/test/sun/tools/jstatd/TestJstatdServer.java index 4622ea64a59..565f799765d 100644 --- a/jdk/test/sun/tools/jstatd/TestJstatdServer.java +++ b/jdk/test/sun/tools/jstatd/TestJstatdServer.java @@ -24,6 +24,7 @@ /* * @test * @bug 4990825 + * @key intermittent * @library /lib/testlibrary * @modules java.management * @build jdk.testlibrary.* JstatdTest JstatGCUtilParser From 7e4d56677d48e02d2df999ed6105cc7566c03455 Mon Sep 17 00:00:00 2001 From: Chris Hegarty Date: Tue, 22 Dec 2015 12:17:25 +0000 Subject: [PATCH 53/62] 8145990: Move sun.misc math support classes to jdk.internal.math Reviewed-by: bpb, darcy --- .../classes/java/lang/AbstractStringBuilder.java | 2 +- .../share/classes/java/lang/Double.java | 4 ++-- .../java.base/share/classes/java/lang/Float.java | 6 +++--- .../java.base/share/classes/java/lang/Math.java | 4 ++-- .../share/classes/java/lang/StrictMath.java | 2 +- .../share/classes/java/math/BigInteger.java | 4 ++-- .../share/classes/java/text/DigitList.java | 2 +- .../share/classes/java/util/Formatter.java | 4 ++-- .../misc => jdk/internal/math}/DoubleConsts.java | 2 +- .../misc => jdk/internal/math}/FDBigInteger.java | 2 +- .../misc => jdk/internal/math}/FloatConsts.java | 2 +- .../internal/math}/FloatingDecimal.java | 2 +- .../internal/math}/FormattedFloatingDecimal.java | 2 +- .../classes/sun/java2d/marlin/FloatMath.java | 4 ++-- .../java/util/Formatter/Basic-X.java.template | 2 +- jdk/test/java/util/Formatter/Basic.java | 2 +- jdk/test/java/util/Formatter/BasicDouble.java | 6 +++++- .../FloatingDecimal/OldFDBigIntForTest.java | 2 +- .../OldFloatingDecimalForTest.java | 2 +- .../math}/FloatingDecimal/TestFDBigInteger.java | 6 +++--- .../FloatingDecimal/TestFloatingDecimal.java | 16 ++++++++-------- 21 files changed, 41 insertions(+), 37 deletions(-) rename jdk/src/java.base/share/classes/{sun/misc => jdk/internal/math}/DoubleConsts.java (99%) rename jdk/src/java.base/share/classes/{sun/misc => jdk/internal/math}/FDBigInteger.java (99%) rename jdk/src/java.base/share/classes/{sun/misc => jdk/internal/math}/FloatConsts.java (99%) rename jdk/src/java.base/share/classes/{sun/misc => jdk/internal/math}/FloatingDecimal.java (99%) rename jdk/src/java.base/share/classes/{sun/misc => jdk/internal/math}/FormattedFloatingDecimal.java (99%) rename jdk/test/{sun/misc => jdk/internal/math}/FloatingDecimal/OldFDBigIntForTest.java (99%) rename jdk/test/{sun/misc => jdk/internal/math}/FloatingDecimal/OldFloatingDecimalForTest.java (99%) rename jdk/test/{sun/misc => jdk/internal/math}/FloatingDecimal/TestFDBigInteger.java (99%) rename jdk/test/{sun/misc => jdk/internal/math}/FloatingDecimal/TestFloatingDecimal.java (96%) diff --git a/jdk/src/java.base/share/classes/java/lang/AbstractStringBuilder.java b/jdk/src/java.base/share/classes/java/lang/AbstractStringBuilder.java index 975c806bc81..619b7de278a 100644 --- a/jdk/src/java.base/share/classes/java/lang/AbstractStringBuilder.java +++ b/jdk/src/java.base/share/classes/java/lang/AbstractStringBuilder.java @@ -25,7 +25,7 @@ package java.lang; -import sun.misc.FloatingDecimal; +import jdk.internal.math.FloatingDecimal; import java.util.Arrays; import java.util.Spliterator; import java.util.stream.IntStream; diff --git a/jdk/src/java.base/share/classes/java/lang/Double.java b/jdk/src/java.base/share/classes/java/lang/Double.java index 88201873c36..f1389ebe0f8 100644 --- a/jdk/src/java.base/share/classes/java/lang/Double.java +++ b/jdk/src/java.base/share/classes/java/lang/Double.java @@ -25,8 +25,8 @@ package java.lang; -import sun.misc.FloatingDecimal; -import sun.misc.DoubleConsts; +import jdk.internal.math.FloatingDecimal; +import jdk.internal.math.DoubleConsts; import jdk.internal.HotSpotIntrinsicCandidate; /** diff --git a/jdk/src/java.base/share/classes/java/lang/Float.java b/jdk/src/java.base/share/classes/java/lang/Float.java index f9d3f9ef325..09eb63527ab 100644 --- a/jdk/src/java.base/share/classes/java/lang/Float.java +++ b/jdk/src/java.base/share/classes/java/lang/Float.java @@ -25,9 +25,9 @@ package java.lang; -import sun.misc.FloatingDecimal; -import sun.misc.FloatConsts; -import sun.misc.DoubleConsts; +import jdk.internal.math.FloatingDecimal; +import jdk.internal.math.FloatConsts; +import jdk.internal.math.DoubleConsts; import jdk.internal.HotSpotIntrinsicCandidate; /** diff --git a/jdk/src/java.base/share/classes/java/lang/Math.java b/jdk/src/java.base/share/classes/java/lang/Math.java index 1cd32bd3839..ddb953225a8 100644 --- a/jdk/src/java.base/share/classes/java/lang/Math.java +++ b/jdk/src/java.base/share/classes/java/lang/Math.java @@ -26,8 +26,8 @@ package java.lang; import java.util.Random; -import sun.misc.FloatConsts; -import sun.misc.DoubleConsts; +import jdk.internal.math.FloatConsts; +import jdk.internal.math.DoubleConsts; import jdk.internal.HotSpotIntrinsicCandidate; /** diff --git a/jdk/src/java.base/share/classes/java/lang/StrictMath.java b/jdk/src/java.base/share/classes/java/lang/StrictMath.java index 68bba059786..0c82f6afbf5 100644 --- a/jdk/src/java.base/share/classes/java/lang/StrictMath.java +++ b/jdk/src/java.base/share/classes/java/lang/StrictMath.java @@ -26,7 +26,7 @@ package java.lang; import java.util.Random; -import sun.misc.DoubleConsts; +import jdk.internal.math.DoubleConsts; import jdk.internal.HotSpotIntrinsicCandidate; /** diff --git a/jdk/src/java.base/share/classes/java/math/BigInteger.java b/jdk/src/java.base/share/classes/java/math/BigInteger.java index 1c44659daf4..c77731b2418 100644 --- a/jdk/src/java.base/share/classes/java/math/BigInteger.java +++ b/jdk/src/java.base/share/classes/java/math/BigInteger.java @@ -38,8 +38,8 @@ import java.util.Random; import java.util.concurrent.ThreadLocalRandom; -import sun.misc.DoubleConsts; -import sun.misc.FloatConsts; +import jdk.internal.math.DoubleConsts; +import jdk.internal.math.FloatConsts; import jdk.internal.HotSpotIntrinsicCandidate; /** diff --git a/jdk/src/java.base/share/classes/java/text/DigitList.java b/jdk/src/java.base/share/classes/java/text/DigitList.java index 0b73543a9d4..708e3e64575 100644 --- a/jdk/src/java.base/share/classes/java/text/DigitList.java +++ b/jdk/src/java.base/share/classes/java/text/DigitList.java @@ -41,7 +41,7 @@ import java.math.BigDecimal; import java.math.BigInteger; import java.math.RoundingMode; -import sun.misc.FloatingDecimal; +import jdk.internal.math.FloatingDecimal; /** * Digit List. Private to DecimalFormat. diff --git a/jdk/src/java.base/share/classes/java/util/Formatter.java b/jdk/src/java.base/share/classes/java/util/Formatter.java index 0a3b50b6a13..c771ac1ba0c 100644 --- a/jdk/src/java.base/share/classes/java/util/Formatter.java +++ b/jdk/src/java.base/share/classes/java/util/Formatter.java @@ -60,8 +60,8 @@ import java.time.temporal.TemporalQueries; import java.time.temporal.UnsupportedTemporalTypeException; -import sun.misc.DoubleConsts; -import sun.misc.FormattedFloatingDecimal; +import jdk.internal.math.DoubleConsts; +import jdk.internal.math.FormattedFloatingDecimal; /** * An interpreter for printf-style format strings. This class provides support diff --git a/jdk/src/java.base/share/classes/sun/misc/DoubleConsts.java b/jdk/src/java.base/share/classes/jdk/internal/math/DoubleConsts.java similarity index 99% rename from jdk/src/java.base/share/classes/sun/misc/DoubleConsts.java rename to jdk/src/java.base/share/classes/jdk/internal/math/DoubleConsts.java index 6ee80490102..c0480e9d497 100644 --- a/jdk/src/java.base/share/classes/sun/misc/DoubleConsts.java +++ b/jdk/src/java.base/share/classes/jdk/internal/math/DoubleConsts.java @@ -23,7 +23,7 @@ * questions. */ -package sun.misc; +package jdk.internal.math; /** * This class contains additional constants documenting limits of the diff --git a/jdk/src/java.base/share/classes/sun/misc/FDBigInteger.java b/jdk/src/java.base/share/classes/jdk/internal/math/FDBigInteger.java similarity index 99% rename from jdk/src/java.base/share/classes/sun/misc/FDBigInteger.java rename to jdk/src/java.base/share/classes/jdk/internal/math/FDBigInteger.java index d25f2017f6a..ba3ce9b8287 100644 --- a/jdk/src/java.base/share/classes/sun/misc/FDBigInteger.java +++ b/jdk/src/java.base/share/classes/jdk/internal/math/FDBigInteger.java @@ -22,7 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package sun.misc; +package jdk.internal.math; import java.math.BigInteger; import java.util.Arrays; diff --git a/jdk/src/java.base/share/classes/sun/misc/FloatConsts.java b/jdk/src/java.base/share/classes/jdk/internal/math/FloatConsts.java similarity index 99% rename from jdk/src/java.base/share/classes/sun/misc/FloatConsts.java rename to jdk/src/java.base/share/classes/jdk/internal/math/FloatConsts.java index 07396f8bca9..977a222278b 100644 --- a/jdk/src/java.base/share/classes/sun/misc/FloatConsts.java +++ b/jdk/src/java.base/share/classes/jdk/internal/math/FloatConsts.java @@ -23,7 +23,7 @@ * questions. */ -package sun.misc; +package jdk.internal.math; /** * This class contains additional constants documenting limits of the diff --git a/jdk/src/java.base/share/classes/sun/misc/FloatingDecimal.java b/jdk/src/java.base/share/classes/jdk/internal/math/FloatingDecimal.java similarity index 99% rename from jdk/src/java.base/share/classes/sun/misc/FloatingDecimal.java rename to jdk/src/java.base/share/classes/jdk/internal/math/FloatingDecimal.java index 0c07520f13e..575255eb174 100644 --- a/jdk/src/java.base/share/classes/sun/misc/FloatingDecimal.java +++ b/jdk/src/java.base/share/classes/jdk/internal/math/FloatingDecimal.java @@ -23,7 +23,7 @@ * questions. */ -package sun.misc; +package jdk.internal.math; import java.util.Arrays; import java.util.regex.*; diff --git a/jdk/src/java.base/share/classes/sun/misc/FormattedFloatingDecimal.java b/jdk/src/java.base/share/classes/jdk/internal/math/FormattedFloatingDecimal.java similarity index 99% rename from jdk/src/java.base/share/classes/sun/misc/FormattedFloatingDecimal.java rename to jdk/src/java.base/share/classes/jdk/internal/math/FormattedFloatingDecimal.java index fc53920e398..0a560dc11e5 100644 --- a/jdk/src/java.base/share/classes/sun/misc/FormattedFloatingDecimal.java +++ b/jdk/src/java.base/share/classes/jdk/internal/math/FormattedFloatingDecimal.java @@ -23,7 +23,7 @@ * questions. */ -package sun.misc; +package jdk.internal.math; import java.util.Arrays; diff --git a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/FloatMath.java b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/FloatMath.java index d1ffc04b786..df6af52438c 100644 --- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/FloatMath.java +++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/FloatMath.java @@ -24,8 +24,8 @@ */ package sun.java2d.marlin; -import sun.misc.DoubleConsts; -import sun.misc.FloatConsts; +import jdk.internal.math.DoubleConsts; +import jdk.internal.math.FloatConsts; /** * Faster Math ceil / floor routines derived from StrictMath diff --git a/jdk/test/java/util/Formatter/Basic-X.java.template b/jdk/test/java/util/Formatter/Basic-X.java.template index 57d77ae887d..206c3e0d27f 100644 --- a/jdk/test/java/util/Formatter/Basic-X.java.template +++ b/jdk/test/java/util/Formatter/Basic-X.java.template @@ -36,7 +36,7 @@ import java.math.BigInteger; import java.text.DateFormatSymbols; import java.util.*; #if[double] -import sun.misc.DoubleConsts; +import jdk.internal.math.DoubleConsts; #end[double] import static java.util.Calendar.*; diff --git a/jdk/test/java/util/Formatter/Basic.java b/jdk/test/java/util/Formatter/Basic.java index 4287f1bc2d8..8fb2e1de3a1 100644 --- a/jdk/test/java/util/Formatter/Basic.java +++ b/jdk/test/java/util/Formatter/Basic.java @@ -28,7 +28,7 @@ * 6344623 6369500 6534606 6282094 6286592 6476425 5063507 6469160 6476168 * 8059175 * - * @modules java.base/sun.misc + * @modules java.base/jdk.internal.math * @run shell/timeout=240 Basic.sh */ diff --git a/jdk/test/java/util/Formatter/BasicDouble.java b/jdk/test/java/util/Formatter/BasicDouble.java index 11760f17f7f..ba54d46be36 100644 --- a/jdk/test/java/util/Formatter/BasicDouble.java +++ b/jdk/test/java/util/Formatter/BasicDouble.java @@ -36,7 +36,7 @@ import java.text.DateFormatSymbols; import java.util.*; -import sun.misc.DoubleConsts; +import jdk.internal.math.DoubleConsts; import static java.util.Calendar.*; @@ -1169,6 +1169,10 @@ public static void test() { + + + + diff --git a/jdk/test/sun/misc/FloatingDecimal/OldFDBigIntForTest.java b/jdk/test/jdk/internal/math/FloatingDecimal/OldFDBigIntForTest.java similarity index 99% rename from jdk/test/sun/misc/FloatingDecimal/OldFDBigIntForTest.java rename to jdk/test/jdk/internal/math/FloatingDecimal/OldFDBigIntForTest.java index a7082ba76bc..f500040a922 100644 --- a/jdk/test/sun/misc/FloatingDecimal/OldFDBigIntForTest.java +++ b/jdk/test/jdk/internal/math/FloatingDecimal/OldFDBigIntForTest.java @@ -21,7 +21,7 @@ * questions. */ -//package sun.misc; +//package jdk.internal.math; /* * A really, really simple bigint package diff --git a/jdk/test/sun/misc/FloatingDecimal/OldFloatingDecimalForTest.java b/jdk/test/jdk/internal/math/FloatingDecimal/OldFloatingDecimalForTest.java similarity index 99% rename from jdk/test/sun/misc/FloatingDecimal/OldFloatingDecimalForTest.java rename to jdk/test/jdk/internal/math/FloatingDecimal/OldFloatingDecimalForTest.java index 63bbbf5eb14..073f2655e3a 100644 --- a/jdk/test/sun/misc/FloatingDecimal/OldFloatingDecimalForTest.java +++ b/jdk/test/jdk/internal/math/FloatingDecimal/OldFloatingDecimalForTest.java @@ -21,7 +21,7 @@ * questions. */ -//package sun.misc; +//package jdk.internal.math; import java.util.regex.*; diff --git a/jdk/test/sun/misc/FloatingDecimal/TestFDBigInteger.java b/jdk/test/jdk/internal/math/FloatingDecimal/TestFDBigInteger.java similarity index 99% rename from jdk/test/sun/misc/FloatingDecimal/TestFDBigInteger.java rename to jdk/test/jdk/internal/math/FloatingDecimal/TestFDBigInteger.java index 983f5d78cee..9eb8cfe7844 100644 --- a/jdk/test/sun/misc/FloatingDecimal/TestFDBigInteger.java +++ b/jdk/test/jdk/internal/math/FloatingDecimal/TestFDBigInteger.java @@ -23,13 +23,13 @@ import java.math.BigInteger; import java.util.Random; -import sun.misc.FDBigInteger; +import jdk.internal.math.FDBigInteger; /** * @test * @bug 7032154 - * @summary unit testys of sun.misc.FDBigInteger - * @modules java.base/sun.misc + * @summary unit testys of FDBigInteger + * @modules java.base/jdk.internal.math * @author Dmitry Nadezhin */ public class TestFDBigInteger { diff --git a/jdk/test/sun/misc/FloatingDecimal/TestFloatingDecimal.java b/jdk/test/jdk/internal/math/FloatingDecimal/TestFloatingDecimal.java similarity index 96% rename from jdk/test/sun/misc/FloatingDecimal/TestFloatingDecimal.java rename to jdk/test/jdk/internal/math/FloatingDecimal/TestFloatingDecimal.java index 96806a9b708..5fcb5b52b04 100644 --- a/jdk/test/sun/misc/FloatingDecimal/TestFloatingDecimal.java +++ b/jdk/test/jdk/internal/math/FloatingDecimal/TestFloatingDecimal.java @@ -22,7 +22,7 @@ */ import java.util.Random; -import sun.misc.FloatingDecimal; +import jdk.internal.math.FloatingDecimal; /* OldFloatingDecimalForTest @@ -40,26 +40,26 @@ public class OldFloatingDecimalForTest { public strictfp float floatValue(); } -sun.misc.FloatingDecimal +jdk.internal.math.FloatingDecimal -public class sun.misc.FloatingDecimal { - public sun.misc.FloatingDecimal(); +public class jdk.internal.math.FloatingDecimal { + public jdk.internal.math.FloatingDecimal(); public static java.lang.String toJavaFormatString(double); public static java.lang.String toJavaFormatString(float); public static void appendTo(double, java.lang.Appendable); public static void appendTo(float, java.lang.Appendable); public static double parseDouble(java.lang.String) throws java.lang.NumberFormatException; public static float parseFloat(java.lang.String) throws java.lang.NumberFormatException; - public static sun.misc.FloatingDecimal$AbstractD2ABuffer getD2ABuffer(double); + public static jdk.internal.math.FloatingDecimal$AbstractD2ABuffer getD2ABuffer(double); } */ /** * @test * @bug 7032154 - * @summary unit tests of sun.misc.FloatingDecimal - * @modules java.base/sun.misc - * @library ../../../java/lang/Math + * @summary unit tests of FloatingDecimal + * @modules java.base/jdk.internal.math + * @library /java/lang/Math * @build DoubleConsts FloatConsts * @run main TestFloatingDecimal * @author Brian Burkhalter From 22df7c453f8fd4497cc64960f073f1a4e66b4dce Mon Sep 17 00:00:00 2001 From: Claes Redestad Date: Tue, 22 Dec 2015 16:42:16 +0100 Subject: [PATCH 54/62] 8145988: Use the raw methods of java.net.URI when possible Reviewed-by: shade, chegar --- jdk/src/java.base/share/classes/java/io/File.java | 6 +++--- .../classes/sun/nio/fs/UnixFileSystemProvider.java | 11 ++++++----- .../unix/classes/sun/nio/fs/UnixUriUtils.java | 6 +++--- .../classes/sun/nio/fs/WindowsFileSystemProvider.java | 11 ++++++----- .../windows/classes/sun/nio/fs/WindowsUriSupport.java | 6 +++--- 5 files changed, 21 insertions(+), 19 deletions(-) diff --git a/jdk/src/java.base/share/classes/java/io/File.java b/jdk/src/java.base/share/classes/java/io/File.java index 6137d0acd29..089171bab9e 100644 --- a/jdk/src/java.base/share/classes/java/io/File.java +++ b/jdk/src/java.base/share/classes/java/io/File.java @@ -420,11 +420,11 @@ public File(URI uri) { String scheme = uri.getScheme(); if ((scheme == null) || !scheme.equalsIgnoreCase("file")) throw new IllegalArgumentException("URI scheme is not \"file\""); - if (uri.getAuthority() != null) + if (uri.getRawAuthority() != null) throw new IllegalArgumentException("URI has an authority component"); - if (uri.getFragment() != null) + if (uri.getRawFragment() != null) throw new IllegalArgumentException("URI has a fragment component"); - if (uri.getQuery() != null) + if (uri.getRawQuery() != null) throw new IllegalArgumentException("URI has a query component"); String p = uri.getPath(); if (p.equals("")) diff --git a/jdk/src/java.base/unix/classes/sun/nio/fs/UnixFileSystemProvider.java b/jdk/src/java.base/unix/classes/sun/nio/fs/UnixFileSystemProvider.java index 182a57b94c2..f161bd3e0bf 100644 --- a/jdk/src/java.base/unix/classes/sun/nio/fs/UnixFileSystemProvider.java +++ b/jdk/src/java.base/unix/classes/sun/nio/fs/UnixFileSystemProvider.java @@ -69,15 +69,16 @@ public final String getScheme() { private void checkUri(URI uri) { if (!uri.getScheme().equalsIgnoreCase(getScheme())) throw new IllegalArgumentException("URI does not match this provider"); - if (uri.getAuthority() != null) + if (uri.getRawAuthority() != null) throw new IllegalArgumentException("Authority component present"); - if (uri.getPath() == null) + String path = uri.getPath(); + if (path == null) throw new IllegalArgumentException("Path component is undefined"); - if (!uri.getPath().equals("/")) + if (!path.equals("/")) throw new IllegalArgumentException("Path component should be '/'"); - if (uri.getQuery() != null) + if (uri.getRawQuery() != null) throw new IllegalArgumentException("Query component present"); - if (uri.getFragment() != null) + if (uri.getRawFragment() != null) throw new IllegalArgumentException("Fragment component present"); } diff --git a/jdk/src/java.base/unix/classes/sun/nio/fs/UnixUriUtils.java b/jdk/src/java.base/unix/classes/sun/nio/fs/UnixUriUtils.java index b59ab4275ce..a0df3e3102e 100644 --- a/jdk/src/java.base/unix/classes/sun/nio/fs/UnixUriUtils.java +++ b/jdk/src/java.base/unix/classes/sun/nio/fs/UnixUriUtils.java @@ -49,11 +49,11 @@ static Path fromUri(UnixFileSystem fs, URI uri) { String scheme = uri.getScheme(); if ((scheme == null) || !scheme.equalsIgnoreCase("file")) throw new IllegalArgumentException("URI scheme is not \"file\""); - if (uri.getAuthority() != null) + if (uri.getRawAuthority() != null) throw new IllegalArgumentException("URI has an authority component"); - if (uri.getFragment() != null) + if (uri.getRawFragment() != null) throw new IllegalArgumentException("URI has a fragment component"); - if (uri.getQuery() != null) + if (uri.getRawQuery() != null) throw new IllegalArgumentException("URI has a query component"); // compatibility with java.io.File diff --git a/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsFileSystemProvider.java b/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsFileSystemProvider.java index cf0d23fea2b..847ef1564b5 100644 --- a/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsFileSystemProvider.java +++ b/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsFileSystemProvider.java @@ -61,15 +61,16 @@ public String getScheme() { private void checkUri(URI uri) { if (!uri.getScheme().equalsIgnoreCase(getScheme())) throw new IllegalArgumentException("URI does not match this provider"); - if (uri.getAuthority() != null) + if (uri.getRawAuthority() != null) throw new IllegalArgumentException("Authority component present"); - if (uri.getPath() == null) + String path = uri.getPath(); + if (path == null) throw new IllegalArgumentException("Path component is undefined"); - if (!uri.getPath().equals("/")) + if (!path.equals("/")) throw new IllegalArgumentException("Path component should be '/'"); - if (uri.getQuery() != null) + if (uri.getRawQuery() != null) throw new IllegalArgumentException("Query component present"); - if (uri.getFragment() != null) + if (uri.getRawFragment() != null) throw new IllegalArgumentException("Fragment component present"); } diff --git a/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsUriSupport.java b/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsUriSupport.java index 5748bbb02af..f95a913f448 100644 --- a/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsUriSupport.java +++ b/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsUriSupport.java @@ -123,16 +123,16 @@ static WindowsPath fromUri(WindowsFileSystem fs, URI uri) { String scheme = uri.getScheme(); if ((scheme == null) || !scheme.equalsIgnoreCase("file")) throw new IllegalArgumentException("URI scheme is not \"file\""); - if (uri.getFragment() != null) + if (uri.getRawFragment() != null) throw new IllegalArgumentException("URI has a fragment component"); - if (uri.getQuery() != null) + if (uri.getRawQuery() != null) throw new IllegalArgumentException("URI has a query component"); String path = uri.getPath(); if (path.equals("")) throw new IllegalArgumentException("URI path component is empty"); // UNC - String auth = uri.getAuthority(); + String auth = uri.getRawAuthority(); if (auth != null && !auth.equals("")) { String host = uri.getHost(); if (host == null) From c6afc47fc1a3793ed63c550af8742d6a88906da6 Mon Sep 17 00:00:00 2001 From: Claes Redestad Date: Tue, 22 Dec 2015 19:14:47 +0100 Subject: [PATCH 55/62] 8145862: Improve lazy initialization of fields in java.net.URI Reviewed-by: shade, chegar --- .../java.base/share/classes/java/net/URI.java | 259 +++++++++--------- 1 file changed, 135 insertions(+), 124 deletions(-) diff --git a/jdk/src/java.base/share/classes/java/net/URI.java b/jdk/src/java.base/share/classes/java/net/URI.java index 93fb98d906c..e896568c7a1 100644 --- a/jdk/src/java.base/share/classes/java/net/URI.java +++ b/jdk/src/java.base/share/classes/java/net/URI.java @@ -489,17 +489,17 @@ public final class URI private transient String path; // null ==> opaque private transient String query; - // The remaining fields may be computed on demand - - private transient volatile String schemeSpecificPart; - private transient volatile int hash; // Zero ==> undefined - - private transient volatile String decodedUserInfo; - private transient volatile String decodedAuthority; - private transient volatile String decodedPath; - private transient volatile String decodedQuery; - private transient volatile String decodedFragment; - private transient volatile String decodedSchemeSpecificPart; + // The remaining fields may be computed on demand, which is safe even in + // the face of multiple threads racing to initialize them + private transient String schemeSpecificPart; + private transient int hash; // Zero ==> undefined + + private transient String decodedUserInfo; + private transient String decodedAuthority; + private transient String decodedPath; + private transient String decodedQuery; + private transient String decodedFragment; + private transient String decodedSchemeSpecificPart; /** * The string form of this URI. @@ -910,8 +910,7 @@ public URI parseServerAuthority() // either more fields or a more-obscure representation. if ((host != null) || (authority == null)) return this; - defineString(); - new Parser(string).parse(true); + new Parser(toString()).parse(true); return this; } @@ -1143,8 +1142,17 @@ public boolean isOpaque() { * (never {@code null}) */ public String getRawSchemeSpecificPart() { - defineSchemeSpecificPart(); - return schemeSpecificPart; + String part = schemeSpecificPart; + if (part != null) { + return part; + } + StringBuilder sb = new StringBuilder(); + appendSchemeSpecificPart(sb, null, getAuthority(), getUserInfo(), + host, port, getPath(), getQuery()); + if (sb.length() == 0) { + return null; + } + return schemeSpecificPart = sb.toString(); } /** @@ -1159,9 +1167,11 @@ public String getRawSchemeSpecificPart() { * (never {@code null}) */ public String getSchemeSpecificPart() { - if (decodedSchemeSpecificPart == null) - decodedSchemeSpecificPart = decode(getRawSchemeSpecificPart()); - return decodedSchemeSpecificPart; + String part = decodedSchemeSpecificPart; + if (part == null) { + decodedSchemeSpecificPart = part = decode(getRawSchemeSpecificPart()); + } + return part; } /** @@ -1192,9 +1202,11 @@ public String getRawAuthority() { * or {@code null} if the authority is undefined */ public String getAuthority() { - if (decodedAuthority == null) - decodedAuthority = decode(authority); - return decodedAuthority; + String auth = decodedAuthority; + if ((auth == null) && (authority != null)) { + decodedAuthority = auth = decode(authority); + } + return auth; } /** @@ -1222,9 +1234,11 @@ public String getRawUserInfo() { * or {@code null} if the user information is undefined */ public String getUserInfo() { - if ((decodedUserInfo == null) && (userInfo != null)) - decodedUserInfo = decode(userInfo); - return decodedUserInfo; + String user = decodedUserInfo; + if ((user == null) && (userInfo != null)) { + decodedUserInfo = user = decode(userInfo); + } + return user; } /** @@ -1306,9 +1320,11 @@ public String getRawPath() { * or {@code null} if the path is undefined */ public String getPath() { - if ((decodedPath == null) && (path != null)) - decodedPath = decode(path); - return decodedPath; + String decoded = decodedPath; + if ((decoded == null) && (path != null)) { + decodedPath = decoded = decode(path); + } + return decoded; } /** @@ -1335,9 +1351,11 @@ public String getRawQuery() { * or {@code null} if the query is undefined */ public String getQuery() { - if ((decodedQuery == null) && (query != null)) - decodedQuery = decode(query, false); - return decodedQuery; + String decoded = decodedQuery; + if ((decoded == null) && (query != null)) { + decodedQuery = decoded = decode(query, false); + } + return decoded; } /** @@ -1364,9 +1382,11 @@ public String getRawFragment() { * or {@code null} if the fragment is undefined */ public String getFragment() { - if ((decodedFragment == null) && (fragment != null)) - decodedFragment = decode(fragment, false); - return decodedFragment; + String decoded = decodedFragment; + if ((decoded == null) && (fragment != null)) { + decodedFragment = decoded = decode(fragment, false); + } + return decoded; } @@ -1452,24 +1472,27 @@ public boolean equals(Object ob) { * @return A hash-code value for this URI */ public int hashCode() { - if (hash != 0) - return hash; - int h = hashIgnoringCase(0, scheme); - h = hash(h, fragment); - if (isOpaque()) { - h = hash(h, schemeSpecificPart); - } else { - h = hash(h, path); - h = hash(h, query); - if (host != null) { - h = hash(h, userInfo); - h = hashIgnoringCase(h, host); - h += 1949 * port; + int h = hash; + if (h == 0) { + h = hashIgnoringCase(0, scheme); + h = hash(h, fragment); + if (isOpaque()) { + h = hash(h, schemeSpecificPart); } else { - h = hash(h, authority); + h = hash(h, path); + h = hash(h, query); + if (host != null) { + h = hash(h, userInfo); + h = hashIgnoringCase(h, host); + h += 1949 * port; + } else { + h = hash(h, authority); + } + } + if (h != 0) { + hash = h; } } - hash = h; return h; } @@ -1599,8 +1622,59 @@ public int compareTo(URI that) { * @return The string form of this URI */ public String toString() { - defineString(); - return string; + String s = string; + if (s == null) { + s = defineString(); + } + return s; + } + + private String defineString() { + String s = string; + if (s != null) { + return s; + } + + StringBuilder sb = new StringBuilder(); + if (scheme != null) { + sb.append(scheme); + sb.append(':'); + } + if (isOpaque()) { + sb.append(schemeSpecificPart); + } else { + if (host != null) { + sb.append("//"); + if (userInfo != null) { + sb.append(userInfo); + sb.append('@'); + } + boolean needBrackets = ((host.indexOf(':') >= 0) + && !host.startsWith("[") + && !host.endsWith("]")); + if (needBrackets) sb.append('['); + sb.append(host); + if (needBrackets) sb.append(']'); + if (port != -1) { + sb.append(':'); + sb.append(port); + } + } else if (authority != null) { + sb.append("//"); + sb.append(authority); + } + if (path != null) + sb.append(path); + if (query != null) { + sb.append('?'); + sb.append(query); + } + } + if (fragment != null) { + sb.append('#'); + sb.append(fragment); + } + return string = sb.toString(); } /** @@ -1617,8 +1691,7 @@ public String toString() { * charset */ public String toASCIIString() { - defineString(); - return encode(string); + return encode(toString()); } @@ -1824,7 +1897,7 @@ private static void checkPath(String s, String scheme, String path) } } - private void appendAuthority(StringBuffer sb, + private void appendAuthority(StringBuilder sb, String authority, String userInfo, String host, @@ -1874,7 +1947,7 @@ private void appendAuthority(StringBuffer sb, } } - private void appendSchemeSpecificPart(StringBuffer sb, + private void appendSchemeSpecificPart(StringBuilder sb, String opaquePart, String authority, String userInfo, @@ -1915,7 +1988,7 @@ private void appendSchemeSpecificPart(StringBuffer sb, } } - private void appendFragment(StringBuffer sb, String fragment) { + private void appendFragment(StringBuilder sb, String fragment) { if (fragment != null) { sb.append('#'); sb.append(quote(fragment, L_URIC, H_URIC)); @@ -1932,7 +2005,7 @@ private String toString(String scheme, String query, String fragment) { - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); if (scheme != null) { sb.append(scheme); sb.append(':'); @@ -1944,61 +2017,6 @@ private String toString(String scheme, return sb.toString(); } - private void defineSchemeSpecificPart() { - if (schemeSpecificPart != null) return; - StringBuffer sb = new StringBuffer(); - appendSchemeSpecificPart(sb, null, getAuthority(), getUserInfo(), - host, port, getPath(), getQuery()); - if (sb.length() == 0) return; - schemeSpecificPart = sb.toString(); - } - - private void defineString() { - if (string != null) return; - - StringBuilder sb = new StringBuilder(); - if (scheme != null) { - sb.append(scheme); - sb.append(':'); - } - if (isOpaque()) { - sb.append(schemeSpecificPart); - } else { - if (host != null) { - sb.append("//"); - if (userInfo != null) { - sb.append(userInfo); - sb.append('@'); - } - boolean needBrackets = ((host.indexOf(':') >= 0) - && !host.startsWith("[") - && !host.endsWith("]")); - if (needBrackets) sb.append('['); - sb.append(host); - if (needBrackets) sb.append(']'); - if (port != -1) { - sb.append(':'); - sb.append(port); - } - } else if (authority != null) { - sb.append("//"); - sb.append(authority); - } - if (path != null) - sb.append(path); - if (query != null) { - sb.append('?'); - sb.append(query); - } - } - if (fragment != null) { - sb.append('#'); - sb.append(fragment); - } - string = sb.toString(); - } - - // -- Normalization, resolution, and relativization -- // RFC2396 5.2 (6) @@ -2649,13 +2667,13 @@ private static boolean match(char c, long lowMask, long highMask) { '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; - private static void appendEscape(StringBuffer sb, byte b) { + private static void appendEscape(StringBuilder sb, byte b) { sb.append('%'); sb.append(hexDigits[(b >> 4) & 0x0f]); sb.append(hexDigits[(b >> 0) & 0x0f]); } - private static void appendEncoded(StringBuffer sb, char c) { + private static void appendEncoded(StringBuilder sb, char c) { ByteBuffer bb = null; try { bb = ThreadLocalCoders.encoderFor("UTF-8") @@ -2676,15 +2694,14 @@ private static void appendEncoded(StringBuffer sb, char c) { // by the given mask pair // private static String quote(String s, long lowMask, long highMask) { - int n = s.length(); - StringBuffer sb = null; + StringBuilder sb = null; boolean allowNonASCII = ((lowMask & L_ESCAPED) != 0); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (c < '\u0080') { if (!match(c, lowMask, highMask)) { if (sb == null) { - sb = new StringBuffer(); + sb = new StringBuilder(); sb.append(s, 0, i); } appendEscape(sb, (byte)c); @@ -2696,7 +2713,7 @@ private static String quote(String s, long lowMask, long highMask) { && (Character.isSpaceChar(c) || Character.isISOControl(c))) { if (sb == null) { - sb = new StringBuffer(); + sb = new StringBuilder(); sb.append(s, 0, i); } appendEncoded(sb, c); @@ -2733,7 +2750,7 @@ private static String encode(String s) { assert false; } - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); while (bb.hasRemaining()) { int b = bb.get() & 0xff; if (b >= 0x80) @@ -2864,12 +2881,6 @@ private void failExpecting(String expected, int p) fail("Expected " + expected, p); } - private void failExpecting(String expected, String prior, int p) - throws URISyntaxException - { - fail("Expected " + expected + " following " + prior, p); - } - // -- Simple access to the input string -- From 042d41828fbe611a22de368f597bbd85c92b3079 Mon Sep 17 00:00:00 2001 From: Roger Riggs Date: Tue, 22 Dec 2015 14:12:55 -0500 Subject: [PATCH 56/62] 8146012: CleanerTest fails: Cleanable should have been freed Simplify cleanables being called and use WhiteBox to trigger GC Reviewed-by: chegar --- jdk/test/java/lang/ref/CleanerTest.java | 86 ++++++++----------------- 1 file changed, 28 insertions(+), 58 deletions(-) diff --git a/jdk/test/java/lang/ref/CleanerTest.java b/jdk/test/java/lang/ref/CleanerTest.java index 8d3862ed713..deaadbdc2f2 100644 --- a/jdk/test/java/lang/ref/CleanerTest.java +++ b/jdk/test/java/lang/ref/CleanerTest.java @@ -28,9 +28,9 @@ import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; import java.util.Objects; -import java.util.Vector; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Supplier; @@ -38,15 +38,21 @@ import jdk.internal.misc.CleanerImpl.WeakCleanable; import jdk.internal.misc.CleanerImpl.SoftCleanable; +import sun.hotspot.WhiteBox; + import org.testng.Assert; import org.testng.TestNG; import org.testng.annotations.Test; /* * @test - * @library /lib/testlibrary + * @library /lib/testlibrary /test/lib + * @build sun.hotspot.WhiteBox * @modules java.base/jdk.internal.misc - * @run testng/othervm -Xmx4m CleanerTest + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run testng/othervm + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -verbose:gc -Xmx4m CleanerTest */ @Test @@ -54,6 +60,9 @@ public class CleanerTest { // A common CleaningService used by the test for notifications static final Cleaner COMMON = Cleaner.create(); + // Access to WhiteBox utilities + static final WhiteBox whitebox = WhiteBox.getWhiteBox(); + /** * Test that sequences of the various actions on a Reference * and on the Cleanable instance have the desired result. @@ -265,50 +274,29 @@ void testCleanerTermination() { } /** - * Check a set of semaphores having been released by cleanup handlers. + * Check a semaphore having been released by cleanup handler. * Force a number of GC cycles to give the GC a chance to process - * all the References and for the cleanup actions to be run. + * the Reference and for the cleanup action to be run. * - * @param semaphore a varargs list of Semaphores - * @return true if all of the semaphores have at least 1 permit, - * false otherwise. + * @param semaphore a Semaphore + * @return true if the semaphores has 1 permit, false otherwise. */ - static boolean checkCleaned(Semaphore... semaphore) { - long[] cycles = new long[semaphore.length]; - long total = 0; - for (int cycle = 0; cycle < 20; cycle++) { - for (int i = 0; i < semaphore.length; i++) { - long count = semaphore[i].availablePermits(); - if (count > 0 && cycles[i] == 0) { - System.out.printf(" Cleanable[%d] cleaned in cycle: %d%n", i, cycle); - cycles[i] = cycle; - total += 1; - } - } - - if (total == semaphore.length) { - System.out.printf(" All cleanups done in cycle: %d, total: %d%n", - cycle, total); - for (int i = 0; i < semaphore.length; i++) { - long count = semaphore[i].availablePermits(); - Assert.assertEquals(count, 1, - "Cleanable invoked more than once, semaphore " + i); + static boolean checkCleaned(Semaphore semaphore) { + int cycle = 0; + for (; cycle < 3; cycle++) { + try { + if (semaphore.tryAcquire(10L, TimeUnit.MILLISECONDS)) { + System.out.printf(" Cleanable cleaned in cycle: %d%n", cycle); + return true; } - return true; // all references freed + } catch (InterruptedException ie) { + // retry in outer loop } // Force GC - memoryPressure(); - } - // Not all objects have been cleaned - - for (int i = 0; i < semaphore.length; i++) { - if (cycles[i] != 0) { - System.out.printf(" Cleanable[%d] cleaned in cycle: %d%n", i, cycles[i]); - } else { - System.out.printf(" Cleanable[%d] not cleaned%n", i); - } + whitebox.fullGC(); } - + // Object has not been cleaned + System.out.printf(" Cleanable not cleaned%n"); return false; // Failing result } @@ -456,24 +444,6 @@ protected void performCleanup() { return new CleanableCase(new SoftReference<>(obj, null), c1, s1, true); } - /** - * MemoryPressure allocates memory to force a gc and to clear SoftReferences. - */ - static void memoryPressure() { - SoftReference soft = new SoftReference<>(new Object(), null); - Vector root = new Vector<>(); - try { - long free = 0; - while (soft.get() != null) { - long[] extra = new long[50_000]; - root.addElement(extra); - } - } catch (OutOfMemoryError mem) { - // ignore - root = null; - } - } - /** * CleanableCase encapsulates the objects used for a test. * The reference to the object is not held directly, From 626fc9bc75f654497d1be747649aae5508374123 Mon Sep 17 00:00:00 2001 From: Chris Hegarty Date: Tue, 22 Dec 2015 21:30:48 +0000 Subject: [PATCH 57/62] 8146038: CleanerImpl should not depend on ManagedLocalsThread Reviewed-by: rriggs --- .../share/classes/jdk/internal/misc/CleanerImpl.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/jdk/src/java.base/share/classes/jdk/internal/misc/CleanerImpl.java b/jdk/src/java.base/share/classes/jdk/internal/misc/CleanerImpl.java index cec14ba5b42..4ce835f9a39 100644 --- a/jdk/src/java.base/share/classes/jdk/internal/misc/CleanerImpl.java +++ b/jdk/src/java.base/share/classes/jdk/internal/misc/CleanerImpl.java @@ -39,7 +39,6 @@ import java.util.function.Function; import sun.misc.InnocuousThread; -import sun.misc.ManagedLocalsThread; /** * CleanerImpl manages a set of object references and corresponding cleaning actions. @@ -130,8 +129,8 @@ public void start(Cleaner service, ThreadFactory threadFactory) { */ public void run() { Thread t = Thread.currentThread(); - ManagedLocalsThread mlThread = (t instanceof ManagedLocalsThread) - ? (ManagedLocalsThread) t + InnocuousThread mlThread = (t instanceof InnocuousThread) + ? (InnocuousThread) t : null; while (!phantomCleanableList.isListEmpty() || !weakCleanableList.isListEmpty() || @@ -787,4 +786,3 @@ public Thread newThread(Runnable r) { } } - From 7f221493d8c4f959ec27f8db0d59360880a817d1 Mon Sep 17 00:00:00 2001 From: Chris Hegarty Date: Tue, 22 Dec 2015 21:32:29 +0000 Subject: [PATCH 58/62] 8146000: Remove sun.mics.CompoundEnumeration Reviewed-by: coffeys, shade --- .../share/classes/java/lang/ClassLoader.java | 35 ++++++++++- .../classes/sun/misc/CompoundEnumeration.java | 63 ------------------- 2 files changed, 34 insertions(+), 64 deletions(-) delete mode 100644 jdk/src/java.base/share/classes/sun/misc/CompoundEnumeration.java diff --git a/jdk/src/java.base/share/classes/java/lang/ClassLoader.java b/jdk/src/java.base/share/classes/java/lang/ClassLoader.java index a3009062c2d..76cdd25dcd7 100644 --- a/jdk/src/java.base/share/classes/java/lang/ClassLoader.java +++ b/jdk/src/java.base/share/classes/java/lang/ClassLoader.java @@ -45,11 +45,11 @@ import java.util.Set; import java.util.Stack; import java.util.Map; +import java.util.NoSuchElementException; import java.util.Vector; import java.util.Hashtable; import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; -import sun.misc.CompoundEnumeration; import sun.misc.Resource; import sun.misc.URLClassPath; import sun.reflect.CallerSensitive; @@ -2206,3 +2206,36 @@ public ClassLoader run() throws Exception { return sys; } } + +/* + * A utility class that will enumerate over an array of enumerations. + */ +final class CompoundEnumeration implements Enumeration { + private final Enumeration[] enums; + private int index; + + public CompoundEnumeration(Enumeration[] enums) { + this.enums = enums; + } + + private boolean next() { + while (index < enums.length) { + if (enums[index] != null && enums[index].hasMoreElements()) { + return true; + } + index++; + } + return false; + } + + public boolean hasMoreElements() { + return next(); + } + + public E nextElement() { + if (!next()) { + throw new NoSuchElementException(); + } + return enums[index].nextElement(); + } +} diff --git a/jdk/src/java.base/share/classes/sun/misc/CompoundEnumeration.java b/jdk/src/java.base/share/classes/sun/misc/CompoundEnumeration.java deleted file mode 100644 index a89ec5f59c4..00000000000 --- a/jdk/src/java.base/share/classes/sun/misc/CompoundEnumeration.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 1998, 2011, 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.misc; - -import java.util.Enumeration; -import java.util.NoSuchElementException; - -/* - * A useful utility class that will enumerate over an array of - * enumerations. - */ -public class CompoundEnumeration implements Enumeration { - private Enumeration[] enums; - private int index = 0; - - public CompoundEnumeration(Enumeration[] enums) { - this.enums = enums; - } - - private boolean next() { - while (index < enums.length) { - if (enums[index] != null && enums[index].hasMoreElements()) { - return true; - } - index++; - } - return false; - } - - public boolean hasMoreElements() { - return next(); - } - - public E nextElement() { - if (!next()) { - throw new NoSuchElementException(); - } - return enums[index].nextElement(); - } -} From 80015b758637d83b02e125139b53e18328ccaf8d Mon Sep 17 00:00:00 2001 From: Xue-Lei Andrew Fan Date: Wed, 23 Dec 2015 03:51:35 +0000 Subject: [PATCH 59/62] 8133070: Hot lock on BulkCipher.isAvailable Reviewed-by: mullan --- .../classes/sun/security/ssl/CipherBox.java | 35 -- .../classes/sun/security/ssl/CipherSuite.java | 119 ++-- .../sun/security/ssl/CipherSuiteList.java | 30 +- .../classes/sun/security/ssl/JsseJce.java | 48 +- .../sun/security/ssl/SSLContextImpl.java | 542 ++++++++++-------- 5 files changed, 359 insertions(+), 415 deletions(-) diff --git a/jdk/src/java.base/share/classes/sun/security/ssl/CipherBox.java b/jdk/src/java.base/share/classes/sun/security/ssl/CipherBox.java index 9f76d19792f..57a3e1e8aa6 100644 --- a/jdk/src/java.base/share/classes/sun/security/ssl/CipherBox.java +++ b/jdk/src/java.base/share/classes/sun/security/ssl/CipherBox.java @@ -1105,41 +1105,6 @@ int estimateFragmentSize(int packetSize, int macLen, int headerSize) { return fragLen; } - - /* - * Is this cipher available? - * - * This method can only be called by CipherSuite.BulkCipher.isAvailable() - * to test the availability of a cipher suites. Please DON'T use it in - * other places, otherwise, the behavior may be unexpected because we may - * initialize AEAD cipher improperly in the method. - */ - Boolean isAvailable() { - // We won't know whether a cipher for a particular key size is - // available until the cipher is successfully initialized. - // - // We do not initialize AEAD cipher in the constructor. Need to - // initialize the cipher to ensure that the AEAD mode for a - // particular key size is supported. - if (cipherType == AEAD_CIPHER) { - try { - Authenticator authenticator = - new Authenticator(protocolVersion); - byte[] nonce = authenticator.sequenceNumber(); - byte[] iv = Arrays.copyOf(fixedIv, - fixedIv.length + nonce.length); - System.arraycopy(nonce, 0, iv, fixedIv.length, nonce.length); - GCMParameterSpec spec = new GCMParameterSpec(tagSize * 8, iv); - - cipher.init(mode, key, spec, random); - } catch (Exception e) { - return Boolean.FALSE; - } - } // Otherwise, we have initialized the cipher in the constructor. - - return Boolean.TRUE; - } - /** * Sanity check the length of a fragment before decryption. * diff --git a/jdk/src/java.base/share/classes/sun/security/ssl/CipherSuite.java b/jdk/src/java.base/share/classes/sun/security/ssl/CipherSuite.java index 65a110b479e..fa6c5ad34bb 100644 --- a/jdk/src/java.base/share/classes/sun/security/ssl/CipherSuite.java +++ b/jdk/src/java.base/share/classes/sun/security/ssl/CipherSuite.java @@ -77,12 +77,6 @@ final class CipherSuite implements Comparable { // minimum priority for default enabled CipherSuites static final int DEFAULT_SUITES_PRIORITY = 300; - // Flag indicating if CipherSuite availability can change dynamically. - // This is the case when we rely on a JCE cipher implementation that - // may not be available in the installed JCE providers. - // It is true because we might not have an ECC implementation. - static final boolean DYNAMIC_AVAILABILITY = true; - private static final boolean ALLOW_ECC = Debug.getBooleanProperty ("com.sun.net.ssl.enableECC", true); @@ -176,9 +170,6 @@ private CipherSuite(String name, int id) { * Return whether this CipherSuite is available for use. A * CipherSuite may be unavailable even if it is supported * (i.e. allowed == true) if the required JCE cipher is not installed. - * In some configuration, this situation may change over time, call - * CipherSuiteList.clearAvailableCache() before this method to obtain - * the most current status. */ boolean isAvailable() { return allowed && keyExchange.isAvailable() && cipher.isAvailable(); @@ -471,10 +462,6 @@ static enum BulkCipher { B_AES_128_GCM(CIPHER_AES_GCM, AEAD_CIPHER, 16, 12, 4, true), B_AES_256_GCM(CIPHER_AES_GCM, AEAD_CIPHER, 32, 12, 4, true); - // Map BulkCipher -> Boolean(available) - private static final Map availableCache = - new HashMap<>(8); - // descriptive name including key size, e.g. AES/128 final String description; @@ -518,6 +505,9 @@ static enum BulkCipher { // The secure random used to detect the cipher availability. private static final SecureRandom secureRandom; + // runtime availability + private final boolean isAvailable; + static { try { secureRandom = JsseJce.getSecureRandom(); @@ -542,6 +532,17 @@ static enum BulkCipher { this.expandedKeySize = expandedKeySize; this.exportable = true; + + // availability of this bulk cipher + // + // Currently all supported ciphers except AES are always available + // via the JSSE internal implementations. We also assume AES/128 of + // CBC mode is always available since it is shipped with the SunJCE + // provider. However, AES/256 is unavailable when the default JCE + // policy jurisdiction files are installed because of key length + // restrictions. + this.isAvailable = + allowed ? isUnlimited(keySize, transformation) : false; } BulkCipher(String transformation, CipherType cipherType, int keySize, @@ -558,6 +559,17 @@ static enum BulkCipher { this.expandedKeySize = keySize; this.exportable = false; + + // availability of this bulk cipher + // + // Currently all supported ciphers except AES are always available + // via the JSSE internal implementations. We also assume AES/128 of + // CBC mode is always available since it is shipped with the SunJCE + // provider. However, AES/256 is unavailable when the default JCE + // policy jurisdiction files are installed because of key length + // restrictions. + this.isAvailable = + allowed ? isUnlimited(keySize, transformation) : false; } /** @@ -575,84 +587,27 @@ CipherBox newCipher(ProtocolVersion version, SecretKey key, /** * Test if this bulk cipher is available. For use by CipherSuite. - * - * Currently all supported ciphers except AES are always available - * via the JSSE internal implementations. We also assume AES/128 of - * CBC mode is always available since it is shipped with the SunJCE - * provider. However, AES/256 is unavailable when the default JCE - * policy jurisdiction files are installed because of key length - * restrictions, and AEAD is unavailable when the underlying providers - * do not support AEAD/GCM mode. */ boolean isAvailable() { - if (allowed == false) { - return false; - } - - if ((this == B_AES_256) || - (this.cipherType == CipherType.AEAD_CIPHER)) { - return isAvailable(this); - } - - // always available - return true; + return this.isAvailable; } - // for use by CipherSuiteList.clearAvailableCache(); - static synchronized void clearAvailableCache() { - if (DYNAMIC_AVAILABILITY) { - availableCache.clear(); - } - } + private static boolean isUnlimited(int keySize, String transformation) { + int keySizeInBits = keySize * 8; + if (keySizeInBits > 128) { // need the JCE unlimited + // strength jurisdiction policy + try { + if (Cipher.getMaxAllowedKeyLength( + transformation) < keySizeInBits) { - private static synchronized boolean isAvailable(BulkCipher cipher) { - Boolean b = availableCache.get(cipher); - if (b == null) { - int keySizeInBits = cipher.keySize * 8; - if (keySizeInBits > 128) { // need the JCE unlimited - // strength jurisdiction policy - try { - if (Cipher.getMaxAllowedKeyLength( - cipher.transformation) < keySizeInBits) { - b = Boolean.FALSE; - } - } catch (Exception e) { - b = Boolean.FALSE; + return false; } + } catch (Exception e) { + return false; } - - if (b == null) { - b = Boolean.FALSE; // may be reset to TRUE if - // the cipher is available - CipherBox temporary = null; - try { - SecretKey key = new SecretKeySpec( - new byte[cipher.expandedKeySize], - cipher.algorithm); - IvParameterSpec iv; - if (cipher.cipherType == CipherType.AEAD_CIPHER) { - iv = new IvParameterSpec( - new byte[cipher.fixedIvSize]); - } else { - iv = new IvParameterSpec(new byte[cipher.ivSize]); - } - temporary = cipher.newCipher( - ProtocolVersion.DEFAULT_TLS, - key, iv, secureRandom, true); - b = temporary.isAvailable(); - } catch (NoSuchAlgorithmException e) { - // not available - } finally { - if (temporary != null) { - temporary.dispose(); - } - } - } - - availableCache.put(cipher, b); } - return b.booleanValue(); + return true; } @Override diff --git a/jdk/src/java.base/share/classes/sun/security/ssl/CipherSuiteList.java b/jdk/src/java.base/share/classes/sun/security/ssl/CipherSuiteList.java index 19dc90fa4c5..491bffa85ba 100644 --- a/jdk/src/java.base/share/classes/sun/security/ssl/CipherSuiteList.java +++ b/jdk/src/java.base/share/classes/sun/security/ssl/CipherSuiteList.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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 @@ -74,24 +74,12 @@ final class CipherSuiteList { throw new IllegalArgumentException("CipherSuites may not be null"); } cipherSuites = new ArrayList(names.length); - // refresh available cache once if a CipherSuite is not available - // (maybe new JCE providers have been installed) - boolean refreshed = false; for (int i = 0; i < names.length; i++) { String suiteName = names[i]; CipherSuite suite = CipherSuite.valueOf(suiteName); if (suite.isAvailable() == false) { - if (refreshed == false) { - // clear the cache so that the isAvailable() call below - // does a full check - clearAvailableCache(); - refreshed = true; - } - // still missing? - if (suite.isAvailable() == false) { - throw new IllegalArgumentException("Cannot support " - + suiteName + " with currently installed providers"); - } + throw new IllegalArgumentException("Cannot support " + + suiteName + " with currently installed providers"); } cipherSuites.add(suite); } @@ -195,16 +183,4 @@ void send(HandshakeOutStream s) throws IOException { } s.putBytes16(suiteBytes); } - - /** - * Clear cache of available ciphersuites. If we support all ciphers - * internally, there is no need to clear the cache and calling this - * method has no effect. - */ - static synchronized void clearAvailableCache() { - if (CipherSuite.DYNAMIC_AVAILABILITY) { - CipherSuite.BulkCipher.clearAvailableCache(); - JsseJce.clearEcAvailable(); - } - } } diff --git a/jdk/src/java.base/share/classes/sun/security/ssl/JsseJce.java b/jdk/src/java.base/share/classes/sun/security/ssl/JsseJce.java index 43d62b60152..aebc7c09c08 100644 --- a/jdk/src/java.base/share/classes/sun/security/ssl/JsseJce.java +++ b/jdk/src/java.base/share/classes/sun/security/ssl/JsseJce.java @@ -55,11 +55,6 @@ final class JsseJce { private static final ProviderList fipsProviderList; - // Flag indicating whether EC crypto is available. - // If null, then we have not checked yet. - // If yes, then all the EC based crypto we need is available. - private static Boolean ecAvailable; - // Flag indicating whether Kerberos crypto is available. // If true, then all the Kerberos-based crypto we need is available. private static final boolean kerberosAvailable; @@ -180,24 +175,8 @@ private JsseJce() { // no instantiation of this class } - static synchronized boolean isEcAvailable() { - if (ecAvailable == null) { - try { - JsseJce.getSignature(SIGNATURE_ECDSA); - JsseJce.getSignature(SIGNATURE_RAWECDSA); - JsseJce.getKeyAgreement("ECDH"); - JsseJce.getKeyFactory("EC"); - JsseJce.getKeyPairGenerator("EC"); - ecAvailable = true; - } catch (Exception e) { - ecAvailable = false; - } - } - return ecAvailable; - } - - static synchronized void clearEcAvailable() { - ecAvailable = null; + static boolean isEcAvailable() { + return EcAvailability.isAvailable; } static boolean isKerberosAvailable() { @@ -399,4 +378,27 @@ static void endFipsProvider(Object o) { } } + + // lazy initialization holder class idiom for static default parameters + // + // See Effective Java Second Edition: Item 71. + private static class EcAvailability { + // Is EC crypto available? + private final static boolean isAvailable; + + static { + boolean mediator = true; + try { + JsseJce.getSignature(SIGNATURE_ECDSA); + JsseJce.getSignature(SIGNATURE_RAWECDSA); + JsseJce.getKeyAgreement("ECDH"); + JsseJce.getKeyFactory("EC"); + JsseJce.getKeyPairGenerator("EC"); + } catch (Exception e) { + mediator = false; + } + + isAvailable = mediator; + } + } } diff --git a/jdk/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java b/jdk/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java index cac13df0184..15b420da172 100644 --- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java +++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java @@ -52,18 +52,8 @@ public abstract class SSLContextImpl extends SSLContextSpi { private X509TrustManager trustManager; private SecureRandom secureRandom; - // supported and default protocols - private ProtocolList defaultServerProtocolList; - private ProtocolList defaultClientProtocolList; - private ProtocolList supportedProtocolList; - - // supported and default cipher suites - private CipherSuiteList defaultServerCipherSuiteList; - private CipherSuiteList defaultClientCipherSuiteList; - private CipherSuiteList supportedCipherSuiteList; - // DTLS cookie exchange manager - private HelloCookieManager helloCookieManager; + private volatile HelloCookieManager helloCookieManager; private StatusResponseManager statusResponseManager; @@ -117,6 +107,7 @@ protected void engineInit(KeyManager[] km, TrustManager[] tm, if (debug != null && Debug.isOn("sslctx")) { System.out.println("done seeding SecureRandom"); } + isInitialized = true; } @@ -242,13 +233,20 @@ EphemeralKeyManager getEphemeralKeyManager() { return ephemeralKeyManager; } + // Used for DTLS in server mode only, see ServerHandshaker. HelloCookieManager getHelloCookieManager() { if (!isInitialized) { throw new IllegalStateException("SSLContext is not initialized"); } - if (helloCookieManager == null) { - helloCookieManager = getHelloCookieManager(secureRandom); + if (helloCookieManager != null) { + return helloCookieManager; + } + + synchronized (this) { + if (helloCookieManager == null) { + helloCookieManager = getHelloCookieManager(secureRandom); + } } return helloCookieManager; @@ -263,78 +261,34 @@ StatusResponseManager getStatusResponseManager() { return statusResponseManager; } - abstract SSLParameters getDefaultServerSSLParams(); - abstract SSLParameters getDefaultClientSSLParams(); - abstract SSLParameters getSupportedSSLParams(); - // Get supported ProtocolList. - ProtocolList getSuportedProtocolList() { - if (supportedProtocolList == null) { - supportedProtocolList = - new ProtocolList(getSupportedSSLParams().getProtocols()); - } + abstract ProtocolList getSuportedProtocolList(); - return supportedProtocolList; - } + // Get default ProtocolList for server mode. + abstract ProtocolList getServerDefaultProtocolList(); - // Get default ProtocolList. - ProtocolList getDefaultProtocolList(boolean roleIsServer) { - if (roleIsServer) { - if (defaultServerProtocolList == null) { - defaultServerProtocolList = new ProtocolList( - getDefaultServerSSLParams().getProtocols()); - } - - return defaultServerProtocolList; - } else { - if (defaultClientProtocolList == null) { - defaultClientProtocolList = new ProtocolList( - getDefaultClientSSLParams().getProtocols()); - } - - return defaultClientProtocolList; - } - } + // Get default ProtocolList for client mode. + abstract ProtocolList getClientDefaultProtocolList(); // Get supported CipherSuiteList. - CipherSuiteList getSupportedCipherSuiteList() { - // The maintenance of cipher suites needs to be synchronized. - synchronized (this) { - // Clear cache of available ciphersuites. - clearAvailableCache(); + abstract CipherSuiteList getSupportedCipherSuiteList(); - if (supportedCipherSuiteList == null) { - supportedCipherSuiteList = getApplicableCipherSuiteList( - getSuportedProtocolList(), false); - } + // Get default CipherSuiteList for server mode. + abstract CipherSuiteList getServerDefaultCipherSuiteList(); - return supportedCipherSuiteList; - } + // Get default CipherSuiteList for client mode. + abstract CipherSuiteList getClientDefaultCipherSuiteList(); + + // Get default ProtocolList. + ProtocolList getDefaultProtocolList(boolean roleIsServer) { + return roleIsServer ? getServerDefaultProtocolList() + : getClientDefaultProtocolList(); } // Get default CipherSuiteList. CipherSuiteList getDefaultCipherSuiteList(boolean roleIsServer) { - // The maintenance of cipher suites needs to be synchronized. - synchronized (this) { - // Clear cache of available ciphersuites. - clearAvailableCache(); - - if (roleIsServer) { - if (defaultServerCipherSuiteList == null) { - defaultServerCipherSuiteList = getApplicableCipherSuiteList( - getDefaultProtocolList(true), true); - } - - return defaultServerCipherSuiteList; - } else { - if (defaultClientCipherSuiteList == null) { - defaultClientCipherSuiteList = getApplicableCipherSuiteList( - getDefaultProtocolList(false), true); - } - - return defaultClientCipherSuiteList; - } - } + return roleIsServer ? getServerDefaultCipherSuiteList() + : getClientDefaultCipherSuiteList(); } /** @@ -342,8 +296,8 @@ CipherSuiteList getDefaultCipherSuiteList(boolean roleIsServer) { * protocols. See: SSLSocket/SSLEngine.setEnabledProtocols() */ boolean isDefaultProtocolList(ProtocolList protocols) { - return (protocols == defaultServerProtocolList) || - (protocols == defaultClientProtocolList); + return (protocols == getServerDefaultProtocolList()) || + (protocols == getClientDefaultProtocolList()); } /** @@ -351,8 +305,8 @@ boolean isDefaultProtocolList(ProtocolList protocols) { * protocols. See: SSLSocket/SSLEngine.setEnabledProtocols() */ boolean isDefaultCipherSuiteList(CipherSuiteList cipherSuites) { - return (cipherSuites == defaultClientCipherSuiteList) || - (cipherSuites == defaultServerCipherSuiteList); + return (cipherSuites == getServerDefaultCipherSuiteList()) || + (cipherSuites == getClientDefaultCipherSuiteList()); } /* @@ -405,24 +359,6 @@ private static CipherSuiteList getApplicableCipherSuiteList( return new CipherSuiteList(suites); } - /** - * Clear cache of available ciphersuites. If we support all ciphers - * internally, there is no need to clear the cache and calling this - * method has no effect. - * - * Note that every call to clearAvailableCache() and the maintenance of - * cipher suites need to be synchronized with this instance. - */ - private void clearAvailableCache() { - if (CipherSuite.DYNAMIC_AVAILABILITY) { - supportedCipherSuiteList = null; - defaultServerCipherSuiteList = null; - defaultClientCipherSuiteList = null; - CipherSuite.BulkCipher.clearAvailableCache(); - JsseJce.clearEcAvailable(); - } - } - private static String[] getAvailableProtocols( ProtocolVersion[] protocolCandidates) { @@ -479,31 +415,28 @@ private static String[] getAvailableProtocols( * @see SSLContext */ private abstract static class AbstractTLSContext extends SSLContextImpl { - // parameters - private static final SSLParameters defaultServerSSLParams; - private static final SSLParameters supportedSSLParams; - - static { - // supported SSL parameters - supportedSSLParams = new SSLParameters(); + private static final ProtocolList supportedProtocolList; + private static final ProtocolList serverDefaultProtocolList; - // candidates for available protocols - ProtocolVersion[] candidates; + private static final CipherSuiteList supportedCipherSuiteList; + private static final CipherSuiteList serverDefaultCipherSuiteList; + static { if (SunJSSE.isFIPS()) { - supportedSSLParams.setProtocols(new String[] { + supportedProtocolList = new ProtocolList(new String[] { ProtocolVersion.TLS10.name, ProtocolVersion.TLS11.name, ProtocolVersion.TLS12.name }); - candidates = new ProtocolVersion[] { + serverDefaultProtocolList = new ProtocolList( + getAvailableProtocols(new ProtocolVersion[] { ProtocolVersion.TLS10, ProtocolVersion.TLS11, ProtocolVersion.TLS12 - }; + })); } else { - supportedSSLParams.setProtocols(new String[] { + supportedProtocolList = new ProtocolList(new String[] { ProtocolVersion.SSL20Hello.name, ProtocolVersion.SSL30.name, ProtocolVersion.TLS10.name, @@ -511,28 +444,40 @@ private abstract static class AbstractTLSContext extends SSLContextImpl { ProtocolVersion.TLS12.name }); - candidates = new ProtocolVersion[] { + serverDefaultProtocolList = new ProtocolList( + getAvailableProtocols(new ProtocolVersion[] { ProtocolVersion.SSL20Hello, ProtocolVersion.SSL30, ProtocolVersion.TLS10, ProtocolVersion.TLS11, ProtocolVersion.TLS12 - }; + })); } - defaultServerSSLParams = new SSLParameters(); - defaultServerSSLParams.setProtocols( - getAvailableProtocols(candidates)); + supportedCipherSuiteList = getApplicableCipherSuiteList( + supportedProtocolList, false); // all supported + serverDefaultCipherSuiteList = getApplicableCipherSuiteList( + serverDefaultProtocolList, true); // enabled only } @Override - SSLParameters getDefaultServerSSLParams() { - return defaultServerSSLParams; + ProtocolList getSuportedProtocolList() { + return supportedProtocolList; + } + + @Override + CipherSuiteList getSupportedCipherSuiteList() { + return supportedCipherSuiteList; } @Override - SSLParameters getSupportedSSLParams() { - return supportedSSLParams; + ProtocolList getServerDefaultProtocolList() { + return serverDefaultProtocolList; + } + + @Override + CipherSuiteList getServerDefaultCipherSuiteList() { + return serverDefaultCipherSuiteList; } @Override @@ -552,30 +497,35 @@ SSLEngine createSSLEngineImpl(String host, int port) { * @see SSLContext */ public static final class TLS10Context extends AbstractTLSContext { - private static final SSLParameters defaultClientSSLParams; + private static final ProtocolList clientDefaultProtocolList; + private static final CipherSuiteList clientDefaultCipherSuiteList; static { - // candidates for available protocols - ProtocolVersion[] candidates; if (SunJSSE.isFIPS()) { - candidates = new ProtocolVersion[] { + clientDefaultProtocolList = new ProtocolList( + getAvailableProtocols(new ProtocolVersion[] { ProtocolVersion.TLS10 - }; + })); } else { - candidates = new ProtocolVersion[] { + clientDefaultProtocolList = new ProtocolList( + getAvailableProtocols(new ProtocolVersion[] { ProtocolVersion.SSL30, ProtocolVersion.TLS10 - }; + })); } - defaultClientSSLParams = new SSLParameters(); - defaultClientSSLParams.setProtocols( - getAvailableProtocols(candidates)); + clientDefaultCipherSuiteList = getApplicableCipherSuiteList( + clientDefaultProtocolList, true); // enabled only } @Override - SSLParameters getDefaultClientSSLParams() { - return defaultClientSSLParams; + ProtocolList getClientDefaultProtocolList() { + return clientDefaultProtocolList; + } + + @Override + CipherSuiteList getClientDefaultCipherSuiteList() { + return clientDefaultCipherSuiteList; } } @@ -585,32 +535,37 @@ SSLParameters getDefaultClientSSLParams() { * @see SSLContext */ public static final class TLS11Context extends AbstractTLSContext { - private static final SSLParameters defaultClientSSLParams; + private static final ProtocolList clientDefaultProtocolList; + private static final CipherSuiteList clientDefaultCipherSuiteList; static { - // candidates for available protocols - ProtocolVersion[] candidates; if (SunJSSE.isFIPS()) { - candidates = new ProtocolVersion[] { + clientDefaultProtocolList = new ProtocolList( + getAvailableProtocols(new ProtocolVersion[] { ProtocolVersion.TLS10, ProtocolVersion.TLS11 - }; + })); } else { - candidates = new ProtocolVersion[] { + clientDefaultProtocolList = new ProtocolList( + getAvailableProtocols(new ProtocolVersion[] { ProtocolVersion.SSL30, ProtocolVersion.TLS10, ProtocolVersion.TLS11 - }; + })); } - defaultClientSSLParams = new SSLParameters(); - defaultClientSSLParams.setProtocols( - getAvailableProtocols(candidates)); + clientDefaultCipherSuiteList = getApplicableCipherSuiteList( + clientDefaultProtocolList, true); // enabled only + } + + @Override + ProtocolList getClientDefaultProtocolList() { + return clientDefaultProtocolList; } @Override - SSLParameters getDefaultClientSSLParams() { - return defaultClientSSLParams; + CipherSuiteList getClientDefaultCipherSuiteList() { + return clientDefaultCipherSuiteList; } } @@ -620,34 +575,39 @@ SSLParameters getDefaultClientSSLParams() { * @see SSLContext */ public static final class TLS12Context extends AbstractTLSContext { - private static final SSLParameters defaultClientSSLParams; + private static final ProtocolList clientDefaultProtocolList; + private static final CipherSuiteList clientDefaultCipherSuiteList; static { - // candidates for available protocols - ProtocolVersion[] candidates; if (SunJSSE.isFIPS()) { - candidates = new ProtocolVersion[] { + clientDefaultProtocolList = new ProtocolList( + getAvailableProtocols(new ProtocolVersion[] { ProtocolVersion.TLS10, ProtocolVersion.TLS11, ProtocolVersion.TLS12 - }; + })); } else { - candidates = new ProtocolVersion[] { + clientDefaultProtocolList = new ProtocolList( + getAvailableProtocols(new ProtocolVersion[] { ProtocolVersion.SSL30, ProtocolVersion.TLS10, ProtocolVersion.TLS11, ProtocolVersion.TLS12 - }; + })); } - defaultClientSSLParams = new SSLParameters(); - defaultClientSSLParams.setProtocols( - getAvailableProtocols(candidates)); + clientDefaultCipherSuiteList = getApplicableCipherSuiteList( + clientDefaultProtocolList, true); // enabled only } @Override - SSLParameters getDefaultClientSSLParams() { - return defaultClientSSLParams; + ProtocolList getClientDefaultProtocolList() { + return clientDefaultProtocolList; + } + + @Override + CipherSuiteList getClientDefaultCipherSuiteList() { + return clientDefaultCipherSuiteList; } } @@ -719,7 +679,9 @@ private static class CustomizedSSLProtocols { */ private static class CustomizedTLSContext extends AbstractTLSContext { - private static final SSLParameters defaultClientSSLParams; + private static final ProtocolList clientDefaultProtocolList; + private static final CipherSuiteList clientDefaultCipherSuiteList; + private static IllegalArgumentException reservedException = null; // Don't want a java.lang.LinkageError for illegal system property. @@ -766,11 +728,13 @@ private static class CustomizedTLSContext extends AbstractTLSContext { candidates = customizedTLSProtocols.toArray(candidates); } - defaultClientSSLParams = new SSLParameters(); - defaultClientSSLParams.setProtocols( + clientDefaultProtocolList = new ProtocolList( getAvailableProtocols(candidates)); + clientDefaultCipherSuiteList = getApplicableCipherSuiteList( + clientDefaultProtocolList, true); // enabled only } else { - defaultClientSSLParams = null; // unlikely to be used + clientDefaultProtocolList = null; // unlikely to be used + clientDefaultCipherSuiteList = null; // unlikely to be used } } @@ -781,8 +745,13 @@ protected CustomizedTLSContext() { } @Override - SSLParameters getDefaultClientSSLParams() { - return defaultClientSSLParams; + ProtocolList getClientDefaultProtocolList() { + return clientDefaultProtocolList; + } + + @Override + CipherSuiteList getClientDefaultCipherSuiteList() { + return clientDefaultCipherSuiteList; } } @@ -795,71 +764,53 @@ public static final class TLSContext extends CustomizedTLSContext { // use the default constructor and methods } - /* - * The SSLContext implementation for default "Default" algorithm - * - * @see SSLContext - */ - public static final class DefaultSSLContext extends CustomizedTLSContext { + // lazy initialization holder class idiom for static default parameters + // + // See Effective Java Second Edition: Item 71. + private static final class DefaultManagersHolder { private static final String NONE = "NONE"; private static final String P11KEYSTORE = "PKCS11"; - private static volatile SSLContextImpl defaultImpl; + private static final TrustManager[] trustManagers; + private static final KeyManager[] keyManagers; - private static TrustManager[] defaultTrustManagers; - private static KeyManager[] defaultKeyManagers; + static Exception reservedException = null; - public DefaultSSLContext() throws Exception { + static { + TrustManager[] tmMediator; try { - super.engineInit(getDefaultKeyManager(), - getDefaultTrustManager(), null); + tmMediator = getTrustManagers(); } catch (Exception e) { - if (debug != null && Debug.isOn("defaultctx")) { - System.out.println("default context init failed: " + e); - } - throw e; + reservedException = e; + tmMediator = new TrustManager[0]; } + trustManagers = tmMediator; - if (defaultImpl == null) { - defaultImpl = this; - } - } - - @Override - protected void engineInit(KeyManager[] km, TrustManager[] tm, - SecureRandom sr) throws KeyManagementException { - throw new KeyManagementException - ("Default SSLContext is initialized automatically"); - } - - static synchronized SSLContextImpl getDefaultImpl() throws Exception { - if (defaultImpl == null) { - new DefaultSSLContext(); + if (reservedException == null) { + KeyManager[] kmMediator; + try { + kmMediator = getKeyManagers(); + } catch (Exception e) { + reservedException = e; + kmMediator = new KeyManager[0]; + } + keyManagers = kmMediator; + } else { + keyManagers = new KeyManager[0]; } - return defaultImpl; } - private static synchronized TrustManager[] getDefaultTrustManager() - throws Exception { - if (defaultTrustManagers != null) { - return defaultTrustManagers; - } - + private static TrustManager[] getTrustManagers() throws Exception { KeyStore ks = TrustManagerFactoryImpl.getCacertsKeyStore("defaultctx"); TrustManagerFactory tmf = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm()); tmf.init(ks); - defaultTrustManagers = tmf.getTrustManagers(); - return defaultTrustManagers; + return tmf.getTrustManagers(); } - private static synchronized KeyManager[] getDefaultKeyManager() - throws Exception { - if (defaultKeyManagers != null) { - return defaultKeyManagers; - } + private static KeyManager[] getKeyManagers() throws Exception { final Map props = new HashMap<>(); AccessController.doPrivileged( @@ -956,8 +907,71 @@ public FileInputStream run() throws Exception { kmf.init(ks, passwd); } - defaultKeyManagers = kmf.getKeyManagers(); - return defaultKeyManagers; + return kmf.getKeyManagers(); + } + } + + // lazy initialization holder class idiom for static default parameters + // + // See Effective Java Second Edition: Item 71. + private static final class DefaultSSLContextHolder { + + private static final SSLContextImpl sslContext; + static Exception reservedException = null; + + static { + SSLContextImpl mediator = null; + if (DefaultManagersHolder.reservedException != null) { + reservedException = DefaultManagersHolder.reservedException; + } else { + try { + mediator = new DefaultSSLContext(); + } catch (Exception e) { + reservedException = e; + } + } + + sslContext = mediator; + } + } + + /* + * The SSLContext implementation for default "Default" algorithm + * + * @see SSLContext + */ + public static final class DefaultSSLContext extends CustomizedTLSContext { + + // public constructor for SSLContext.getInstance("Default") + public DefaultSSLContext() throws Exception { + if (DefaultManagersHolder.reservedException != null) { + throw DefaultManagersHolder.reservedException; + } + + try { + super.engineInit(DefaultManagersHolder.keyManagers, + DefaultManagersHolder.trustManagers, null); + } catch (Exception e) { + if (debug != null && Debug.isOn("defaultctx")) { + System.out.println("default context init failed: " + e); + } + throw e; + } + } + + @Override + protected void engineInit(KeyManager[] km, TrustManager[] tm, + SecureRandom sr) throws KeyManagementException { + throw new KeyManagementException + ("Default SSLContext is initialized automatically"); + } + + static SSLContextImpl getDefaultImpl() throws Exception { + if (DefaultSSLContextHolder.reservedException != null) { + throw DefaultSSLContextHolder.reservedException; + } + + return DefaultSSLContextHolder.sslContext; } } @@ -971,39 +985,50 @@ public FileInputStream run() throws Exception { * @see SSLContext */ private abstract static class AbstractDTLSContext extends SSLContextImpl { - // parameters - private static final SSLParameters defaultServerSSLParams; - private static final SSLParameters supportedSSLParams; + private static final ProtocolList supportedProtocolList; + private static final ProtocolList serverDefaultProtocolList; - static { - // supported SSL parameters - supportedSSLParams = new SSLParameters(); + private static final CipherSuiteList supportedCipherSuiteList; + private static final CipherSuiteList serverDefaultCipherSuiteList; + static { // Both DTLSv1.0 and DTLSv1.2 can be used in FIPS mode. - supportedSSLParams.setProtocols(new String[] { + supportedProtocolList = new ProtocolList(new String[] { ProtocolVersion.DTLS10.name, ProtocolVersion.DTLS12.name }); - // candidates for available protocols - ProtocolVersion[] candidates = new ProtocolVersion[] { + // available protocols for server mode + serverDefaultProtocolList = new ProtocolList( + getAvailableProtocols(new ProtocolVersion[] { ProtocolVersion.DTLS10, ProtocolVersion.DTLS12 - }; + })); + + supportedCipherSuiteList = getApplicableCipherSuiteList( + supportedProtocolList, false); // all supported + serverDefaultCipherSuiteList = getApplicableCipherSuiteList( + serverDefaultProtocolList, true); // enabled only + } + + @Override + ProtocolList getSuportedProtocolList() { + return supportedProtocolList; + } - defaultServerSSLParams = new SSLParameters(); - defaultServerSSLParams.setProtocols( - getAvailableProtocols(candidates)); + @Override + CipherSuiteList getSupportedCipherSuiteList() { + return supportedCipherSuiteList; } @Override - SSLParameters getDefaultServerSSLParams() { - return defaultServerSSLParams; + ProtocolList getServerDefaultProtocolList() { + return serverDefaultProtocolList; } @Override - SSLParameters getSupportedSSLParams() { - return supportedSSLParams; + CipherSuiteList getServerDefaultCipherSuiteList() { + return serverDefaultCipherSuiteList; } @Override @@ -1028,22 +1053,28 @@ HelloCookieManager getHelloCookieManager(SecureRandom secureRandom) { * @see SSLContext */ public static final class DTLS10Context extends AbstractDTLSContext { - private static final SSLParameters defaultClientSSLParams; + private static final ProtocolList clientDefaultProtocolList; + private static final CipherSuiteList clientDefaultCipherSuiteList; static { - // candidates for available protocols - ProtocolVersion[] candidates = new ProtocolVersion[] { + // available protocols for client mode + clientDefaultProtocolList = new ProtocolList( + getAvailableProtocols(new ProtocolVersion[] { ProtocolVersion.DTLS10 - }; + })); - defaultClientSSLParams = new SSLParameters(); - defaultClientSSLParams.setProtocols( - getAvailableProtocols(candidates)); + clientDefaultCipherSuiteList = getApplicableCipherSuiteList( + clientDefaultProtocolList, true); // enabled only } @Override - SSLParameters getDefaultClientSSLParams() { - return defaultClientSSLParams; + ProtocolList getClientDefaultProtocolList() { + return clientDefaultProtocolList; + } + + @Override + CipherSuiteList getClientDefaultCipherSuiteList() { + return clientDefaultCipherSuiteList; } } @@ -1053,23 +1084,29 @@ SSLParameters getDefaultClientSSLParams() { * @see SSLContext */ public static final class DTLS12Context extends AbstractDTLSContext { - private static final SSLParameters defaultClientSSLParams; + private static final ProtocolList clientDefaultProtocolList; + private static final CipherSuiteList clientDefaultCipherSuiteList; static { - // candidates for available protocols - ProtocolVersion[] candidates = new ProtocolVersion[] { + // available protocols for client mode + clientDefaultProtocolList = new ProtocolList( + getAvailableProtocols(new ProtocolVersion[] { ProtocolVersion.DTLS10, ProtocolVersion.DTLS12 - }; + })); - defaultClientSSLParams = new SSLParameters(); - defaultClientSSLParams.setProtocols( - getAvailableProtocols(candidates)); + clientDefaultCipherSuiteList = getApplicableCipherSuiteList( + clientDefaultProtocolList, true); // enabled only + } + + @Override + ProtocolList getClientDefaultProtocolList() { + return clientDefaultProtocolList; } @Override - SSLParameters getDefaultClientSSLParams() { - return defaultClientSSLParams; + CipherSuiteList getClientDefaultCipherSuiteList() { + return clientDefaultCipherSuiteList; } } @@ -1079,7 +1116,9 @@ SSLParameters getDefaultClientSSLParams() { * @see SSLContext */ private static class CustomizedDTLSContext extends AbstractDTLSContext { - private static final SSLParameters defaultClientSSLParams; + private static final ProtocolList clientDefaultProtocolList; + private static final CipherSuiteList clientDefaultCipherSuiteList; + private static IllegalArgumentException reservedException = null; // Don't want a java.lang.LinkageError for illegal system property. @@ -1119,11 +1158,13 @@ private static class CustomizedDTLSContext extends AbstractDTLSContext { candidates = customizedDTLSProtocols.toArray(candidates); } - defaultClientSSLParams = new SSLParameters(); - defaultClientSSLParams.setProtocols( + clientDefaultProtocolList = new ProtocolList( getAvailableProtocols(candidates)); + clientDefaultCipherSuiteList = getApplicableCipherSuiteList( + clientDefaultProtocolList, true); // enabled only } else { - defaultClientSSLParams = null; // unlikely to be used + clientDefaultProtocolList = null; // unlikely to be used + clientDefaultCipherSuiteList = null; // unlikely to be used } } @@ -1134,8 +1175,13 @@ protected CustomizedDTLSContext() { } @Override - SSLParameters getDefaultClientSSLParams() { - return defaultClientSSLParams; + ProtocolList getClientDefaultProtocolList() { + return clientDefaultProtocolList; + } + + @Override + CipherSuiteList getClientDefaultCipherSuiteList() { + return clientDefaultCipherSuiteList; } } From 1a8918d371b0bdc7765a203a097edf6af88f0e1d Mon Sep 17 00:00:00 2001 From: Olivier Lagneau Date: Fri, 18 Dec 2015 17:42:06 +0100 Subject: [PATCH 60/62] 8058865: JMX Test Refactoring Reviewed-by: jbachorik --- .../MBeanServer/ExceptionFactory.java | 131 +++ .../management/MBeanServer/ExceptionTest.java | 372 ++++++++ .../MBeanServer/ExceptionThrower.java | 35 + .../MBeanServer/ExceptionThrowerMBean.java | 29 + jdk/test/javax/management/mxbean/Basic.java | 530 ++++++++++++ .../javax/management/mxbean/BasicMXBean.java | 203 +++++ .../mxbean/MXBeanExceptionHandlingTest.java | 245 ++++++ .../management/mxbean/MXBeanInteropTest1.java | 638 ++++++++++++++ .../management/mxbean/MXBeanInteropTest2.java | 217 +++++ .../management/mxbean/MXBeanLoadingTest1.java | 329 +++++++ .../management/mxbean/MXBeanNotifTest.java | 385 +++++++++ .../mxbean/MXBeanWeirdParamTest.java | 277 ++++++ .../management/mxbean/SqeDescriptorKey.java | 49 ++ .../management/mxbean/SqeNotification.java | 54 ++ .../javax/management/mxbean/SqeParameter.java | 63 ++ jdk/test/javax/management/mxbean/Utils.java | 241 ++++++ .../javax/management/query/QueryData.java | 32 + .../javax/management/query/QueryFactory.java | 328 +++++++ .../management/query/ServerDelegate.java | 177 ++++ .../management/query/ServerDelegateMBean.java | 67 ++ .../management/query/SqeDescriptorKey.java | 49 ++ .../query/SupportedQueryTypesTest.java | 471 +++++++++++ .../javax/management/query/TestQuery.java | 167 ++++ .../management/query/TestQueryMBean.java | 101 +++ .../security/AuthorizationTest.java | 613 ++++++++++++++ .../javax/management/security/MBS_Light.java | 213 +++++ .../management/security/MBS_LightMBean.java | 108 +++ .../security/RjmxMBeanParameter.java | 47 + .../management/security/SecurityTest.java | 800 ++++++++++++++++++ .../management/security/ServerDelegate.java | 177 ++++ .../security/ServerDelegateMBean.java | 67 ++ .../javax/management/security/Simple.java | 75 ++ .../management/security/SimpleListener.java | 131 +++ .../management/security/SimpleMBean.java | 58 ++ .../management/security/SqeDescriptorKey.java | 49 ++ .../security/TestJMXAuthenticator.java | 107 +++ .../security/TestSampleLoginModule.java | 115 +++ jdk/test/javax/management/security/Utils.java | 424 ++++++++++ .../management/security/access.properties | 11 + .../security/java.policy.authorization | 98 +++ .../javax/management/security/keystoreAgent | Bin 0 -> 1325 bytes .../javax/management/security/keystoreClient | Bin 0 -> 1327 bytes .../javax/management/security/login.config | 8 + .../management/security/password.properties | 12 + .../javax/management/security/truststoreAgent | Bin 0 -> 619 bytes .../management/security/truststoreClient | Bin 0 -> 618 bytes 46 files changed, 8303 insertions(+) create mode 100644 jdk/test/javax/management/MBeanServer/ExceptionFactory.java create mode 100644 jdk/test/javax/management/MBeanServer/ExceptionTest.java create mode 100644 jdk/test/javax/management/MBeanServer/ExceptionThrower.java create mode 100644 jdk/test/javax/management/MBeanServer/ExceptionThrowerMBean.java create mode 100644 jdk/test/javax/management/mxbean/Basic.java create mode 100644 jdk/test/javax/management/mxbean/BasicMXBean.java create mode 100644 jdk/test/javax/management/mxbean/MXBeanExceptionHandlingTest.java create mode 100644 jdk/test/javax/management/mxbean/MXBeanInteropTest1.java create mode 100644 jdk/test/javax/management/mxbean/MXBeanInteropTest2.java create mode 100644 jdk/test/javax/management/mxbean/MXBeanLoadingTest1.java create mode 100644 jdk/test/javax/management/mxbean/MXBeanNotifTest.java create mode 100644 jdk/test/javax/management/mxbean/MXBeanWeirdParamTest.java create mode 100644 jdk/test/javax/management/mxbean/SqeDescriptorKey.java create mode 100644 jdk/test/javax/management/mxbean/SqeNotification.java create mode 100644 jdk/test/javax/management/mxbean/SqeParameter.java create mode 100644 jdk/test/javax/management/mxbean/Utils.java create mode 100644 jdk/test/javax/management/query/QueryData.java create mode 100644 jdk/test/javax/management/query/QueryFactory.java create mode 100644 jdk/test/javax/management/query/ServerDelegate.java create mode 100644 jdk/test/javax/management/query/ServerDelegateMBean.java create mode 100644 jdk/test/javax/management/query/SqeDescriptorKey.java create mode 100644 jdk/test/javax/management/query/SupportedQueryTypesTest.java create mode 100644 jdk/test/javax/management/query/TestQuery.java create mode 100644 jdk/test/javax/management/query/TestQueryMBean.java create mode 100644 jdk/test/javax/management/security/AuthorizationTest.java create mode 100644 jdk/test/javax/management/security/MBS_Light.java create mode 100644 jdk/test/javax/management/security/MBS_LightMBean.java create mode 100644 jdk/test/javax/management/security/RjmxMBeanParameter.java create mode 100644 jdk/test/javax/management/security/SecurityTest.java create mode 100644 jdk/test/javax/management/security/ServerDelegate.java create mode 100644 jdk/test/javax/management/security/ServerDelegateMBean.java create mode 100644 jdk/test/javax/management/security/Simple.java create mode 100644 jdk/test/javax/management/security/SimpleListener.java create mode 100644 jdk/test/javax/management/security/SimpleMBean.java create mode 100644 jdk/test/javax/management/security/SqeDescriptorKey.java create mode 100644 jdk/test/javax/management/security/TestJMXAuthenticator.java create mode 100644 jdk/test/javax/management/security/TestSampleLoginModule.java create mode 100644 jdk/test/javax/management/security/Utils.java create mode 100644 jdk/test/javax/management/security/access.properties create mode 100644 jdk/test/javax/management/security/java.policy.authorization create mode 100644 jdk/test/javax/management/security/keystoreAgent create mode 100644 jdk/test/javax/management/security/keystoreClient create mode 100644 jdk/test/javax/management/security/login.config create mode 100644 jdk/test/javax/management/security/password.properties create mode 100644 jdk/test/javax/management/security/truststoreAgent create mode 100644 jdk/test/javax/management/security/truststoreClient diff --git a/jdk/test/javax/management/MBeanServer/ExceptionFactory.java b/jdk/test/javax/management/MBeanServer/ExceptionFactory.java new file mode 100644 index 00000000000..2315edff3fc --- /dev/null +++ b/jdk/test/javax/management/MBeanServer/ExceptionFactory.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.util.ArrayList; +import javax.management.AttributeNotFoundException; +import javax.management.BadAttributeValueExpException; +import javax.management.BadBinaryOpValueExpException; +import javax.management.BadStringOperationException; +import javax.management.InstanceAlreadyExistsException; +import javax.management.InstanceNotFoundException; +import javax.management.IntrospectionException; +import javax.management.InvalidApplicationException; +import javax.management.InvalidAttributeValueException; +import javax.management.JMException; +import javax.management.JMRuntimeException; +import javax.management.ListenerNotFoundException; +import javax.management.MBeanException; +import javax.management.MBeanRegistrationException; +import javax.management.MalformedObjectNameException; +import javax.management.NotCompliantMBeanException; +import javax.management.OperationsException; +import javax.management.ReflectionException; +import javax.management.RuntimeErrorException; +import javax.management.RuntimeMBeanException; +import javax.management.RuntimeOperationsException; +import javax.management.ServiceNotFoundException; +import javax.management.StringValueExp; +import javax.management.modelmbean.InvalidTargetObjectTypeException; +import javax.management.modelmbean.XMLParseException; +import javax.management.monitor.MonitorSettingException; +import javax.management.openmbean.InvalidKeyException; +import javax.management.openmbean.InvalidOpenTypeException; +import javax.management.openmbean.KeyAlreadyExistsException; +import javax.management.openmbean.OpenDataException; +import javax.management.relation.InvalidRelationIdException; +import javax.management.relation.InvalidRelationServiceException; +import javax.management.relation.InvalidRelationTypeException; +import javax.management.relation.InvalidRoleInfoException; +import javax.management.relation.InvalidRoleValueException; +import javax.management.relation.RelationException; +import javax.management.relation.RelationNotFoundException; +import javax.management.relation.RelationServiceNotRegisteredException; +import javax.management.relation.RelationTypeNotFoundException; +import javax.management.relation.RoleInfoNotFoundException; +import javax.management.relation.RoleNotFoundException; +import javax.management.remote.JMXProviderException; +import javax.management.remote.JMXServerErrorException; + +/** + * |----- Original Description Coming From Tonga Original Source Code -------| + * | | + * | That class creates an ArrayList and fill it with an instance of each of | + * | the Exception class of the JMX API. | + * | It's dedicated to use by ExceptionTest. | + * |-------------------------------------------------------------------------| + */ +public class ExceptionFactory { + + public static final ArrayList exceptions = + new ArrayList(); + + static { + String mes = "SQE"; + exceptions.add(new AttributeNotFoundException()); + exceptions.add(new BadAttributeValueExpException(mes)); + exceptions.add(new BadBinaryOpValueExpException(new StringValueExp(mes))); + exceptions.add(new BadStringOperationException(mes)); + exceptions.add(new InstanceAlreadyExistsException()); + exceptions.add(new InstanceNotFoundException()); + exceptions.add(new IntrospectionException()); + exceptions.add(new InvalidApplicationException(mes)); + exceptions.add(new InvalidAttributeValueException()); + exceptions.add(new JMException()); + exceptions.add(new JMRuntimeException()); + exceptions.add(new ListenerNotFoundException()); + exceptions.add(new MalformedObjectNameException()); + exceptions.add(new MBeanException(new Exception(mes), mes)); + exceptions.add(new MBeanRegistrationException(new Exception(mes), mes)); + exceptions.add(new NotCompliantMBeanException()); + exceptions.add(new OperationsException()); + exceptions.add(new ReflectionException(new Exception(mes), mes)); + exceptions.add(new RuntimeErrorException(new Error(mes), mes)); + exceptions.add(new RuntimeMBeanException(new RuntimeException(mes), mes)); + exceptions.add(new RuntimeOperationsException(new RuntimeException(mes), mes)); + exceptions.add(new ServiceNotFoundException()); + exceptions.add(new InvalidTargetObjectTypeException()); + exceptions.add(new XMLParseException()); + exceptions.add(new MonitorSettingException()); + exceptions.add(new InvalidKeyException()); + exceptions.add(new InvalidOpenTypeException()); + exceptions.add(new KeyAlreadyExistsException()); + exceptions.add(new OpenDataException()); + exceptions.add(new InvalidRelationIdException()); + exceptions.add(new InvalidRelationServiceException()); + exceptions.add(new InvalidRelationTypeException()); + exceptions.add(new InvalidRoleInfoException()); + exceptions.add(new InvalidRoleValueException()); + exceptions.add(new RelationException()); + exceptions.add(new RelationNotFoundException()); + exceptions.add(new RelationServiceNotRegisteredException()); + exceptions.add(new RelationTypeNotFoundException()); + exceptions.add(new RoleInfoNotFoundException()); + exceptions.add(new RoleNotFoundException()); + exceptions.add(new JMXProviderException()); + exceptions.add(new JMXServerErrorException(mes, new Error(mes))); + ExceptionTest.Utils.debug(ExceptionTest.Utils.DEBUG_STANDARD, + "DataFactory::updateMap: Initialized" + + " an ArrayList with the " + + exceptions.size() + " exceptions of the JMX API"); + } +} diff --git a/jdk/test/javax/management/MBeanServer/ExceptionTest.java b/jdk/test/javax/management/MBeanServer/ExceptionTest.java new file mode 100644 index 00000000000..571e43d3a03 --- /dev/null +++ b/jdk/test/javax/management/MBeanServer/ExceptionTest.java @@ -0,0 +1,372 @@ +/* + * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8058865 + * @summary Checks that exceptions are correctly wired (compared to reference). + * @author Olivier Lagneau + * @modules java.management + * @run main/othervm/timeout=300 -DDEBUG_STANDARD ExceptionTest + */ + +import java.util.Map; +import java.util.HashMap; +import java.util.Properties; +import java.lang.reflect.Method; + +import java.lang.management.ManagementFactory; +import javax.management.ObjectName; +import javax.management.MBeanServer; +import javax.management.MBeanServerConnection; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; + + +public class ExceptionTest { + + /* + * First Debug properties and arguments are collect in expected + * map (argName, value) format, then calls original test's run method. + */ + public static void main(String args[]) throws Exception { + + System.out.println("================================================="); + + // Parses parameters + Utils.parseDebugProperties(); + Map map = Utils.parseParameters(args) ; + + // Run test + ExceptionTest test = new ExceptionTest(); + test.run(map); + + } + + public void run(Map args) { + + System.out.println("ExceptionTest::run: Start"); + int errorCount = 0; + + JMXConnectorServer cs = null; + JMXConnector cc = null; + + try { + // JMX MbeanServer used inside single VM as if remote. + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + + JMXServiceURL url = new JMXServiceURL("rmi", null, 0); + cs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs); + cs.start(); + + JMXServiceURL addr = cs.getAddress(); + cc = JMXConnectorFactory.connect(addr); + MBeanServerConnection mbsc = cc.getMBeanServerConnection(); + + // ---- + ObjectName objName = + new ObjectName(ExceptionThrower.EXCEPTION_THROWER_NAME); + System.out.println("ExceptionTest::run: Create and register MBean " + objName); + mbsc.createMBean("ExceptionThrower", objName); + System.out.println("---- OK\n"); + + // ---- + System.out.println("ExceptionTest::run: Ask for exception(s)"); + Object[] throwExceptionParam = new Object[1]; + String[] throwExceptionSig = new String[]{"int"}; + + for (int i = 0; i < ExceptionFactory.exceptions.size(); i++) { + throwExceptionParam[0] = new Integer(i); + + Exception ex = + (Exception)mbsc.invoke(objName, + "throwException", throwExceptionParam, throwExceptionSig); + + if ( ! matches(ex, ExceptionFactory.exceptions.get(i)) ) { + errorCount++; + System.out.println("ExceptionTest::run: (ERROR) Received \n[" + + ex + "]\nin place of\n[" + + ExceptionFactory.exceptions.get(i) + "]"); + } else { + System.out.println("OK [" + ex + "]"); + } + } + + System.out.println("---- DONE\n"); + + } catch (Exception e) { + Utils.printThrowable(e, true); + throw new RuntimeException(); + } finally { + try { + // Close JMX Connector Client + cc.close(); + // Stop connertor server + cs.stop(); + + } catch (Exception e) { + Utils.printThrowable(e, true); + throw new RuntimeException( + "Unable to either close connector client or stop connector server"); + } + } + + if (errorCount == 0) { + System.out.println("ExceptionTest::run: Done without any error"); + } else { + System.out.println("ExceptionTest::run: Done with " + errorCount + + " error(s)"); + throw new RuntimeException("errorCount = " + errorCount); + } + + System.out.println("ExceptionTest::run: Done"); + } + + // Check both Exception are identical. + // That means: + // - none is null. + // - they are of the same Class. + // - if their respective messages aren't null they're equal. + // - if the message of one is null the message of the other is null too. + private boolean matches(Exception ex, Exception refex) { + if ( ex == null || refex == null ) { + System.out.println("(ERROR) Called with one or more null parameter; check " + + ex + " against " + refex); + return false; + } + + String exClass = ex.getClass().getName(); + String refexClass = refex.getClass().getName(); + + if ( ! exClass.equals(refexClass) ) { + System.out.println("(ERROR) Class names don't match; check [" + + exClass + "] against [" + refexClass + "]"); + return false; + } + + String exMes = ex.getMessage(); + String refexMes = refex.getMessage(); + + if ( exMes != null && refexMes != null ) { + if ( ! exMes.equals(refexMes) ) { + System.out.println("(ERROR) Non null messages don't match; check [" + + exMes + "] against [" + refexMes + "]"); + return false; + } + } else if ( (exMes == null && refexMes != null) + || (exMes != null && refexMes == null) ) { + System.out.println("(ERROR) Messages don't match; check [" + exMes + + "] against [" + refexMes + "]"); + } + + return true; + } + + // Utility inner class coming from JMX Tonga test suite. + // Also used by ExceptionFactory. + static class Utils { + + // DEBUG is printed depending on the DEBUG and DEBUG_LEVEL JAVA property + static final String DEBUG_HEADER = "[debug] "; + + // DEBUG levels + static int selectedDebugLevel = 0; + static final int DEBUG_STANDARD = 1; + static final int DEBUG_VERBOSE = 2; // Mainly used for stress tests + static final int DEBUG_ALL = DEBUG_STANDARD | DEBUG_VERBOSE; + + static void parseDebugProperties() { + int level = 0; + Properties p = System.getProperties(); + + // get selected levels + if (p.getProperty("DEBUG_STANDARD") != null) { + level |= DEBUG_STANDARD; + } + + if (p.getProperty("DEBUG_VERBOSE") != null) { + level |= DEBUG_VERBOSE; + } + + if (p.getProperty("DEBUG_ALL") != null) { + level |= DEBUG_ALL; + } + + selectedDebugLevel = level; + } + + /** + * Reproduces the original parsing and collection of test parameters + * from the DTonga JMX test suite. + * + * Collects passed args and returns them in a map(argname, value) structure, + * which will be then propagated as necessary to various called methods. + */ + static Map parseParameters(String args[]) + throws Exception { + debug(DEBUG_STANDARD, "TestRoot::parseParameters: Start"); + HashMap map = new HashMap<>(); + + for ( int i = 0; i < args.length; i++ ) { + if ( args[i].trim().startsWith("-") ) { + if ((i+1) < args.length && !args[i+1].startsWith("-") ) { + debug(DEBUG_STANDARD, + "TestRoot::parseParameters: added in map = " + + args[i] + + " with value " + + args[i+1]) ; + map.put(args[i].trim(), args[i+1].trim()) ; + } else if ((i+1) < args.length && args[i+1].startsWith("-") || + (i+1) == args.length ) { + debug(DEBUG_STANDARD, + "TestRoot::parseParameters: added in map = " + + args[i] + + " with null value") ; + map.put(args[i].trim(), null) ; + } else { + System.out.println( + "TestRoot::parseParameters: (WARNING) not added in map = " + + args[i]) ; + } + } + } + + debug(DEBUG_STANDARD, "TestRoot::parseParameters: Done") ; + return map ; + } + + /** + * This method is to be used in all tests to print anything + * that is temporary. + * Printing is done only when debug is activated by the property DEBUG. + * Printing depends also on the DEBUG_LEVEL property. + * Here it encapsulates a System.out.println. + */ + static void debug(int level, String line) { + if ((selectedDebugLevel & level) != 0) { + System.out.println(DEBUG_HEADER + line); + } + } + + /** + * Do print stack trace when withStack is true. + * Does try to call getTargetException() and getTargetError() then + * print embedded stacks in the case of an Exception wrapping + * another Exception or an Error. Recurse until no more wrapping + * is found. + */ + static void printThrowable(Throwable theThro, boolean withStack) { + try { + if (withStack) { + theThro.printStackTrace(System.out); + } + if (theThro instanceof Exception) { + Exception t = (Exception) theThro; + Method target = null; + String blank = " "; + try { + target = t.getClass().getMethod("getTargetException", + (java.lang.Class[]) null); + } catch (Exception ee) { + // OK: getTargetException method could be there or not + } + System.out.println(blank + t.getClass() + "==>" + t.getMessage()); + while (target != null) { + try { + t = (Exception) target.invoke(t, + (java.lang.Object[]) null); + } catch (Exception ee) { + t = null; + } + try { + if (t != null) { + blank = blank + " "; + System.out.println(blank + t.getClass() + "==>" + + t.getMessage()); + try { + target = + t.getClass().getMethod("getTargetException", + (java.lang.Class[]) null); + } catch (Exception ee) { + // OK: getTargetException method could be there or not } + } + } else { + target = null; + } + } catch (Exception ee) { + target = null; + } + } + + // We may have exceptions wrapping an Error then it is + // getTargetError that is likely to be called + try { + target = ((Exception) theThro).getClass().getMethod("getTargetError", + (java.lang.Class[]) null); + } catch (Exception ee) { + // OK: getTargetError method could be there or not + } + Throwable err = theThro; + while (target != null) { + try { + err = (Error) target.invoke(err, + (java.lang.Object[]) null); + } catch (Exception ee) { + err = null; + } + try { + if (err != null) { + blank = blank + " "; + System.out.println(blank + err.getClass() + "==>" + + err.getMessage()); + if (withStack) { + err.printStackTrace(System.out); + } + try { + target = err.getClass().getMethod("getTargetError", + (java.lang.Class[]) null); + } catch (Exception ee) { + // OK: getTargetError method could be there or not + } + } else { + target = null; + } + } catch (Exception ee) { + target = null; + } + } + } else { + System.out.println("Throwable is : " + theThro); + } + } catch (Throwable x) { + System.out.println("Exception : raised in printException : " + x); + } + } + } + +} + + diff --git a/jdk/test/javax/management/MBeanServer/ExceptionThrower.java b/jdk/test/javax/management/MBeanServer/ExceptionThrower.java new file mode 100644 index 00000000000..37d661eaca6 --- /dev/null +++ b/jdk/test/javax/management/MBeanServer/ExceptionThrower.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * This class defines a simple standard MBean. + */ +public class ExceptionThrower implements ExceptionThrowerMBean { + + public static final String EXCEPTION_THROWER_NAME + = "sqe:type=ExceptionThrower"; + + public Exception throwException(int exceptionIndex) { + return ExceptionFactory.exceptions.get(exceptionIndex); + } +} diff --git a/jdk/test/javax/management/MBeanServer/ExceptionThrowerMBean.java b/jdk/test/javax/management/MBeanServer/ExceptionThrowerMBean.java new file mode 100644 index 00000000000..d5485d30e67 --- /dev/null +++ b/jdk/test/javax/management/MBeanServer/ExceptionThrowerMBean.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * This interface defines a simple standard MBean. + */ +public interface ExceptionThrowerMBean { + public Exception throwException(int exceptionIndex); +} diff --git a/jdk/test/javax/management/mxbean/Basic.java b/jdk/test/javax/management/mxbean/Basic.java new file mode 100644 index 00000000000..3f32af666b9 --- /dev/null +++ b/jdk/test/javax/management/mxbean/Basic.java @@ -0,0 +1,530 @@ +/* + * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import javax.management.Descriptor; +import javax.management.ImmutableDescriptor; +import javax.management.ListenerNotFoundException; +import javax.management.MBeanNotificationInfo; +import javax.management.MBeanRegistration; +import javax.management.MBeanServer; +import javax.management.Notification; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationEmitter; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectName; + +/** + * Class Basic + * Basic Description + */ +public class Basic implements BasicMXBean, NotificationEmitter, + MBeanRegistration { + + public static final String EXCEPTION_MESSAGE = "from Basic"; + public static final String NOTIFICATION_MESSAGE = "from Basic"; + /** Attribute : IntAtt */ + private int intAtt = 0; + /** Attribute : IntegerAtt */ + private Integer integerAtt = 0; + /** Attribute : BoolAtt */ + private boolean boolAtt = false; + /** Attribute : BooleanAtt */ + private Boolean booleanAtt = false; + /** Attribute : StringAtt */ + private String stringAtt = null; + /** Attribute : DateAtt */ + private Date dateAtt = null; + /** Attribute : ObjectNameAtt */ + private ObjectName objectNameAtt = null; + /** Attribute : NotifDescriptorAsMapAtt */ + private Map notifDescriptorAsMapAtt = null; + /** Attribute : NotifDescriptorAtt */ + private Descriptor notifDescriptorAtt = null; + /** Attribute : SqeParameter */ + private SqeParameter sqeParameterAtt = null; + + /* Creates a new instance of Basic */ + @SqeDescriptorKey("CONSTRUCTOR Basic") + public Basic() { + } + + /* Creates a new instance of Basic */ + @SqeDescriptorKey("CONSTRUCTOR Basic") + public Basic( + @SqeDescriptorKey("CONSTRUCTOR PARAMETER SqeParameter") SqeParameter param) { + } + + /** + * Get int attribute + */ + public int getIntAtt() { + return intAtt; + } + + /** + * Set int attribute + */ + public void setIntAtt(int value) { + intAtt = value; + } + + /** + * Get Integer attribute + */ + public Integer getIntegerAtt() { + return integerAtt; + } + + /** + * Set Integer attribute + */ + public void setIntegerAtt(Integer value) { + integerAtt = value; + } + + /** + * Get boolean attribute + */ + public boolean getBoolAtt() { + return boolAtt; + } + + /** + * Set boolean attribute + */ + public void setBoolAtt(boolean value) { + boolAtt = value; + } + + /** + * Get Boolean attribute + */ + public Boolean getBooleanAtt() { + return booleanAtt; + } + + /** + * Set Boolean attribute + */ + public void setBooleanAtt(Boolean value) { + booleanAtt = value; + } + + /** + * Get String attribute + */ + public String getStringAtt() { + return stringAtt; + } + + /** + * Set String attribute + */ + public void setStringAtt(String value) { + stringAtt = value; + } + + /** + * Get Date attribute + */ + public Date getDateAtt() { + return dateAtt; + } + + /** + * Set Date attribute + */ + public void setDateAtt(Date value) { + dateAtt = value; + } + + /** + * Get ObjectName attribute + */ + public ObjectName getObjectNameAtt() { + return objectNameAtt; + } + + /** + * Set ObjectName attribute + */ + public void setObjectNameAtt(ObjectName value) { + objectNameAtt = value; + } + + /** + * Get SqeParameter attribute + */ + public SqeParameter getSqeParameterAtt() throws Exception { + if (sqeParameterAtt == null) { + sqeParameterAtt = new SqeParameter(); + sqeParameterAtt.setGlop("INITIALIZED"); + } + + return sqeParameterAtt; + } + + /** + * Set SqeParameter attribute + */ + public void setSqeParameterAtt(SqeParameter value) { + sqeParameterAtt = value; + } + + /** + * Get the Descriptor used to build the NotificationInfo + * of emitted notifications. + */ + public Map getNotifDescriptorAsMapAtt() { + if (notifDescriptorAsMapAtt == null) { + initNotifDescriptorAtt(); + } + + return notifDescriptorAsMapAtt; + } + + /** + * Set the Descriptor used to build the NotificationInfo + * of emitted notifications. + *
    A Map would better fit Descriptor needs but then + * it is not convertible according the MXBean specification so the MBean + * registration fails. + * As we plan to test our custom Descriptor finds its way into + * the metadata of emitted notifications, String is good enough. + */ + public void setNotifDescriptorAsMapAtt(Map value) { + notifDescriptorAsMapAtt = new HashMap(value); + notifDescriptorAtt = new ImmutableDescriptor(value); + } + + /** + * Do nothing + */ + public void doNothing() { + // I said NOTHING ! + } + + /** + * Do take SqeParameter as a parameter + */ + public void doWeird(SqeParameter param) { + } + + /** + * Throw an Exception + */ + public void throwException() throws Exception { + throw new Exception(EXCEPTION_MESSAGE); + } + + /** + * Throw an Error + */ + public void throwError() { + throw new InternalError(EXCEPTION_MESSAGE); + } + + /** + * Reset all attributes + */ + public void reset() { + intAtt = 0; + integerAtt = 0; + boolAtt = false; + booleanAtt = Boolean.FALSE; + stringAtt = null; + dateAtt = null; + objectNameAtt = null; + } + + /** + * Returns the weather for the coming days + * @param verbose boolean verbosity + * @throws java.lang.Exception storm + * @return ObjectName + */ + public Weather getWeather(boolean verbose) + throws java.lang.Exception { + return Weather.SUNNY; + } + + // Starting here are the 4 methods of MBeanRegistration interface. + // We use that to grab the ObjectName the MBean is registered with. + // + public ObjectName preRegister(MBeanServer server, ObjectName name) + throws Exception { + // Grab a reference on the MBeanServer we're registered in. + mbs = server; + // Compute the name we're registered with. + if (name != null) { + mbeanName = name; + return name; + } else { + mbeanName = + new ObjectName("sqe:type=" + Basic.class.getName()); + return mbeanName; + } + } + + public void postRegister(Boolean registrationDone) { + // Do nothing + } + + public void preDeregister() throws Exception { + // Do nothing + } + + public void postDeregister() { + // Do nothing + } + + /** + * Send one Notification of the provided notifType type. + */ + public void sendNotification(String notifType) { + Notification notification = null; + + if (notifType.equals(NOTIF_TYPE_0)) { + notification = new Notification(NOTIF_TYPE_0, + mbeanName, + seqNumber, + NOTIFICATION_MESSAGE); + } else if (notifType.equals(NOTIF_TYPE_1)) { + notification = new SqeNotification(NOTIF_TYPE_1, + mbeanName, + seqNumber, + NOTIFICATION_MESSAGE); + } + + seqNumber++; + broadcaster.sendNotification(notification); + } + + /** + * That method starts a set of threads, each thread sends a given number of + * notifications. + * The number of threads can be set via the attribute numOfNotificationSenders. + * The number of notification sent by each thread can be set via + * the attribute numOfNotificationSenderLoops. + * Depending on the parameter customNotification we send either custom + * notification(s) or MBeanServer registration and unregistration notification(s). + * When customNotification=true the total number of notification(s) sent is + * (numOfNotificationSenders * numOfNotificationSenderLoops). They are + * sequentially of type NOTIF_TYPE_0 then NOTIF_TYPE_1 and so on. + * + * When customNotification=false the total number of notification(s) sent is + * (numOfNotificationSenders * numOfNotificationSenderLoops) registration + * notification(s) + * + + * (numOfNotificationSenders * numOfNotificationSenderLoops) unregistration + * notification(s) + * + * @throws java.lang.Exception + */ + public void sendNotificationWave(boolean customNotification) throws + Exception { + // Build the set of notification sender. + Collection> tasks = + new HashSet>(numOfNotificationSenders); + + for (int i = 1; i <= numOfNotificationSenders; i++) { + tasks.add(new NotifSender(numOfNotificationSenderLoops, + customNotification, i)); + } + + // Start all notification sender in parallel. + ExecutorService execServ = null; + try { + execServ = Executors.newFixedThreadPool(numOfNotificationSenders); + List> taskHandlers = execServ.invokeAll(tasks); + checkNotifSenderThreadStatus(taskHandlers); + } finally { + if (!execServ.isShutdown()) { + execServ.shutdown(); + } + } + } + + public void setNumOfNotificationSenders(int value) { + numOfNotificationSenders = value; + } + + public void setNumOfNotificationSenderLoops(int value) { + numOfNotificationSenderLoops = value; + } + + /** + * MBean Notification support + * You shouldn't update these methods + */ + // + public void addNotificationListener(NotificationListener listener, + NotificationFilter filter, + Object handback) + throws IllegalArgumentException { + broadcaster.addNotificationListener(listener, filter, handback); + } + + public MBeanNotificationInfo[] getNotificationInfo() { + if (notifDescriptorAtt == null) { + initNotifDescriptorAtt(); + } + + return new MBeanNotificationInfo[]{ + new MBeanNotificationInfo(new String[]{ + NOTIF_TYPE_0 + }, + javax.management.Notification.class.getName(), + "Standard JMX Notification", + notifDescriptorAtt), + new MBeanNotificationInfo(new String[]{ + NOTIF_TYPE_1 + }, + SqeNotification.class.getName(), + "SQE Notification", + notifDescriptorAtt) + }; + } + + public void removeNotificationListener(NotificationListener listener) + throws ListenerNotFoundException { + broadcaster.removeNotificationListener(listener); + } + + public void removeNotificationListener(NotificationListener listener, + NotificationFilter filter, + Object handback) + throws ListenerNotFoundException { + broadcaster.removeNotificationListener(listener, filter, handback); + } + // + private synchronized long getNextSeqNumber() { + return seqNumber++; + } + + private void initNotifDescriptorAtt() { + String key = "CRABE"; + String value = "TAMBOUR"; + notifDescriptorAtt = + new ImmutableDescriptor(new String[]{key + "=" + value}); + notifDescriptorAsMapAtt = + new HashMap(); + notifDescriptorAsMapAtt.put(key, value); + } + + private void checkNotifSenderThreadStatus( + List> taskHandlers) + throws Exception { + String msgTag = "Basic::checkNotifSenderThreadStatus: "; + // Grab back status of each notification sender. + for (Future f : taskHandlers) { + if (f.isCancelled()) { + String message = msgTag + + "---- ERROR : One thread has been cancelled"; + System.out.println(message); + throw new RuntimeException(message); + } else { + Integer effectiveNumOfLoops = f.get(); + + if (effectiveNumOfLoops != numOfNotificationSenderLoops) { + String message = msgTag + "---- ERROR : One thread did " + + effectiveNumOfLoops + " loops in place of " + + numOfNotificationSenderLoops; + System.out.println(message); + throw new RuntimeException(message); + } + } + } + } + // + private int numOfNotificationSenderLoops = 2; + private int numOfNotificationSenders = 13; + + private class NotifSender implements Callable { + + private int cycles; + private boolean customNotification; + private int senderID; + + public NotifSender(int cycles, boolean customNotification, int id) { + this.cycles = cycles; + this.customNotification = customNotification; + this.senderID = id; + } + + public Integer call() throws Exception { + int callsDone = 0; + + try { + for (int i = 1; i <= cycles; i++) { + if (customNotification) { + if (i % 2 == 0) { + sendNotification(NOTIF_TYPE_0); + } else { + sendNotification(NOTIF_TYPE_1); + } + } else { + ObjectName mbeanName = new ObjectName("SQE:type=" + + mbeanClassName + ",senderID=" + senderID); + mbs.createMBean(mbeanClassName, mbeanName); + mbs.unregisterMBean(mbeanName); + } + callsDone++; + } + } catch (Exception e) { + System.out.println("NotifSender::call: (ERROR) Thread [" + senderID + + "] failed after " + callsDone + " cycles"); + throw e; + } + + return Integer.valueOf(callsDone); + } + } + + // + private long seqNumber; + private final NotificationBroadcasterSupport broadcaster = + new NotificationBroadcasterSupport(); + private ObjectName mbeanName; + private MBeanServer mbs; + private String mbeanClassName = "Simple"; + + /** + * Notification types definitions. To use when creating JMX Notifications. + */ + public static final String NOTIF_TYPE_0 = + "sqe.notification.a.type"; + public static final String NOTIF_TYPE_1 = + "sqe.notification.b.type"; +} diff --git a/jdk/test/javax/management/mxbean/BasicMXBean.java b/jdk/test/javax/management/mxbean/BasicMXBean.java new file mode 100644 index 00000000000..7693be90c9f --- /dev/null +++ b/jdk/test/javax/management/mxbean/BasicMXBean.java @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.util.Date; +import java.util.Map; + +import javax.management.ObjectName; + +/** + * Interface BasicMBean + * Basic Description + */ +@SqeDescriptorKey("INTERFACE BasicMXBean") +public interface BasicMXBean +{ + /** + * Get int attribute + */ + @SqeDescriptorKey("ATTRIBUTE intAtt") + public int getIntAtt(); + + /** + * Set int attribute + */ + @SqeDescriptorKey("ATTRIBUTE intAtt") + public void setIntAtt(int value); + + /** + * Get Integer attribute + */ + @SqeDescriptorKey("ATTRIBUTE integerAtt") + public Integer getIntegerAtt(); + + /** + * Set Integer attribute + */ + @SqeDescriptorKey("ATTRIBUTE integerAtt") + public void setIntegerAtt(Integer value); + + /** + * Get boolean attribute + */ + @SqeDescriptorKey("ATTRIBUTE boolAtt") + public boolean getBoolAtt(); + + /** + * Set boolean attribute + */ + @SqeDescriptorKey("ATTRIBUTE boolAtt") + public void setBoolAtt(boolean value); + + /** + * Get Boolean attribute + */ + @SqeDescriptorKey("ATTRIBUTE booleanAtt") + public Boolean getBooleanAtt(); + + /** + * Set Boolean attribute + */ + @SqeDescriptorKey("ATTRIBUTE booleanAtt") + public void setBooleanAtt(Boolean value); + + /** + * Get String attribute + */ + @SqeDescriptorKey("ATTRIBUTE stringAtt") + public String getStringAtt(); + + /** + * Set String attribute + */ + @SqeDescriptorKey("ATTRIBUTE stringAtt") + public void setStringAtt(String value); + + /** + * Get Date attribute + */ + @SqeDescriptorKey("ATTRIBUTE dateAtt") + public Date getDateAtt(); + + /** + * Set Date attribute + */ + @SqeDescriptorKey("ATTRIBUTE dateAtt") + public void setDateAtt(Date value); + + /** + * Get ObjectName attribute + */ + @SqeDescriptorKey("ATTRIBUTE objectNameAtt") + public ObjectName getObjectNameAtt(); + + /** + * Set ObjectName attribute + */ + @SqeDescriptorKey("ATTRIBUTE objectNameAtt") + public void setObjectNameAtt(ObjectName value); + + /** + * Get SqeParameter attribute + */ + @SqeDescriptorKey("ATTRIBUTE sqeParameterAtt") + public SqeParameter getSqeParameterAtt() throws Exception; + + /** + * Set SqeParameter attribute + */ + @SqeDescriptorKey("ATTRIBUTE sqeParameterAtt") + public void setSqeParameterAtt(SqeParameter value); + + /** + * Set NumOfNotificationSenders attribute + */ + @SqeDescriptorKey("ATTRIBUTE NumOfNotificationSenders") + public void setNumOfNotificationSenders(int value); + + /** + * Set NumOfNotificationSenderLoops attribute + */ + @SqeDescriptorKey("ATTRIBUTE NumOfNotificationSenderLoops") + public void setNumOfNotificationSenderLoops(int value); + + /** + * do nothing + * + */ + @SqeDescriptorKey("OPERATION doNothing") + public void doNothing(); + + /** + * Do take SqeParameter as a parameter + */ + @SqeDescriptorKey("OPERATION doWeird") + public void doWeird(@SqeDescriptorKey("METHOD PARAMETER")SqeParameter param); + + /** + * throw an Exception + * + */ + @SqeDescriptorKey("OPERATION throwException") + public void throwException() throws Exception; + + /** + * throw an Error + * + */ + @SqeDescriptorKey("OPERATION throwError") + public void throwError(); + + /** + * reset all attributes + * + */ + @SqeDescriptorKey("OPERATION reset") + public void reset(); + + /** + * returns the weather for the coming days + * + * @param verbose boolean verbosity + * @return ObjectName + */ + @SqeDescriptorKey("OPERATION getWeather") + public Weather getWeather(@SqeDescriptorKey("METHOD PARAMETER")boolean verbose) + throws java.lang.Exception; + + public enum Weather { + CLOUDY, SUNNY + } + + @SqeDescriptorKey("ATTRIBUTE notifDescriptorAsMapAtt") + public Map getNotifDescriptorAsMapAtt(); + + @SqeDescriptorKey("ATTRIBUTE notifDescriptorAsMapAtt") + public void setNotifDescriptorAsMapAtt(Map value); + + @SqeDescriptorKey("OPERATION sendNotification") + public void sendNotification(@SqeDescriptorKey("METHOD PARAMETER")String notifType); + + @SqeDescriptorKey("OPERATION sendNotificationWave") + public void sendNotificationWave(boolean customNotification) throws Exception; +} diff --git a/jdk/test/javax/management/mxbean/MXBeanExceptionHandlingTest.java b/jdk/test/javax/management/mxbean/MXBeanExceptionHandlingTest.java new file mode 100644 index 00000000000..96071980c1e --- /dev/null +++ b/jdk/test/javax/management/mxbean/MXBeanExceptionHandlingTest.java @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8058865 + * @summary Checks correct exception and error events from NotificationListener + * @author Olivier Lagneau + * @modules java.management + * @library /lib/testlibrary + * @compile Basic.java + * @run main/othervm/timeout=300 -DDEBUG_STANDARD MXBeanExceptionHandlingTest -timeForNotificationInSeconds 3 + */ + + +import java.util.Map; +import java.util.HashMap; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; + +import java.lang.management.ManagementFactory; +import javax.management.MBeanServer; +import javax.management.MBeanException; +import javax.management.MBeanServerDelegate; +import javax.management.Notification; +import javax.management.NotificationListener; +import javax.management.MBeanServerConnection; +import javax.management.ObjectName; +import javax.management.RuntimeErrorException; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; + +public class MXBeanExceptionHandlingTest implements NotificationListener { + + private static String BASIC_MXBEAN_CLASS_NAME = "Basic"; + + private long timeForNotificationInSeconds = 3L; + private int numOfNotifications = 2; + private BlockingQueue notifList = null; + + + /* + * First Debug properties and arguments are collect in expected + * map (argName, value) format, then calls original test's run method. + */ + public static void main(String args[]) throws Exception { + + System.out.println("================================================="); + + // Parses parameters + Utils.parseDebugProperties(); + Map map = Utils.parseParameters(args) ; + + // Run test + MXBeanExceptionHandlingTest test = new MXBeanExceptionHandlingTest(); + test.run(map); + + } + + protected void parseArgs(Map args) throws Exception { + + String arg = null; + + // Init timeForNotificationInSeconds + // It is the maximum time in seconds we wait for a notification. + arg = (String)args.get("-timeForNotificationInSeconds") ; + if (arg != null) { + timeForNotificationInSeconds = (new Long(arg)).longValue(); + } + + } + + public void run(Map args) { + + System.out.println("MXBeanExceptionHandlingTest::run: Start") ; + int errorCount = 0 ; + + try { + parseArgs(args); + notifList = new ArrayBlockingQueue(numOfNotifications); + + // JMX MbeanServer used inside single VM as if remote. + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + + JMXServiceURL url = new JMXServiceURL("rmi", null, 0); + JMXConnectorServer cs = + JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs); + cs.start(); + + JMXServiceURL addr = cs.getAddress(); + JMXConnector cc = JMXConnectorFactory.connect(addr); + MBeanServerConnection mbsc = cc.getMBeanServerConnection(); + + // ---- + System.out.println("Add me as notification listener"); + mbsc.addNotificationListener(MBeanServerDelegate.DELEGATE_NAME, + this, null, null); + System.out.println("---- OK\n") ; + + // ---- + System.out.println("Create and register the MBean"); + ObjectName objName = new ObjectName("sqe:type=Basic,protocol=rmi") ; + mbsc.createMBean(BASIC_MXBEAN_CLASS_NAME, objName); + System.out.println("---- OK\n") ; + + // ---- + System.out.println("Call method throwException on our MXBean"); + + try { + mbsc.invoke(objName, "throwException", null, null); + errorCount++; + System.out.println("(ERROR) Did not get awaited MBeanException") ; + } catch (MBeanException mbe) { + System.out.println("(OK) Got awaited MBeanException") ; + Throwable cause = mbe.getCause(); + + if ( cause instanceof java.lang.Exception ) { + System.out.println("(OK) Cause is of the right class") ; + String mess = cause.getMessage(); + + if ( mess.equals(Basic.EXCEPTION_MESSAGE ) ) { + System.out.println("(OK) Cause message is fine") ; + } else { + errorCount++; + System.out.println("(ERROR) Cause has message " + + cause.getMessage() + + " as we expect " + + Basic.EXCEPTION_MESSAGE) ; + } + } else { + errorCount++; + System.out.println("(ERROR) Cause is of class " + + cause.getClass().getName() + + " as we expect java.lang.Exception") ; + } + } catch (Exception e) { + errorCount++; + System.out.println("(ERROR) Did not get awaited MBeanException but " + + e) ; + Utils.printThrowable(e, true); + } + System.out.println("---- DONE\n") ; + + // ---- + System.out.println("Call method throwError on our MXBean"); + + try { + mbsc.invoke(objName, "throwError", null, null); + errorCount++; + System.out.println("(ERROR) Did not get awaited RuntimeErrorException") ; + } catch (RuntimeErrorException ree) { + System.out.println("(OK) Got awaited RuntimeErrorException") ; + Throwable cause = ree.getCause(); + + if ( cause instanceof java.lang.InternalError ) { + System.out.println("(OK) Cause is of the right class") ; + String mess = cause.getMessage(); + + if ( mess.equals(Basic.EXCEPTION_MESSAGE ) ) { + System.out.println("(OK) Cause message is fine") ; + } else { + errorCount++; + System.out.println("(ERROR) Cause has message " + + cause.getMessage() + + " as we expect " + + Basic.EXCEPTION_MESSAGE) ; + } + } else { + errorCount++; + System.out.println("(ERROR) Cause is of class " + + cause.getClass().getName() + + " as we expect java.lang.InternalError") ; + } + } catch (Exception e) { + errorCount++; + System.out.println("(ERROR) Did not get awaited RuntimeErrorException but " + + e) ; + Utils.printThrowable(e, true); + } + System.out.println("---- DONE\n") ; + + // ---- + System.out.println("Unregister the MBean"); + mbsc.unregisterMBean(objName); + System.out.println("---- OK\n") ; + + Thread.sleep(timeForNotificationInSeconds * 1000); + int numOfReceivedNotif = notifList.size(); + + if ( numOfReceivedNotif == numOfNotifications ) { + System.out.println("(OK) We received " + + numOfNotifications + + " Notifications") ; + } else { + errorCount++; + System.out.println("(ERROR) We received " + + numOfReceivedNotif + + " Notifications in place of " + + numOfNotifications) ; + } + } catch(Exception e) { + Utils.printThrowable(e, true) ; + throw new RuntimeException(e); + } + + if ( errorCount == 0 ) { + System.out.println("MXBeanExceptionHandlingTest::run: Done without any error") ; + } else { + System.out.println("MXBeanExceptionHandlingTest::run: Done with " + + errorCount + + " error(s)") ; + throw new RuntimeException("errorCount = " + errorCount); + } + } + + public void handleNotification(Notification notification, Object handback) { + System.out.println("MXBeanExceptionHandlingTest::handleNotification: Received " + + notification); + notifList.add(notification); + } + +} diff --git a/jdk/test/javax/management/mxbean/MXBeanInteropTest1.java b/jdk/test/javax/management/mxbean/MXBeanInteropTest1.java new file mode 100644 index 00000000000..5f0e834a72e --- /dev/null +++ b/jdk/test/javax/management/mxbean/MXBeanInteropTest1.java @@ -0,0 +1,638 @@ +/* + * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8058865 + * @summary Test all MXBeans available by default on the platform + * @author Olivier Lagneau + * @modules java.management + * @library /lib/testlibrary + * @run main/othervm/timeout=300 -DDEBUG_STANDARD MXBeanInteropTest1 + */ + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import java.lang.management.ClassLoadingMXBean; +import java.lang.management.CompilationMXBean; +import java.lang.management.GarbageCollectorMXBean; +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; +import java.lang.management.MemoryManagerMXBean; +import java.lang.management.MemoryPoolMXBean; +import java.lang.management.OperatingSystemMXBean; +import java.lang.management.RuntimeMXBean; +import java.lang.management.ThreadMXBean; + +import javax.management.JMX; +import javax.management.MBeanAttributeInfo; +import javax.management.MBeanConstructorInfo; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.MBeanInfo; +import javax.management.MBeanNotificationInfo; +import javax.management.MBeanOperationInfo; +import javax.management.MBeanServerConnection; +import javax.management.ObjectName; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; + +public class MXBeanInteropTest1 { + + /* + * First Debug properties and arguments are collect in expected + * map (argName, value) format, then calls original test's run method. + */ + public static void main(String args[]) throws Exception { + + System.out.println("================================================="); + + // Parses parameters + Utils.parseDebugProperties(); + Map map = Utils.parseParameters(args) ; + + // Run test + MXBeanInteropTest1 test = new MXBeanInteropTest1(); + test.run(map); + + } + + public void run(Map args) { + + System.out.println("MXBeanInteropTest1::run: Start") ; + int errorCount = 0 ; + + try { + // JMX MbeanServer used inside single VM as if remote. + // MBeanServer mbs = MBeanServerFactory.newMBeanServer(); + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + + JMXServiceURL url = new JMXServiceURL("rmi", null, 0); + JMXConnectorServer cs = + JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs); + cs.start(); + + JMXServiceURL addr = cs.getAddress(); + JMXConnector cc = JMXConnectorFactory.connect(addr); + MBeanServerConnection mbsc = cc.getMBeanServerConnection(); + + // Print out registered java.lang.management MXBeans found + // in the remote jvm. + printMBeans(mbsc) ; + + // For each possible kind of JDK 5 defined MXBean, we retrieve its + // MBeanInfo and print it and we call all getters and print + // their output. + errorCount += doClassLoadingMXBeanTest(mbsc) ; + errorCount += doMemoryMXBeanTest(mbsc) ; + errorCount += doThreadMXBeanTest(mbsc) ; + errorCount += doRuntimeMXBeanTest(mbsc) ; + errorCount += doOperatingSystemMXBeanTest(mbsc) ; + errorCount += doCompilationMXBeanTest(mbsc) ; + errorCount += doGarbageCollectorMXBeanTest(mbsc) ; + errorCount += doMemoryManagerMXBeanTest(mbsc) ; + errorCount += doMemoryPoolMXBeanTest(mbsc) ; + + // Terminate the JMX Client + cc.close(); + + } catch(Exception e) { + Utils.printThrowable(e, true) ; + throw new RuntimeException(e); + } + + if ( errorCount == 0 ) { + System.out.println("MXBeanInteropTest1::run: Done without any error") ; + } else { + System.out.println("MXBeanInteropTest1::run: Done with " + + errorCount + + " error(s)") ; + throw new RuntimeException("errorCount = " + errorCount); + } + } + + /** + * Prints all MBeans of domain java.lang. + * They are MBeans related to the JSR 174 that defines + * package java.lang.management. + */ + private static void printMBeans(MBeanServerConnection mbsc) throws Exception { + ObjectName filterName = new ObjectName("java.lang:*"); + Set set = mbsc.queryNames(filterName, null); + + if ( set.size() == 0 ) { + throw new RuntimeException("(ERROR) No MBean found with filter " + + filterName); + } + + System.out.println("---- MBeans found in domain java.lang :"); + + for (Iterator iter = set.iterator(); iter.hasNext(); ) { + System.out.println(iter.next().toString()); + } + + System.out.println("\n") ; + } + + + private final int doClassLoadingMXBeanTest(MBeanServerConnection mbsc) { + int errorCount = 0 ; + System.out.println("---- ClassLoadingMXBean") ; + + try { + ObjectName classLoadingName = + new ObjectName(ManagementFactory.CLASS_LOADING_MXBEAN_NAME) ; + MBeanInfo mbInfo = mbsc.getMBeanInfo(classLoadingName); + errorCount += checkNonEmpty(mbInfo); + System.out.println("getMBeanInfo\t\t" + + mbInfo); + ClassLoadingMXBean classLoading = null; + + classLoading = JMX.newMXBeanProxy(mbsc, + classLoadingName, + ClassLoadingMXBean.class) ; + System.out.println("getLoadedClassCount\t\t" + + classLoading.getLoadedClassCount()); + System.out.println("getTotalLoadedClassCount\t\t" + + classLoading.getTotalLoadedClassCount()); + System.out.println("getUnloadedClassCount\t\t" + + classLoading.getUnloadedClassCount()); + System.out.println("isVerbose\t\t" + + classLoading.isVerbose()); + + System.out.println("---- OK\n") ; + + } catch (Exception e) { + Utils.printThrowable(e, true) ; + errorCount++ ; + System.out.println("---- ERROR\n") ; + } + + return errorCount ; + } + + + private final int doMemoryMXBeanTest(MBeanServerConnection mbsc) { + int errorCount = 0 ; + System.out.println("---- MemoryMXBean") ; + + try { + ObjectName memoryName = + new ObjectName(ManagementFactory.MEMORY_MXBEAN_NAME) ; + MBeanInfo mbInfo = mbsc.getMBeanInfo(memoryName); + errorCount += checkNonEmpty(mbInfo); + System.out.println("getMBeanInfo\t\t" + + mbInfo); + MemoryMXBean memory = null ; + + memory = + JMX.newMXBeanProxy(mbsc, + memoryName, + MemoryMXBean.class, + true) ; + System.out.println("getMemoryHeapUsage\t\t" + + memory.getHeapMemoryUsage()); + System.out.println("getNonHeapMemoryHeapUsage\t\t" + + memory.getNonHeapMemoryUsage()); + System.out.println("getObjectPendingFinalizationCount\t\t" + + memory.getObjectPendingFinalizationCount()); + System.out.println("isVerbose\t\t" + + memory.isVerbose()); + + System.out.println("---- OK\n") ; + + } catch (Exception e) { + Utils.printThrowable(e, true) ; + errorCount++ ; + System.out.println("---- ERROR\n") ; + } + + return errorCount ; + } + + + private final int doThreadMXBeanTest(MBeanServerConnection mbsc) { + int errorCount = 0 ; + System.out.println("---- ThreadMXBean") ; + + try { + ObjectName threadName = + new ObjectName(ManagementFactory.THREAD_MXBEAN_NAME) ; + MBeanInfo mbInfo = mbsc.getMBeanInfo(threadName); + errorCount += checkNonEmpty(mbInfo); + System.out.println("getMBeanInfo\t\t" + mbInfo); + ThreadMXBean thread = null ; + + thread = + JMX.newMXBeanProxy(mbsc, + threadName, + ThreadMXBean.class) ; + System.out.println("findMonitorDeadlockedThreads\t\t" + + thread.findMonitorDeadlockedThreads()); + long[] threadIDs = thread.getAllThreadIds() ; + System.out.println("getAllThreadIds\t\t" + + threadIDs); + + for ( long threadID : threadIDs ) { + System.out.println("getThreadInfo long\t\t" + + thread.getThreadInfo(threadID)); + System.out.println("getThreadInfo long, int\t\t" + + thread.getThreadInfo(threadID, 2)); + } + + System.out.println("getThreadInfo long[]\t\t" + + thread.getThreadInfo(threadIDs)); + System.out.println("getThreadInfo long[], int\t\t" + + thread.getThreadInfo(threadIDs, 2)); + System.out.println("getDaemonThreadCount\t\t" + + thread.getDaemonThreadCount()); + System.out.println("getPeakThreadCount\t\t" + + thread.getPeakThreadCount()); + System.out.println("getThreadCount\t\t" + + thread.getThreadCount()); + System.out.println("getTotalStartedThreadCount\t\t" + + thread.getTotalStartedThreadCount()); + boolean supported = thread.isThreadContentionMonitoringSupported() ; + System.out.println("isThreadContentionMonitoringSupported\t\t" + + supported); + + if ( supported ) { + System.out.println("isThreadContentionMonitoringEnabled\t\t" + + thread.isThreadContentionMonitoringEnabled()); + } + + supported = thread.isThreadCpuTimeSupported() ; + System.out.println("isThreadCpuTimeSupported\t\t" + + supported); + + if ( supported ) { + System.out.println("isThreadCpuTimeEnabled\t\t" + + thread.isThreadCpuTimeEnabled()); + + for (long id : threadIDs) { + System.out.println("getThreadCpuTime(" + id + ")\t\t" + + thread.getThreadCpuTime(id)); + System.out.println("getThreadUserTime(" + id + ")\t\t" + + thread.getThreadUserTime(id)); + } + } + + supported = thread.isCurrentThreadCpuTimeSupported() ; + System.out.println("isCurrentThreadCpuTimeSupported\t\t" + + supported); + + if ( supported ) { + System.out.println("getCurrentThreadCpuTime\t\t" + + thread.getCurrentThreadCpuTime()); + System.out.println("getCurrentThreadUserTime\t\t" + + thread.getCurrentThreadUserTime()); + } + + thread.resetPeakThreadCount() ; + + System.out.println("---- OK\n") ; + } catch (Exception e) { + Utils.printThrowable(e, true) ; + errorCount++ ; + System.out.println("---- ERROR\n") ; + } + + return errorCount ; + } + + + private final int doRuntimeMXBeanTest(MBeanServerConnection mbsc) { + int errorCount = 0 ; + System.out.println("---- RuntimeMXBean") ; + + try { + ObjectName runtimeName = + new ObjectName(ManagementFactory.RUNTIME_MXBEAN_NAME) ; + MBeanInfo mbInfo = mbsc.getMBeanInfo(runtimeName); + errorCount += checkNonEmpty(mbInfo); + System.out.println("getMBeanInfo\t\t" + mbInfo); + RuntimeMXBean runtime = null; + + runtime = + JMX.newMXBeanProxy(mbsc, + runtimeName, + RuntimeMXBean.class) ; + System.out.println("getClassPath\t\t" + + runtime.getClassPath()); + System.out.println("getInputArguments\t\t" + + runtime.getInputArguments()); + System.out.println("getLibraryPath\t\t" + + runtime.getLibraryPath()); + System.out.println("getManagementSpecVersion\t\t" + + runtime.getManagementSpecVersion()); + System.out.println("getName\t\t" + + runtime.getName()); + System.out.println("getSpecName\t\t" + + runtime.getSpecName()); + System.out.println("getSpecVendor\t\t" + + runtime.getSpecVendor()); + System.out.println("getSpecVersion\t\t" + + runtime.getSpecVersion()); + System.out.println("getStartTime\t\t" + + runtime.getStartTime()); + System.out.println("getSystemProperties\t\t" + + runtime.getSystemProperties()); + System.out.println("getUptime\t\t" + + runtime.getUptime()); + System.out.println("getVmName\t\t" + + runtime.getVmName()); + System.out.println("getVmVendor\t\t" + + runtime.getVmVendor()); + System.out.println("getVmVersion\t\t" + + runtime.getVmVersion()); + boolean supported = runtime.isBootClassPathSupported() ; + System.out.println("isBootClassPathSupported\t\t" + + supported); + + if ( supported ) { + System.out.println("getBootClassPath\t\t" + + runtime.getBootClassPath()); + } + + System.out.println("---- OK\n") ; + } catch (Exception e) { + Utils.printThrowable(e, true) ; + errorCount++ ; + System.out.println("---- ERROR\n") ; + } + + return errorCount ; + } + + + private final int doOperatingSystemMXBeanTest(MBeanServerConnection mbsc) { + int errorCount = 0 ; + System.out.println("---- OperatingSystemMXBean") ; + + try { + ObjectName operationName = + new ObjectName(ManagementFactory.OPERATING_SYSTEM_MXBEAN_NAME) ; + MBeanInfo mbInfo = mbsc.getMBeanInfo(operationName); + errorCount += checkNonEmpty(mbInfo); + System.out.println("getMBeanInfo\t\t" + mbInfo); + OperatingSystemMXBean operation = null ; + + operation = + JMX.newMXBeanProxy(mbsc, + operationName, + OperatingSystemMXBean.class) ; + System.out.println("getArch\t\t" + + operation.getArch()); + System.out.println("getAvailableProcessors\t\t" + + operation.getAvailableProcessors()); + System.out.println("getName\t\t" + + operation.getName()); + System.out.println("getVersion\t\t" + + operation.getVersion()); + + System.out.println("---- OK\n") ; + } catch (Exception e) { + Utils.printThrowable(e, true) ; + errorCount++ ; + System.out.println("---- ERROR\n") ; + } + + return errorCount ; + } + + + private final int doCompilationMXBeanTest(MBeanServerConnection mbsc) { + int errorCount = 0 ; + System.out.println("---- CompilationMXBean") ; + + try { + ObjectName compilationName = + new ObjectName(ManagementFactory.COMPILATION_MXBEAN_NAME); + + if ( mbsc.isRegistered(compilationName) ) { + MBeanInfo mbInfo = mbsc.getMBeanInfo(compilationName); + errorCount += checkNonEmpty(mbInfo); + System.out.println("getMBeanInfo\t\t" + mbInfo); + CompilationMXBean compilation = null ; + + compilation = + JMX.newMXBeanProxy(mbsc, + compilationName, + CompilationMXBean.class) ; + System.out.println("getName\t\t" + + compilation.getName()); + boolean supported = + compilation.isCompilationTimeMonitoringSupported() ; + System.out.println("isCompilationTimeMonitoringSupported\t\t" + + supported); + + if ( supported ) { + System.out.println("getTotalCompilationTime\t\t" + + compilation.getTotalCompilationTime()); + } + } + + System.out.println("---- OK\n") ; + } catch (Exception e) { + Utils.printThrowable(e, true) ; + errorCount++ ; + System.out.println("---- ERROR\n") ; + } + + return errorCount ; + } + + + private final int doGarbageCollectorMXBeanTest(MBeanServerConnection mbsc) { + int errorCount = 0 ; + System.out.println("---- GarbageCollectorMXBean") ; + + try { + ObjectName filterName = + new ObjectName(ManagementFactory.GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE + + ",*"); + Set onSet = mbsc.queryNames(filterName, null); + + for (Iterator iter = onSet.iterator(); iter.hasNext(); ) { + ObjectName garbageName = iter.next() ; + System.out.println("-------- " + garbageName) ; + MBeanInfo mbInfo = mbsc.getMBeanInfo(garbageName); + errorCount += checkNonEmpty(mbInfo); + System.out.println("getMBeanInfo\t\t" + mbInfo); + GarbageCollectorMXBean garbage = null ; + + garbage = + JMX.newMXBeanProxy(mbsc, + garbageName, + GarbageCollectorMXBean.class) ; + System.out.println("getCollectionCount\t\t" + + garbage.getCollectionCount()); + System.out.println("getCollectionTime\t\t" + + garbage.getCollectionTime()); + } + + System.out.println("---- OK\n") ; + } catch (Exception e) { + Utils.printThrowable(e, true) ; + errorCount++ ; + System.out.println("---- ERROR\n") ; + } + + return errorCount ; + } + + + private final int doMemoryManagerMXBeanTest(MBeanServerConnection mbsc) { + int errorCount = 0 ; + System.out.println("---- MemoryManagerMXBean") ; + + try { + ObjectName filterName = + new ObjectName(ManagementFactory.MEMORY_MANAGER_MXBEAN_DOMAIN_TYPE + + ",*"); + Set onSet = mbsc.queryNames(filterName, null); + + for (Iterator iter = onSet.iterator(); iter.hasNext(); ) { + ObjectName memoryManagerName = iter.next() ; + System.out.println("-------- " + memoryManagerName) ; + MBeanInfo mbInfo = mbsc.getMBeanInfo(memoryManagerName); + System.out.println("getMBeanInfo\t\t" + mbInfo); + errorCount += checkNonEmpty(mbInfo); + MemoryManagerMXBean memoryManager = null; + + memoryManager = + JMX.newMXBeanProxy(mbsc, + memoryManagerName, + MemoryManagerMXBean.class) ; + System.out.println("getMemoryPoolNames\t\t" + + Arrays.deepToString(memoryManager.getMemoryPoolNames())); + System.out.println("getName\t\t" + + memoryManager.getName()); + System.out.println("isValid\t\t" + + memoryManager.isValid()); + } + + System.out.println("---- OK\n") ; + } catch (Exception e) { + Utils.printThrowable(e, true) ; + errorCount++ ; + System.out.println("---- ERROR\n") ; + } + + return errorCount ; + } + + + private final int doMemoryPoolMXBeanTest(MBeanServerConnection mbsc) { + int errorCount = 0 ; + System.out.println("---- MemoryPoolMXBean") ; + + try { + ObjectName filterName = + new ObjectName(ManagementFactory.MEMORY_POOL_MXBEAN_DOMAIN_TYPE + + ",*"); + Set onSet = mbsc.queryNames(filterName, null); + + for (Iterator iter = onSet.iterator(); iter.hasNext(); ) { + ObjectName memoryPoolName = iter.next() ; + System.out.println("-------- " + memoryPoolName) ; + MBeanInfo mbInfo = mbsc.getMBeanInfo(memoryPoolName); + errorCount += checkNonEmpty(mbInfo); + System.out.println("getMBeanInfo\t\t" + mbInfo); + MemoryPoolMXBean memoryPool = null; + + memoryPool = + JMX.newMXBeanProxy(mbsc, + memoryPoolName, + MemoryPoolMXBean.class, + true) ; + System.out.println("getCollectionUsage\t\t" + + memoryPool.getCollectionUsage()); + System.out.println("getMemoryManagerNames\t\t" + + Arrays.deepToString(memoryPool.getMemoryManagerNames())); + System.out.println("getName\t\t" + + memoryPool.getName()); + System.out.println("getPeakUsage\t\t" + + memoryPool.getPeakUsage()); + System.out.println("getType\t\t" + + memoryPool.getType()); + System.out.println("getUsage\t\t" + + memoryPool.getUsage()); + System.out.println("isValid\t\t" + + memoryPool.isValid()); + boolean supported = memoryPool.isUsageThresholdSupported() ; + System.out.println("isUsageThresholdSupported\t\t" + + supported); + + if ( supported ) { + System.out.println("getUsageThreshold\t\t" + + memoryPool.getUsageThreshold()); + System.out.println("isUsageThresholdExceeded\t\t" + + memoryPool.isUsageThresholdExceeded()); + System.out.println("getUsageThresholdCount\t\t" + + memoryPool.getUsageThresholdCount()); + } + + supported = memoryPool.isCollectionUsageThresholdSupported() ; + System.out.println("isCollectionUsageThresholdSupported\t\t" + + supported); + + if ( supported ) { + System.out.println("getCollectionUsageThreshold\t\t" + + memoryPool.getCollectionUsageThreshold()); + System.out.println("getCollectionUsageThresholdCount\t\t" + + memoryPool.getCollectionUsageThresholdCount()); + System.out.println("isCollectionUsageThresholdExceeded\t\t" + + memoryPool.isCollectionUsageThresholdExceeded()); + } + + memoryPool.resetPeakUsage(); + } + + System.out.println("---- OK\n") ; + } catch (Exception e) { + Utils.printThrowable(e, true) ; + errorCount++ ; + System.out.println("---- ERROR\n") ; + } + + return errorCount ; + } + + + private int checkNonEmpty(MBeanInfo mbi) { + if ( mbi.toString().length() == 0 ) { + System.out.println("(ERROR) MBeanInfo is empty !"); + return 1; + } else { + return 0; + } + } + +} diff --git a/jdk/test/javax/management/mxbean/MXBeanInteropTest2.java b/jdk/test/javax/management/mxbean/MXBeanInteropTest2.java new file mode 100644 index 00000000000..4c713574100 --- /dev/null +++ b/jdk/test/javax/management/mxbean/MXBeanInteropTest2.java @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8058865 + * @summary Checks access to test MXBean + * @author Olivier Lagneau + * @modules java.management + * @library /lib/testlibrary + * @compile Basic.java + * @run main/othervm/timeout=300 -DDEBUG_STANDARD MXBeanInteropTest2 + */ + +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import javax.management.Attribute; +import javax.management.JMX; +import javax.management.MBeanAttributeInfo; +import javax.management.MBeanConstructorInfo; +import javax.management.MBeanServer; +import java.lang.management.ManagementFactory; +import javax.management.MBeanInfo; +import javax.management.MBeanNotificationInfo; +import javax.management.MBeanOperationInfo; +import javax.management.MBeanServerConnection; +import javax.management.ObjectName; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; + +public class MXBeanInteropTest2 { + + private static String BASIC_MXBEAN_CLASS_NAME = "Basic"; + + /* + * First Debug properties and arguments are collect in expected + * map (argName, value) format, then calls original test's run method. + */ + public static void main(String args[]) throws Exception { + + System.out.println("================================================="); + + // Parses parameters + Utils.parseDebugProperties(); + Map map = Utils.parseParameters(args) ; + + // Run test + MXBeanInteropTest2 test = new MXBeanInteropTest2(); + test.run(map); + + } + + public void run(Map args) { + + System.out.println("MXBeanInteropTest2::run: Start") ; + int errorCount = 0 ; + + try { + // JMX MbeanServer used inside single VM as if remote. + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + + JMXServiceURL url = new JMXServiceURL("rmi", null, 0); + JMXConnectorServer cs = + JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs); + cs.start(); + + JMXServiceURL addr = cs.getAddress(); + JMXConnector cc = JMXConnectorFactory.connect(addr); + MBeanServerConnection mbsc = cc.getMBeanServerConnection(); + + // Prints all MBeans whatever the domain is. + printMBeans(mbsc) ; + + // Call test body + errorCount += doBasicMXBeanTest(mbsc) ; + + // Terminate the JMX Client + cc.close(); + + } catch(Exception e) { + Utils.printThrowable(e, true) ; + throw new RuntimeException(e); + } + + if ( errorCount == 0 ) { + System.out.println("MXBeanInteropTest2::run: Done without any error") ; + } else { + System.out.println("MXBeanInteropTest2::run: Done with " + + errorCount + + " error(s)") ; + throw new RuntimeException("errorCount = " + errorCount); + } + } + + + /** + * Prints all MBeans whatever the domain is. + */ + private static void printMBeans(MBeanServerConnection mbsc) throws Exception { + Set set = mbsc.queryNames(null, null); + System.out.println("---- MBeans found :"); + + for (Iterator iter = set.iterator(); iter.hasNext(); ) { + System.out.println(iter.next().toString()); + } + + System.out.println("\n") ; + } + + + private final int doBasicMXBeanTest(MBeanServerConnection mbsc) { + int errorCount = 0 ; + System.out.println("---- doBasicMXBeanTest") ; + + try { + ObjectName objName = + new ObjectName("sqe:type=BasicMXBean") ; + mbsc.createMBean(BASIC_MXBEAN_CLASS_NAME, objName); + MBeanInfo mbInfo = mbsc.getMBeanInfo(objName); + printMBeanInfo(mbInfo); + System.out.println("---- OK\n") ; + System.out.println("getMBeanInfo\t\t" + + mbInfo); + System.out.println("---- OK\n") ; + + System.out.println("Check mxbean field in the MBeanInfo"); + String mxbeanField = + (String)mbInfo.getDescriptor().getFieldValue(JMX.MXBEAN_FIELD); + + if ( mxbeanField == null || ! mxbeanField.equals("true")) { + System.out.println("---- ERROR : Improper mxbean field value " + + mxbeanField); + errorCount++; + } + System.out.println("---- OK\n") ; + + System.out.println("Set attribute ObjectNameAtt"); + Attribute att = new Attribute("ObjectNameAtt", objName); + mbsc.setAttribute(objName, att); + ObjectName value = + (ObjectName)mbsc.getAttribute(objName, "ObjectNameAtt"); + + if ( ! value.equals(objName) ) { + errorCount++; + System.out.println("---- ERROR : setAttribute failed, got " + + value + + " while expecting " + + objName); + } + System.out.println("---- OK\n") ; + + System.out.println("Call operation doNothing"); + mbsc.invoke(objName, "doNothing", null, null); + System.out.println("---- OK\n") ; + + System.out.println("Call operation getWeather"); + Object weather = mbsc.invoke(objName, + "getWeather", + new Object[]{Boolean.TRUE}, + new String[]{"boolean"}); + System.out.println("Weather is " + weather); + System.out.println("---- OK\n") ; + } catch (Exception e) { + Utils.printThrowable(e, true) ; + errorCount++ ; + System.out.println("---- ERROR\n") ; + } + + return errorCount ; + } + + private void printMBeanInfo(MBeanInfo mbInfo) { + System.out.println("Description " + mbInfo.getDescription()); + + for (MBeanConstructorInfo ctor : mbInfo.getConstructors()) { + System.out.println("Constructor " + ctor.getName()); + } + + for (MBeanAttributeInfo att : mbInfo.getAttributes()) { + System.out.println("Attribute " + att.getName() + + " [" + att.getType() + "]"); + } + + for (MBeanOperationInfo oper : mbInfo.getOperations()) { + System.out.println("Operation " + oper.getName()); + } + + for (MBeanNotificationInfo notif : mbInfo.getNotifications()) { + System.out.println("Notification " + notif.getName()); + } + } +} diff --git a/jdk/test/javax/management/mxbean/MXBeanLoadingTest1.java b/jdk/test/javax/management/mxbean/MXBeanLoadingTest1.java new file mode 100644 index 00000000000..699260777d1 --- /dev/null +++ b/jdk/test/javax/management/mxbean/MXBeanLoadingTest1.java @@ -0,0 +1,329 @@ +/* + * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8058865 + * @summary Checks correct collection of MXBean's class after unregistration + * @author Olivier Lagneau + * @modules java.management + * @library /lib/testlibrary + * @run main/othervm/timeout=300 MXBeanLoadingTest1 + */ + +import java.lang.ref.WeakReference; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Arrays; +import java.util.Map; +import javax.management.Attribute; +import javax.management.JMX; +import javax.management.MBeanAttributeInfo; +import javax.management.MBeanInfo; +import javax.management.MBeanOperationInfo; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.MXBean; +import javax.management.ObjectName; +import javax.management.loading.PrivateMLet; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.SimpleType; + +public class MXBeanLoadingTest1 { + + public static void main(String[] args) throws Exception { + MXBeanLoadingTest1 test = new MXBeanLoadingTest1(); + test.run((Map)null); + } + + + public void run(Map args) { + + System.out.println("MXBeanLoadingTest1::run: Start") ; + + try { + System.out.println("We ensure no reference is retained on MXBean class" + + " after it is unregistered. We take time to perform" + + " some little extra check of Descriptors, MBean*Info."); + + ClassLoader myClassLoader = MXBeanLoadingTest1.class.getClassLoader(); + + if (!(myClassLoader instanceof URLClassLoader)) { + String message = "(ERROR) Test's class loader is not " + + "a URLClassLoader"; + System.out.println(message); + throw new RuntimeException(message); + } + + URLClassLoader myURLClassLoader = (URLClassLoader) myClassLoader; + URL[] urls = myURLClassLoader.getURLs(); + PrivateMLet mlet = new PrivateMLet(urls, null, false); + Class shadowClass = mlet.loadClass(TestMXBean.class.getName()); + + if (shadowClass == TestMXBean.class) { + String message = "(ERROR) MLet got original TestMXBean, not shadow"; + System.out.println(message); + throw new RuntimeException(message); + } + shadowClass = null; + + MBeanServer mbs = MBeanServerFactory.createMBeanServer(); + ObjectName mletName = new ObjectName("x:type=mlet"); + mbs.registerMBean(mlet, mletName); + + ObjectName testName = new ObjectName("x:type=test"); + mbs.createMBean(Test.class.getName(), testName, mletName); + + // That test fails because the MXBean instance is accessed via + // a delegate OpenMBean which has + ClassLoader testLoader = mbs.getClassLoaderFor(testName); + + if (testLoader != mlet) { + System.out.println("MLet " + mlet); + String message = "(ERROR) MXBean's class loader is not MLet: " + + testLoader; + System.out.println(message); + throw new RuntimeException(message); + } + testLoader = null; + + + // Cycle get/set/get of the attribute of type Luis. + // We check the set is effective. + CompositeData cd_B = (CompositeData)mbs.getAttribute(testName, "B"); + CompositeType compType_B = cd_B.getCompositeType(); + + CompositeDataSupport cds_B = + new CompositeDataSupport(compType_B, + new String[]{"something"}, + new Object[]{Integer.valueOf(13)}); + Attribute myAtt = new Attribute("B", cds_B); + mbs.setAttribute(testName, myAtt); + + CompositeData cd_B2 = (CompositeData)mbs.getAttribute(testName, "B"); + + if ( ((Integer)cd_B2.get("something")).intValue() != 13 ) { + String message = "(ERROR) The setAttribute of att B did not work;" + + " expect Luis.something = 13 but got " + + cd_B2.get("something"); + System.out.println(message); + throw new RuntimeException(message); + } + + MBeanInfo info = mbs.getMBeanInfo(testName); + String mxbeanField = + (String)info.getDescriptor().getFieldValue(JMX.MXBEAN_FIELD); + + if ( mxbeanField == null || ! mxbeanField.equals("true")) { + String message = "(ERROR) Improper mxbean field value " + + mxbeanField; + System.out.println(message); + throw new RuntimeException(message); + } + + // Check the 2 attributes. + MBeanAttributeInfo[] attrs = info.getAttributes(); + + if ( attrs.length == 2 ) { + for (MBeanAttributeInfo mbai : attrs) { + String originalTypeFieldValue = + (String)mbai.getDescriptor().getFieldValue(JMX.ORIGINAL_TYPE_FIELD); + OpenType openTypeFieldValue = + (OpenType)mbai.getDescriptor().getFieldValue(JMX.OPEN_TYPE_FIELD); + + if ( mbai.getName().equals("A") ) { + if ( !mbai.isReadable() || !mbai.isWritable() + || mbai.isIs() + || !mbai.getType().equals("int") ) { + String message = "(ERROR) Unexpected MBeanAttributeInfo for A " + + mbai; + System.out.println(message); + throw new RuntimeException(message); + } + + if ( ! originalTypeFieldValue.equals("int") ) { + String message = "(ERROR) Unexpected originalType in Descriptor for A " + + originalTypeFieldValue; + System.out.println(message); + throw new RuntimeException(message); + } + + if ( ! openTypeFieldValue.equals(SimpleType.INTEGER) ) { + String message = "(ERROR) Unexpected openType in Descriptor for A " + + originalTypeFieldValue; + System.out.println(message); + throw new RuntimeException(message); + } + } else if ( mbai.getName().equals("B") ) { + if ( !mbai.isReadable() || !mbai.isWritable() + || mbai.isIs() + || !mbai.getType().equals("javax.management.openmbean.CompositeData") ) { + String message = "(ERROR) Unexpected MBeanAttributeInfo for B " + + mbai; + System.out.println(message); + throw new RuntimeException(message); + } + + if ( ! originalTypeFieldValue.equals(Luis.class.getName()) ) { + String message = "(ERROR) Unexpected originalType in Descriptor for B " + + originalTypeFieldValue; + System.out.println(message); + throw new RuntimeException(message); + } + + if ( ! openTypeFieldValue.equals(compType_B) ) { + String message = "(ERROR) Unexpected openType in Descriptor for B " + + compType_B; + System.out.println(message); + throw new RuntimeException(message); + } + } else { + String message = "(ERROR) Unknown attribute name"; + System.out.println(message); + throw new RuntimeException(message); + } + } + } else { + String message = "(ERROR) Unexpected MBeanAttributeInfo array" + + Arrays.deepToString(attrs); + System.out.println(message); + throw new RuntimeException(message); + } + + // Check the MXBean operation. + MBeanOperationInfo[] ops = info.getOperations(); + // The impact is ACTION_INFO as for a standard MBean it is UNKNOWN, + // logged 6320104. + if (ops.length != 1 || !ops[0].getName().equals("bogus") + || ops[0].getSignature().length > 0 + || !ops[0].getReturnType().equals("void")) { + String message = "(ERROR) Unexpected MBeanOperationInfo array " + + Arrays.deepToString(ops); + System.out.println(message); + throw new RuntimeException(message); + } + + String originalTypeFieldValue = + (String)ops[0].getDescriptor().getFieldValue(JMX.ORIGINAL_TYPE_FIELD); + OpenType openTypeFieldValue = + (OpenType)ops[0].getDescriptor().getFieldValue(JMX.OPEN_TYPE_FIELD); + + if ( ! originalTypeFieldValue.equals("void") ) { + String message = "(ERROR) Unexpected originalType in Descriptor for bogus " + + originalTypeFieldValue; + System.out.println(message); + throw new RuntimeException(message); + } + + if ( ! openTypeFieldValue.equals(SimpleType.VOID) ) { + String message = "(ERROR) Unexpected openType in Descriptor for bogus " + + originalTypeFieldValue; + System.out.println(message); + throw new RuntimeException(message); + } + + // Check there is 2 constructors. + if (info.getConstructors().length != 2) { + String message = "(ERROR) Wrong number of constructors " + + "in introspected bean: " + + Arrays.asList(info.getConstructors()); + System.out.println(message); + throw new RuntimeException(message); + } + + // Check MXBean class name. + if (!info.getClassName().endsWith("Test")) { + String message = "(ERROR) Wrong info class name: " + + info.getClassName(); + System.out.println(message); + throw new RuntimeException(message); + } + + mbs.unregisterMBean(testName); + mbs.unregisterMBean(mletName); + + WeakReference mletRef = + new WeakReference(mlet); + mlet = null; + + System.out.println("MXBean registered and unregistered, waiting for " + + "garbage collector to collect class loader"); + + for (int i = 0; i < 10000 && mletRef.get() != null; i++) { + System.gc(); + Thread.sleep(1); + } + + if (mletRef.get() == null) + System.out.println("(OK) class loader was GC'd"); + else { + String message = "(ERROR) Class loader was not GC'd"; + System.out.println(message); + throw new RuntimeException(message); + } + } catch(Exception e) { + Utils.printThrowable(e, true) ; + throw new RuntimeException(e); + } + + System.out.println("MXBeanLoadingTest1::run: Done without any error") ; + } + + + // I agree the use of the MXBean annotation and the MXBean suffix for the + // interface name are redundant but however harmless. + // + @MXBean(true) + public static interface TestMXBean { + public void bogus(); + public int getA(); + public void setA(int a); + public Luis getB(); + public void setB(Luis mi); + } + + + public static class Test implements TestMXBean { + private Luis luis = new Luis() ; + public Test() {} + public Test(int x) {} + + public void bogus() {} + public int getA() {return 0;} + public void setA(int a) {} + public Luis getB() {return this.luis;} + public void setB(Luis luis) {this.luis = luis;} + } + + + public static class Luis { + private int something = 0; + public Luis() {} + public int getSomething() {return something;} + public void setSomething(int v) {something = v;} + public void doNothing() {} + } +} diff --git a/jdk/test/javax/management/mxbean/MXBeanNotifTest.java b/jdk/test/javax/management/mxbean/MXBeanNotifTest.java new file mode 100644 index 00000000000..021731e4fa7 --- /dev/null +++ b/jdk/test/javax/management/mxbean/MXBeanNotifTest.java @@ -0,0 +1,385 @@ +/* + * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8058865 + * @summary Checks MXBean proper registration both as its implementation class and interface + * @author Olivier Lagneau + * @modules java.management + * @library /lib/testlibrary + * @compile Basic.java + * @run main/othervm/timeout=300 -DDEBUG_STANDARD MXBeanNotifTest -numOfNotifications 239 -timeForNotificationInSeconds 4 + */ + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; + +import java.lang.management.ManagementFactory; + +import javax.management.Attribute; +import javax.management.Descriptor; +import javax.management.ImmutableDescriptor; +import javax.management.MBeanServer; +import javax.management.MBeanInfo; +import javax.management.MBeanNotificationInfo; +import javax.management.Notification; +import javax.management.NotificationListener; +import javax.management.MBeanServerConnection; +import javax.management.ObjectName; + +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; + +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.SimpleType; +import javax.management.openmbean.TabularData; +import javax.management.openmbean.TabularDataSupport; +import javax.management.openmbean.TabularType; + +public class MXBeanNotifTest implements NotificationListener { + + private static String BASIC_MXBEAN_CLASS_NAME = "Basic"; + private static String BASIC_MXBEAN_INTERFACE_NAME = "BasicMXBean"; + + private long timeForNotificationInSeconds = 3L; + private int numOfNotifications = 1; + private BlockingQueue notifList = null; + private int numOfNotifDescriptorElements = 13; + + /* + * First Debug properties and arguments are collect in expected + * map (argName, value) format, then calls original test's run method. + */ + public static void main(String args[]) throws Exception { + + System.out.println("================================================="); + + // Parses parameters + Utils.parseDebugProperties(); + Map map = Utils.parseParameters(args) ; + + // Run test + MXBeanNotifTest test = new MXBeanNotifTest(); + test.run(map); + + } + + protected void parseArgs(Map args) throws Exception { + + String arg = null; + + // Init numOfNotifications + // It is the number of notifications we should trigger and check. + arg = (String)args.get("-numOfNotifications") ; + if (arg != null) { + numOfNotifications = (new Integer(arg)).intValue(); + } + + // Init timeForNotificationInSeconds + // It is the maximum time in seconds we wait for each notification. + arg = (String)args.get("-timeForEachNotificationInSeconds") ; + if (arg != null) { + timeForNotificationInSeconds = (new Long(arg)).longValue(); + } + + } + + public void run(Map args) { + + System.out.println("MXBeanNotifTest::run: Start") ; + int errorCount = 0 ; + + try { + parseArgs(args); + notifList = new ArrayBlockingQueue(numOfNotifications); + + // JMX MbeanServer used inside single VM as if remote. + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + + JMXServiceURL url = new JMXServiceURL("rmi", null, 0); + JMXConnectorServer cs = + JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs); + cs.start(); + + JMXServiceURL addr = cs.getAddress(); + JMXConnector cc = JMXConnectorFactory.connect(addr); + MBeanServerConnection mbsc = cc.getMBeanServerConnection(); + + // ---- + System.out.println("MXBeanNotifTest::run: Create and register the MBean"); + ObjectName objName = new ObjectName("sqe:type=Basic,protocol=rmi") ; + mbsc.createMBean(BASIC_MXBEAN_CLASS_NAME, objName); + System.out.println("---- OK\n") ; + + // ---- + System.out.println("MXBeanNotifTest::run: Add me as notification listener"); + mbsc.addNotificationListener(objName, this, null, null); + + // ---- + System.out.println("MXBeanNotifTest::run: Retrieve the Descriptor" + + " that should be in MBeanNotificationInfo"); + TabularData tabData = + (TabularData)mbsc.getAttribute(objName, "NotifDescriptorAsMapAtt"); + Map descrMap = new HashMap<>(); + + for (Iterator it = tabData.values().iterator(); it.hasNext(); ) { + CompositeData compData = (CompositeData)it.next(); + descrMap.put((String)compData.get("key"), + (String)compData.get("value")); + } + + Descriptor refNotifDescriptor = new ImmutableDescriptor(descrMap); + System.out.println("---- OK\n") ; + + // ---- + // Because the MBean holding the targeted attribute is MXBean, we + // should use for the setAttribute a converted form for the + // attribute value as described by the MXBean mapping rules. + // This explains all that lovely stuff for creating a + // TabularDataSupport. + // + // WARNING : the MBeanInfo of the MXBean used on opposite side + // is computed when the MBean is registered. + // It means the Descriptor considered for the MBeanNotificationInfo + // is not the one we set in the lines below, it is too late. + // However, we check that set is harmless when we check + // the MBeanNotificationInfo. + // + System.out.println("MXBeanNotifTest::run: Set a Map" + + " attribute"); + String typeName = + "java.util.Map"; + String[] keyValue = new String[] {"key", "value"}; + OpenType[] openTypes = + new OpenType[] {SimpleType.STRING, SimpleType.STRING}; + CompositeType rowType = new CompositeType(typeName, typeName, + keyValue, keyValue, openTypes); + TabularType tabType = new TabularType(typeName, typeName, + rowType, new String[]{"key"}); + TabularDataSupport convertedDescrMap = + new TabularDataSupport(tabType); + + for (int i = 0; i < numOfNotifDescriptorElements; i++) { + Object[] descrValue = {"field" + i, "value" + i}; + CompositeData data = + new CompositeDataSupport(rowType, keyValue, descrValue); + convertedDescrMap.put(data); + } + + Attribute descrAtt = + new Attribute("NotifDescriptorAsMapAtt", convertedDescrMap); + mbsc.setAttribute(objName, descrAtt); + System.out.println("---- OK\n") ; + + // ---- + System.out.println("MXBeanNotifTest::run: Compare the Descriptor from" + + " the MBeanNotificationInfo against a reference"); + MBeanInfo mbInfo = mbsc.getMBeanInfo(objName); + errorCount += checkMBeanInfo(mbInfo, refNotifDescriptor); + System.out.println("---- DONE\n") ; + + // ---- + System.out.println("Check isInstanceOf(Basic)"); + + if ( ! mbsc.isInstanceOf(objName, BASIC_MXBEAN_CLASS_NAME) ) { + errorCount++; + System.out.println("---- ERROR isInstanceOf returned false\n") ; + } else { + System.out.println("---- OK\n") ; + } + + // ---- + System.out.println("Check isInstanceOf(BasicMXBean)"); + + if ( ! mbsc.isInstanceOf(objName, BASIC_MXBEAN_INTERFACE_NAME) ) { + errorCount++; + System.out.println("---- ERROR isInstanceOf returned false\n") ; + } else { + System.out.println("---- OK\n") ; + } + + // ---- + System.out.println("MXBeanNotifTest::run: Ask for " + + numOfNotifications + " notification(s)"); + Object[] sendNotifParam = new Object[1]; + String[] sendNotifSig = new String[]{"java.lang.String"}; + + for (int i = 0; i < numOfNotifications; i++) { + // Select which type of notification we ask for + if ( i % 2 == 0 ) { + sendNotifParam[0] = Basic.NOTIF_TYPE_0; + } else { + sendNotifParam[0] = Basic.NOTIF_TYPE_1; + } + + // Trigger notification emission + mbsc.invoke(objName, + "sendNotification", + sendNotifParam, + sendNotifSig); + + // Wait for it then check it when it comes early enough + Notification notif = + notifList.poll(timeForNotificationInSeconds, + TimeUnit.SECONDS) ; + // The very first notification is likely to come in slower than + // all the others. Because that test isn't targeting the speed + // notifications are delivered with, we prefer to secure it. + if (i == 0 && notif == null) { + System.out.println("MXBeanNotifTest::run: Wait extra " + + timeForNotificationInSeconds + " second(s) the " + + " very first notification"); + notif = notifList.poll(timeForNotificationInSeconds, + TimeUnit.SECONDS); + } + + if ( notif == null ) { + errorCount++; + System.out.println("---- ERROR No notification received" + + " within allocated " + timeForNotificationInSeconds + + " second(s) !"); + } else { + errorCount += + checkNotification(notif, + (String)sendNotifParam[0], + Basic.NOTIFICATION_MESSAGE, + objName); + } + } + + int toc = 0; + while ( notifList.size() < 2 && toc < 10 ) { + Thread.sleep(499); + toc++; + } + System.out.println("---- DONE\n") ; + } catch(Exception e) { + Utils.printThrowable(e, true) ; + throw new RuntimeException(e); + } + + if ( errorCount == 0 ) { + System.out.println("MXBeanNotifTest::run: Done without any error") ; + } else { + System.out.println("MXBeanNotifTest::run: Done with " + + errorCount + + " error(s)") ; + throw new RuntimeException("errorCount = " + errorCount); + } + } + + + private int checkMBeanInfo(MBeanInfo mbi, Descriptor refDescr) { + MBeanNotificationInfo[] notifsInfo = mbi.getNotifications(); + int res = 0; + + for (MBeanNotificationInfo mbni : notifsInfo) { + if ( mbni.getDescriptor().equals(refDescr) ) { + System.out.println("(OK)"); + } else { + System.out.println("(ERROR) Descriptor of the notification is " + + mbni.getDescriptor() + + " as we expect " + + refDescr); + res++; + } + } + + return res; + } + + + private int checkNotification(Notification notif, + String refType, + String refMessage, + ObjectName refSource) { + int res = 0; + + Utils.debug(Utils.DEBUG_VERBOSE, + "\t getSource " + notif.getSource()); + Utils.debug(Utils.DEBUG_VERBOSE, + "\t getMessage " + notif.getMessage()); + Utils.debug(Utils.DEBUG_VERBOSE, + "\t getSequenceNumber " + notif.getSequenceNumber()); + Utils.debug(Utils.DEBUG_VERBOSE, + "\t getTimeStamp " + notif.getTimeStamp()); + Utils.debug(Utils.DEBUG_VERBOSE, + "\t getType " + notif.getType()); + Utils.debug(Utils.DEBUG_VERBOSE, + "\t getUserData " + notif.getUserData()); + + if ( ! notif.getType().equals(refType) ) { + res++; + System.out.println("(ERROR) Type is not " + + refType + " in notification\n" + notif); + } else { + if ( notif.getType().equals(Basic.NOTIF_TYPE_0) + && ! (notif instanceof javax.management.Notification) ) { + res++; + System.out.println("(ERROR) Notification is not instance of " + + " javax.management.Notification but rather " + + notif.getClass().getName()); + } else if ( notif.getType().equals(Basic.NOTIF_TYPE_1) + && ! (notif instanceof SqeNotification) ) { + res++; + System.out.println("(ERROR) Notification is not instance of " + + " javasoft.sqe.jmx.share.SqeNotification but rather " + + notif.getClass().getName()); + } + } + + if ( ! notif.getMessage().equals(refMessage) ) { + res++; + System.out.println("(ERROR) Message is not " + + refMessage + " in notification\n" + notif); + } + + if ( ! notif.getSource().equals(refSource) ) { + res++; + System.out.println("(ERROR) Source is not " + + refSource + " in notification\n" + notif); + } + + return res; + } + + public void handleNotification(Notification notification, Object handback) { + Utils.debug(Utils.DEBUG_VERBOSE, + "MXBeanNotifTest::handleNotification: Received " + + notification); + notifList.add(notification); + } + +} diff --git a/jdk/test/javax/management/mxbean/MXBeanWeirdParamTest.java b/jdk/test/javax/management/mxbean/MXBeanWeirdParamTest.java new file mode 100644 index 00000000000..358233f79a3 --- /dev/null +++ b/jdk/test/javax/management/mxbean/MXBeanWeirdParamTest.java @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8058865 + * @summary Checks that a serialized instance is not transmitted from an MXBean. + * All the communication should be done via Open Types + * @author Olivier Lagneau + * @modules java.management + * @library /lib/testlibrary + * @compile Basic.java + * @run main/othervm/timeout=300 -DDEBUG_STANDARD MXBeanWeirdParamTest + */ + +import java.util.Map; +import java.util.List; +import java.util.ArrayList; +import java.util.Arrays; + +import java.lang.Process; +import java.lang.management.ManagementFactory; + +import javax.management.MBeanServer; +import javax.management.MBeanServerConnection; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; + +import javax.management.ObjectName; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.SimpleType; +import javax.management.openmbean.TabularDataSupport; +import javax.management.openmbean.TabularType; + +import jdk.testlibrary.ProcessTools; +import jdk.testlibrary.JDKToolFinder; + +public class MXBeanWeirdParamTest { + + private static String BASIC_MXBEAN_CLASS_NAME = "Basic"; + + private static final String CLIENT_CLASS_MAIN = + "MXBeanWeirdParamTest$ClientSide"; + + private JMXConnectorServer cs; + + /* + * First Debug properties and arguments are collect in expected + * map (argName, value) format, then calls original test's run method. + */ + public static void main(String args[]) throws Exception { + + System.out.println("================================================="); + + // Parses parameters + Utils.parseDebugProperties(); + Map map = Utils.parseParameters(args) ; + + // Run test + MXBeanWeirdParamTest test = new MXBeanWeirdParamTest(); + test.run(map); + + } + + /* + * Create the MBeansServe side of the test and returns its address + */ + private JMXServiceURL createServerSide() throws Exception { + final int NINETY_SECONDS = 90; + + // We will use the platform mbean server + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + + JMXServiceURL url = new JMXServiceURL("rmi", null, 0); + cs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs); + cs.start(); + + Utils.waitReady(cs, NINETY_SECONDS); + + JMXServiceURL addr = cs.getAddress(); + return addr; + } + + + /* + * Creating command-line for running subprocess JVM: + * + * JVM command line is like: + * {test_jdk}/bin/java {defaultopts} -cp {test.class.path} {testopts} main + * + * {defaultopts} are the default java options set by the framework. + * + */ + private List buildCommandLine() { + List opts = new ArrayList<>(); + opts.add(JDKToolFinder.getJDKTool("java")); + opts.addAll(Arrays.asList(jdk.testlibrary.Utils.getTestJavaOpts())); + // We need to set WEIRD_PARAM propertty on the client-side + opts.add("-DWEIRD_PARAM"); + opts.add("-cp"); + opts.add(System.getProperty("test.class.path", "test.class.path")); + opts.add(CLIENT_CLASS_MAIN); + + return opts; + } + + /** + * Runs MXBeanWeirdParamTest$ClientSide with the passed options and redirects + * subprocess standard I/O to the current (parent) process. This provides a + * trace of what happens in the subprocess while it is runnning (and before + * it terminates). + * + * @param serviceUrlStr string representing the JMX service Url to connect to. + */ + private int runClientSide(String serviceUrlStr) throws Exception { + + // Building command-line + List opts = buildCommandLine(); + opts.add(serviceUrlStr); + + // Launch separate JVM subprocess + int exitCode = 0; + String[] optsArray = opts.toArray(new String[0]); + ProcessBuilder pb = new ProcessBuilder(optsArray); + Process p = ProcessTools.startProcess("MXBeanWeirdParamTest$ClientSide", pb); + + // Handling end of subprocess + try { + exitCode = p.waitFor(); + if (exitCode != 0) { + System.out.println( + "Subprocess unexpected exit value of [" + exitCode + + "]. Expected 0.\n"); + } + } catch (InterruptedException e) { + System.out.println("Parent process interrupted with exception : \n " + e + " :" ); + + // Parent thread unknown state, killing subprocess. + p.destroyForcibly(); + + throw new RuntimeException( + "Parent process interrupted with exception : \n " + e + " :" ); + } finally { + return exitCode; + } + + } + + public void run(Map args) throws Exception { + + System.out.println("MXBeanWeirdParamTest::run: Start") ; + int errorCount = 0; + + try { + // Initialise the server side + JMXServiceURL urlToUse = createServerSide(); + + // Run client side + errorCount = runClientSide(urlToUse.toString()); + + if ( errorCount == 0 ) { + System.out.println("MXBeanWeirdParamTest::run: Done without any error") ; + } else { + System.out.println("MXBeanWeirdParamTest::run: Done with " + + errorCount + + " error(s)") ; + throw new RuntimeException("errorCount = " + errorCount); + } + + cs.stop(); + + } catch(Exception e) { + throw new RuntimeException(e); + } + + } + + private static class ClientSide { + public static void main(String args[]) throws Exception { + + int errorCount = 0 ; + String msgTag = "ClientSide::main: "; + + try { + + // Get a connection to remote mbean server + JMXServiceURL addr = new JMXServiceURL(args[0]); + JMXConnector cc = JMXConnectorFactory.connect(addr); + MBeanServerConnection mbsc = cc.getMBeanServerConnection(); + + // ---- + System.out.println(msgTag + "Create and register the MBean"); + ObjectName objName = new ObjectName("sqe:type=Basic,protocol=rmi") ; + mbsc.createMBean(BASIC_MXBEAN_CLASS_NAME, objName); + System.out.println(msgTag +"---- OK\n") ; + + // ---- + System.out.println(msgTag +"Get attribute SqeParameterAtt on our MXBean"); + Object result = mbsc.getAttribute(objName, "SqeParameterAtt"); + System.out.println(msgTag +"(OK) Got result of class " + + result.getClass().getName()); + System.out.println(msgTag +"Received CompositeData is " + result); + System.out.println(msgTag +"---- OK\n") ; + + // ---- + // We use the value returned by getAttribute to perform the invoke. + System.out.println(msgTag +"Call operation doWeird on our MXBean [1]"); + mbsc.invoke(objName, "doWeird", + new Object[]{result}, + new String[]{"javax.management.openmbean.CompositeData"}); + System.out.println(msgTag +"---- OK\n") ; + + // ---- + // We build the CompositeData ourselves that time. + System.out.println(msgTag +"Call operation doWeird on our MXBean [2]"); + String typeName = "SqeParameter"; + String[] itemNames = new String[] {"glop"}; + OpenType[] openTypes = new OpenType[] {SimpleType.STRING}; + CompositeType rowType = new CompositeType(typeName, typeName, + itemNames, itemNames, openTypes); + Object[] itemValues = {"HECTOR"}; + CompositeData data = + new CompositeDataSupport(rowType, itemNames, itemValues); + TabularType tabType = new TabularType(typeName, typeName, + rowType, new String[]{"glop"}); + TabularDataSupport tds = new TabularDataSupport(tabType); + tds.put(data); + System.out.println(msgTag +"Source CompositeData is " + data); + mbsc.invoke(objName, "doWeird", + new Object[]{data}, + new String[]{"javax.management.openmbean.CompositeData"}); + System.out.println(msgTag +"---- OK\n") ; + + // ---- + System.out.println(msgTag +"Unregister the MBean"); + mbsc.unregisterMBean(objName); + System.out.println(msgTag +"---- OK\n") ; + + // Terminate the JMX Client + cc.close(); + + } catch(Exception e) { + Utils.printThrowable(e, true) ; + errorCount++; + throw new RuntimeException(e); + } finally { + System.exit(errorCount); + } + } + } +} diff --git a/jdk/test/javax/management/mxbean/SqeDescriptorKey.java b/jdk/test/javax/management/mxbean/SqeDescriptorKey.java new file mode 100644 index 00000000000..60e4926218b --- /dev/null +++ b/jdk/test/javax/management/mxbean/SqeDescriptorKey.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import javax.management.DescriptorKey; + +/** + * That annotation is usable everywhere DescriptorKey is (and even more). + * It is for use to test that you can retrieve the SqeDescriptorKey into the + * appropriate Descriptor instances as built by the JMX runtime. + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +public @interface SqeDescriptorKey { + @DescriptorKey("sqeDescriptorKey") + String value(); + + // List descriptor fields that may be added or may be updated + // when retrieving an MBeanInfo using a JMXWS connection compared to the + // MBeanInfo returned by a local MBeanServer. + // The annotation format is : + // = + // The values actually handled by the test suite are : + // openType=SimpleType.VOID + @DescriptorKey("descriptorFields") + String[] descriptorFields() default {}; +} diff --git a/jdk/test/javax/management/mxbean/SqeNotification.java b/jdk/test/javax/management/mxbean/SqeNotification.java new file mode 100644 index 00000000000..7d0130ea902 --- /dev/null +++ b/jdk/test/javax/management/mxbean/SqeNotification.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.management.Notification; + +/** + * Could hold someday a specific semantic. + * For now it is used to have a Notification which of another class, no more. + */ +public class SqeNotification extends Notification { + + /** Creates a new instance of SqeNotification */ + public SqeNotification(String type, Object source, long sequenceNumber) { + super(type, source, sequenceNumber); + } + + /** Creates a new instance of SqeNotification */ + public SqeNotification(String type, Object source, long sequenceNumber, + long timeStamp) { + super(type, source, sequenceNumber, timeStamp); + } + + /** Creates a new instance of SqeNotification */ + public SqeNotification(String type, Object source, long sequenceNumber, + long timeStamp, String message) { + super(type, source, sequenceNumber, timeStamp, message); + } + + /** Creates a new instance of SqeNotification */ + public SqeNotification(String type, Object source, long sequenceNumber, + String message) { + super(type, source, sequenceNumber, message); + } +} diff --git a/jdk/test/javax/management/mxbean/SqeParameter.java b/jdk/test/javax/management/mxbean/SqeParameter.java new file mode 100644 index 00000000000..35a6ff0d897 --- /dev/null +++ b/jdk/test/javax/management/mxbean/SqeParameter.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.Serializable; + +/** + * That class is to use as an MBean operation parameter or returned value. + * The property Glop with its public getter + setter is only there to be + * reconstructible following MXBean specification, so that SqeParameter can be + * used for what it is designed to. + */ +public class SqeParameter implements Serializable { + + private static boolean weird; + private String glop; + + static { + if ( System.getProperty("WEIRD_PARAM") != null ) { + weird = true; + } + } + + /** + * Creates a new instance of SqeParameter. + *
    When the Java property WEIRD_PARAM is set, that constructor + * throws an exception. + *
    That can be used to ensure the class is instantiated on server side + * but never on client side. + */ + public SqeParameter() throws Exception { + if ( weird ) { + throw new Exception(); + } + } + + public String getGlop() { + return glop; + } + + public void setGlop(String value) { + glop = value; + } +} diff --git a/jdk/test/javax/management/mxbean/Utils.java b/jdk/test/javax/management/mxbean/Utils.java new file mode 100644 index 00000000000..f77196baa2c --- /dev/null +++ b/jdk/test/javax/management/mxbean/Utils.java @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.util.Map; +import java.util.HashMap; +import java.util.Properties; +import java.lang.reflect.Method; +import javax.management.remote.JMXConnectorServerMBean; + +// utility class for MXBean* tests coming from JMX Tonga test suite +class Utils { + + // DEBUG is printed depending on the DEBUG and DEBUG_LEVEL JAVA property + private static final String DEBUG_HEADER = "[debug] "; + + // DEBUG levels + private static int selectedDebugLevel = 0; + static final int DEBUG_STANDARD = 1; + static final int DEBUG_VERBOSE = 2; // Mainly used for stress tests + static final int DEBUG_ALL = DEBUG_STANDARD | DEBUG_VERBOSE; + + static void parseDebugProperties() { + int level = 0; + Properties p = System.getProperties(); + + // get selected levels + if (p.getProperty("DEBUG_STANDARD") != null) { + level |= DEBUG_STANDARD; + } + + if (p.getProperty("DEBUG_VERBOSE") != null) { + level |= DEBUG_VERBOSE; + } + + if (p.getProperty("DEBUG_ALL") != null) { + level |= DEBUG_ALL; + } + + selectedDebugLevel = level; + } + + /** + * Reproduces the original parsing and collection of test parameters + * from the DTonga JMX test suite. + * + * Collects passed args and returns them in a map(argname, value) structure, + * which will be then propagated as necessary to various called methods. + */ + static Map parseParameters(String args[]) + throws Exception { + Utils.debug(DEBUG_STANDARD, "TestRoot::parseParameters: Start"); + HashMap map = new HashMap<>(); + + for ( int i = 0; i < args.length; i++ ) { + if ( args[i].trim().startsWith("-") ) { + if ((i+1) < args.length && !args[i+1].startsWith("-") ) { + Utils.debug(DEBUG_STANDARD, + "TestRoot::parseParameters: added in map = " + + args[i] + + " with value " + + args[i+1]) ; + map.put(args[i].trim(), args[i+1].trim()) ; + } else if ((i+1) < args.length && args[i+1].startsWith("-") || + (i+1) == args.length ) { + Utils.debug(DEBUG_STANDARD, + "TestRoot::parseParameters: added in map = " + + args[i] + + " with null value") ; + map.put(args[i].trim(), null) ; + } else { + System.out.println( + "TestRoot::parseParameters: (WARNING) not added in map = " + + args[i]) ; + } + } + } + + Utils.debug(DEBUG_STANDARD, "TestRoot::parseParameters: Done") ; + return map ; + } + + /** + * This method is to be used in all tests to print anything + * that is temporary. + * Printing is done only when debug is activated by the property DEBUG. + * Printing depends also on the DEBUG_LEVEL property. + * Here it encapsulates a System.out.println. + */ + public static void debug(int level, String line) { + if ((selectedDebugLevel & level) != 0) { + System.out.println(DEBUG_HEADER + line); + } + } + + /** + * Do print stack trace when withStack is true. + * Does try to call getTargetException() and getTargetError() then + * print embedded stacks in the case of an Exception wrapping + * another Exception or an Error. Recurse until no more wrapping + * is found. + */ + public static void printThrowable(Throwable theThro, boolean withStack) { + try { + if (withStack) { + theThro.printStackTrace(System.out); + } + if (theThro instanceof Exception) { + Exception t = (Exception) theThro; + Method target = null; + String blank = " "; + try { + target = t.getClass().getMethod("getTargetException", + (java.lang.Class[]) null); + } catch (Exception ee) { + // OK: getTargetException method could be there or not + } + System.out.println(blank + t.getClass() + "==>" + t.getMessage()); + while (target != null) { + try { + t = (Exception) target.invoke(t, + (java.lang.Object[]) null); + } catch (Exception ee) { + t = null; + } + try { + if (t != null) { + blank = blank + " "; + System.out.println(blank + t.getClass() + "==>" + + t.getMessage()); + try { + target = + t.getClass().getMethod("getTargetException", + (java.lang.Class[]) null); + } catch (Exception ee) { + // OK: getTargetException method could be there or not } + } + } else { + target = null; + } + } catch (Exception ee) { + target = null; + } + } + + // We may have exceptions wrapping an Error then it is + // getTargetError that is likely to be called + try { + target = ((Exception) theThro).getClass().getMethod("getTargetError", + (java.lang.Class[]) null); + } catch (Exception ee) { + // OK: getTargetError method could be there or not + } + Throwable err = theThro; + while (target != null) { + try { + err = (Error) target.invoke(err, + (java.lang.Object[]) null); + } catch (Exception ee) { + err = null; + } + try { + if (err != null) { + blank = blank + " "; + System.out.println(blank + err.getClass() + "==>" + + err.getMessage()); + if (withStack) { + err.printStackTrace(System.out); + } + try { + target = err.getClass().getMethod("getTargetError", + (java.lang.Class[]) null); + } catch (Exception ee) { + // OK: getTargetError method could be there or not + } + } else { + target = null; + } + } catch (Exception ee) { + target = null; + } + } + } else { + System.out.println("Throwable is : " + theThro); + } + } catch (Throwable x) { + System.out.println("Exception : raised in printException : " + x); + } + } + + /** + * Wait up to maxTimeInSeconds second(s) the given JMX connector server + * comes up (which means isActive returns true). + * If it fails to do so we throw a RunTime exception. + */ + public static void waitReady(JMXConnectorServerMBean server, + int maxTimeInSeconds) throws Exception { + int elapsed = 0; + + while (!server.isActive() && elapsed < maxTimeInSeconds) { + Thread.sleep(1000); + elapsed++; + } + + if (server.isActive()) { + String message = "Utils::waitReady: JMX connector server came up"; + if ( elapsed == 0) { + message += " immediately"; + } else { + message += " after " + elapsed + " seconds"; + } + message += " [" + server.getAddress() + "]"; + Utils.debug(DEBUG_STANDARD, message); + } else { + String message = "Utils::waitReady: (ERROR) JMX connector" + + " server didn't come up after " + elapsed + " seconds [" + + server.getAddress() + "]"; + System.out.println(message); + throw new RuntimeException(message); + } + } +} diff --git a/jdk/test/javax/management/query/QueryData.java b/jdk/test/javax/management/query/QueryData.java new file mode 100644 index 00000000000..fadc6d386ee --- /dev/null +++ b/jdk/test/javax/management/query/QueryData.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +public abstract class QueryData { + protected int intValue = 9; + protected long longValue = 9L; + protected Integer integerValue = Integer.valueOf(9); + protected boolean booleanValue = true; + protected double doubleValue = 9D; + protected float floatValue = 9.0F; + protected String stringValue = "9"; +} diff --git a/jdk/test/javax/management/query/QueryFactory.java b/jdk/test/javax/management/query/QueryFactory.java new file mode 100644 index 00000000000..96394996d46 --- /dev/null +++ b/jdk/test/javax/management/query/QueryFactory.java @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.util.ArrayList; + +import javax.management.Query; +import javax.management.QueryExp; +import javax.management.ValueExp; + +/** + * Class used for building QueryExp instances of all every possible type + * in terms of JMX API members; note that several JMX classes are private + * and appears in the JDK API only by their serial form. + * Comments in each case of the big switch in method getQuery() details which + * API member we cover with a given query. + */ +public class QueryFactory extends QueryData { + + private String mbeanClassName = ""; + private String primitiveIntAttName = "IntAtt"; + private String primitiveLongAttName = "LongAtt"; + private String integerAttName = "IntegerAtt"; + private String primitiveBooleanAttName = "BooleanAtt"; + private String primitiveDoubleAttName = "DoubleAtt"; + private String primitiveFloatAttName = "FloatAtt"; + private String stringAttName = "StringAtt"; + private ArrayList queries = new ArrayList(); + + /** + * Creates a new instance of QueryFactory. + * The name is the fully qualified class name of an MBean. + * There is severe constraints on that MBean that must: + *
      + *
    • extend QueryData in order to inherit attribute values. + *
    • define a RW attribute IntAtt of type int + * initialized to QueryData.longValue + *
    • define a RW attribute LongAtt of type long + * initialized to QueryData.intValue + *
    • define a RW attribute IntegerAtt of type Integer + * initialized to QueryData.integerValue + *
    • define a RW attribute BooleanAtt of type boolean + * initialized to QueryData.booleanValue + *
    • define a RW attribute DoubleAtt of type double + * initialized to QueryData.doubleValue + *
    • define a RW attribute FloatAtt of type float + * initialized to QueryData.floatValue + *
    • define a RW attribute StringAtt of type String + * initialized to QueryData.stringValue + *
    + */ + public QueryFactory(String name) { + this.mbeanClassName = name; + } + + /** + * Returns the highest index value the method getQuery supports. + * WARNING : returns 0 if buildQueries haven't been called first ! + */ + public int getSize() { + return queries.size(); + } + + /** + * Populates an ArrayList of QueryExp. + * Lowest index is 1. + * Highest index is returned by getSize(). + *
    The queries numbered 1 to 23 allow to cover all the underlying + * Java classes of the JMX API used to build queries. + */ + public void buildQueries() { + if ( queries.size() == 0 ) { + int smallerIntValue = intValue - 1; + int biggerIntValue = intValue + 1; + + // case 1: + // True if the MBean is of class mbeanClassName + // We cover javax.management.InstanceOfQueryExp + queries.add(Query.isInstanceOf(Query.value(mbeanClassName))); + + // case 2: + // True if the MBean is of class mbeanClassName + // We cover javax.management.MatchQueryExp and + // javax.management.ClassAttributeValueExp + queries.add(Query.match(Query.classattr(), + Query.value(mbeanClassName))); + + // case 3: + // True if an attribute named primitiveIntAttName of type int has + // the value intValue + // We cover javax.management.BinaryRelQueryExp with + // a relOp equal to EQ and javax.management.NumericValueExp + queries.add(Query.eq(Query.attr(primitiveIntAttName), + Query.value(intValue))); + + // case 4: + // True if an attribute named primitiveLongAttName of type long has + // the value longValue + // We cover javax.management.BinaryRelQueryExp with + // a relOp equal to EQ and javax.management.NumericValueExp + queries.add(Query.eq(Query.attr(primitiveLongAttName), + Query.value(longValue))); + + // case 5: + // True if an attribute named primitiveDoubleAttName of type double + // has the value doubleValue + // We cover javax.management.BinaryRelQueryExp with + // a relOp equal to EQ and javax.management.NumericValueExp + queries.add(Query.eq(Query.attr(primitiveDoubleAttName), + Query.value(doubleValue))); + + // case 6: + // True if an attribute named primitiveFloatAttName of type float + // has the value floatValue + // We cover javax.management.BinaryRelQueryExp with + // a relOp equal to EQ and javax.management.NumericValueExp + queries.add(Query.eq(Query.attr(primitiveFloatAttName), + Query.value(floatValue))); + + // case 7: + // True if an attribute named primitiveIntAttName of type int is + // hold by an MBean of class mbeanClassName and has + // the value intValue + // We cover javax.management.QualifiedAttributeValueExp + queries.add(Query.eq(Query.attr(mbeanClassName, primitiveIntAttName), + Query.value(intValue))); + + // case 8: + // True if an attribute named stringAttName of type String has + // the value stringValue + // We cover javax.management.BinaryRelQueryExp with + // a relOp equal to EQ and javax.management.StringValueExp + queries.add(Query.eq(Query.attr(stringAttName), + Query.value(stringValue))); + + // case 9: + // True if an attribute named integerAttName of type Integer has + // the value integerValue + // We cover javax.management.BinaryRelQueryExp with + // a relOp equal to EQ and javax.management.NumericValueExp + queries.add(Query.eq(Query.attr(integerAttName), + Query.value(integerValue))); + + // case 10: + // True if an attribute named primitiveBooleanAttName of type boolean + // has the value booleanValue + // We cover javax.management.BinaryRelQueryExp with + // a relOp equal to EQ and javax.management.BooleanValueExp + queries.add(Query.eq(Query.attr(primitiveBooleanAttName), + Query.value(booleanValue))); + + // case 11: + // True if an attribute named primitiveIntAttName of type int has + // not the value smallerIntValue + // We cover javax.management.NotQueryExp + queries.add(Query.not(Query.eq(Query.attr(primitiveIntAttName), + Query.value(smallerIntValue)))); + + // case 12: + // True if either + // an attribute named primitiveIntAttName of type int has + // the value intValue + // or + // an attribute named primitiveLongAttName of type long has + // the value longValue + // We cover javax.management.OrQueryExp + queries.add(Query.or( + Query.eq(Query.attr(primitiveIntAttName), + Query.value(intValue)), + Query.eq(Query.attr(primitiveLongAttName), + Query.value(longValue)))); + + // case 13: + // True if + // an attribute named primitiveIntAttName of type int has + // the value intValue + // and + // an attribute named primitiveLongAttName of type long has + // the value longValue + // We cover javax.management.AndQueryExp + queries.add(Query.and( + Query.eq(Query.attr(primitiveIntAttName), + Query.value(intValue)), + Query.eq(Query.attr(primitiveLongAttName), + Query.value(longValue)))); + + // case 14: + // True if an attribute named primitiveIntAttName of type int has + // the value intValue + // We cover javax.management.InQueryExp + ValueExp[] inArray = {Query.value(intValue)}; + queries.add(Query.in(Query.attr(primitiveIntAttName), inArray)); + + // case 15: + // True if an attribute named primitiveIntAttName of type int has + // its value in between smallerIntValue and biggerIntValue + // We cover javax.management.BetweenRelQueryExp + queries.add(Query.between(Query.attr(primitiveIntAttName), + Query.value(smallerIntValue), + Query.value(biggerIntValue))); + + // case 16: + // True if an attribute named primitiveIntAttName of type int has + // a value greater than smallerIntValue + // We cover javax.management.BinaryRelQueryExp with + // a relOp equal to GT + queries.add(Query.gt(Query.attr(primitiveIntAttName), + Query.value(smallerIntValue))); + + // case 17: + // True if an attribute named primitiveIntAttName of type int has + // a value greater or equal to smallerIntValue + // We cover javax.management.BinaryRelQueryExp with + // a relOp equal to GE + queries.add(Query.geq(Query.attr(primitiveIntAttName), + Query.value(smallerIntValue))); + + // case 18: + // True if an attribute named primitiveIntAttName of type int has + // a value smaller than biggerIntValue + // We cover javax.management.BinaryRelQueryExp with + // a relOp equal to LT + queries.add(Query.lt(Query.attr(primitiveIntAttName), + Query.value(biggerIntValue))); + + // case 19: + // True if an attribute named primitiveIntAttName of type int has + // a value smaller or equal to biggerIntValue + // We cover javax.management.BinaryRelQueryExp with + // a relOp equal to LE + queries.add(Query.leq(Query.attr(primitiveIntAttName), + Query.value(biggerIntValue))); + + // case 20: + // True if an attribute named primitiveIntAttName of type int has + // a value equal to intValue minus zero + // We cover javax.management.BinaryRelQueryExp with + // a relOp equal to MINUS + queries.add(Query.eq(Query.attr(primitiveIntAttName), + Query.minus(Query.value(intValue), Query.value(0)))); + + // case 21: + // True if an attribute named primitiveIntAttName of type int has + // a value equal to intValue plus zero + // We cover javax.management.BinaryRelQueryExp with + // a relOp equal to PLUS + queries.add(Query.eq(Query.attr(primitiveIntAttName), + Query.plus(Query.value(intValue), Query.value(0)))); + + // case 22: + // True if an attribute named primitiveIntAttName of type int has + // a value equal to intValue divided by one + // We cover javax.management.BinaryRelQueryExp with + // a relOp equal to DIV + queries.add(Query.eq(Query.attr(primitiveIntAttName), + Query.div(Query.value(intValue), Query.value(1)))); + + // case 23: + // True if an attribute named primitiveIntAttName of type int has + // a value equal to intValue multiplicated by one + // We cover javax.management.BinaryRelQueryExp with + // a relOp equal to TIMES + queries.add(Query.eq(Query.attr(primitiveIntAttName), + Query.times(Query.value(intValue), Query.value(1)))); + + // case 24: + // That query is a complex one that combines within a big AND + // queries with index 2 to 23 inclusive. But because a List is + // zero based, we must decrement all indexes by 1 when retrieving + // any previously stored query. + QueryExp q2_3 = Query.and(queries.get(2-1), queries.get(3-1)); + QueryExp q4_5 = Query.and(queries.get(4-1), queries.get(5-1)); + QueryExp q6_7 = Query.and(queries.get(6-1), queries.get(7-1)); + QueryExp q8_9 = Query.and(queries.get(8-1), queries.get(9-1)); + QueryExp q10_11 = Query.and(queries.get(10-1), queries.get(11-1)); + QueryExp q12_13 = Query.and(queries.get(12-1), queries.get(13-1)); + QueryExp q14_15 = Query.and(queries.get(14-1), queries.get(15-1)); + QueryExp q16_17 = Query.and(queries.get(16-1), queries.get(17-1)); + QueryExp q18_19 = Query.and(queries.get(18-1), queries.get(19-1)); + QueryExp q20_21 = Query.and(queries.get(20-1), queries.get(21-1)); + QueryExp q22_23 = Query.and(queries.get(22-1), queries.get(23-1)); + QueryExp q2_5 = Query.and(q2_3, q4_5); + QueryExp q6_9 = Query.and(q6_7, q8_9); + QueryExp q10_13 = Query.and(q10_11, q12_13); + QueryExp q14_17 = Query.and(q14_15, q16_17); + QueryExp q18_21 = Query.and(q18_19, q20_21); + QueryExp q2_9 = Query.and(q2_5, q6_9); + QueryExp q10_17 = Query.and(q10_13, q14_17); + QueryExp q18_23 = Query.and(q18_21, q22_23); + QueryExp q2_17 = Query.and(q2_9, q10_17); + queries.add(Query.and(q2_17, q18_23)); + + // case 25: + // Complex query mixing AND and OR. + queries.add(Query.or(q6_9, q18_23)); + } + } + + /** + * Returns a QueryExp taken is the ArrayList populated by buildQueries(). + * Lowest index is 1. + * Highest index is returned by getSize(). + *
    The queries numbered 1 to 23 allow to cover all the underlying + * Java classes of the JMX API used to build queries. + */ + public QueryExp getQuery(int index) { + return queries.get(index - 1); + } +} diff --git a/jdk/test/javax/management/query/ServerDelegate.java b/jdk/test/javax/management/query/ServerDelegate.java new file mode 100644 index 00000000000..6ce5face871 --- /dev/null +++ b/jdk/test/javax/management/query/ServerDelegate.java @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.security.Principal; +import java.util.ArrayList; +import java.util.List; + +import javax.management.remote.JMXServiceURL ; +import javax.management.MBeanRegistration; +import javax.management.MBeanServer; +import javax.management.ObjectName; +import javax.management.StandardMBean; + +/** + * This class defines an MBean that can be registered and used on client side + * to handle informations or properties of the remote server. + * + * For example, this MBean can store IOR addresses + * of RMI/IIOP connector(s) used in a test. + * + * That MBean might not be used for testing purpose itself. + */ +public class ServerDelegate implements ServerDelegateMBean, MBeanRegistration { + + private MBeanServer mbeanServer = null; + private List addresses = null; + private String port; + private static String javaVersion = System.getProperty("java.version"); + private int sqeJmxwsCredentialsProviderCallCount = 0; + private String jmxwsCredentialsProviderUrl = null; + private int testJMXAuthenticatorCallCount = 0; + private Principal testJMXAuthenticatorPrincipal = null; + + @SqeDescriptorKey("NO PARAMETER CONSTRUCTOR ServerDelegate") + public ServerDelegate() { + addresses = new ArrayList(); + } + + public ObjectName preRegister(MBeanServer server, ObjectName name) + throws Exception { + // Initialize MBeanServer attribute + mbeanServer = server; + return name; + } + public void postRegister(Boolean registrationDone) { + } + public void preDeregister() throws Exception { + } + public void postDeregister() { + } + + public void addAddress(JMXServiceURL url) { + addresses.add(url) ; + } + + public List getAddresses() { + return addresses ; + } + + public void setPort(String p) { + port = p ; + } + + public String getPort() { + return port ; + } + + public String getJavaVersion() { + return javaVersion; + } + + public void sqeJmxwsCredentialsProviderCalled() { + sqeJmxwsCredentialsProviderCallCount++; + } + + public int getSqeJmxwsCredentialsProviderCallCount() { + return sqeJmxwsCredentialsProviderCallCount; + } + + public void setJmxwsCredentialsProviderUrl(String url) { + jmxwsCredentialsProviderUrl = url; + } + + public String getJmxwsCredentialsProviderUrl() { + return jmxwsCredentialsProviderUrl; + } + + public void testJMXAuthenticatorCalled() { + testJMXAuthenticatorCallCount++; + } + + public int getTestJMXAuthenticatorCallCount() { + return testJMXAuthenticatorCallCount; + } + + public void setTestJMXAuthenticatorPrincipal(Principal principal) { + testJMXAuthenticatorPrincipal = principal; + } + + public String getTestJMXAuthenticatorPrincipalString() { + if ( testJMXAuthenticatorPrincipal != null ) { + return testJMXAuthenticatorPrincipal.toString(); + } + + return null; + } + + /** + * Instantiates and registers a StandardMBean in the MBean server. + * + * @param implementationClassName + * The implementation class name of the MBean. + * @param interfaceClassName + * The management interface class name of the MBean. + * @param isMXBean + * If true, the resultant MBean is an MXBean. + * @param name + * The object name of the StandardMBean. + */ + @SuppressWarnings("unchecked") + public void createStandardMBean( + String implementationClassName, + String interfaceClassName, + boolean isMXBean, + ObjectName name) + throws Exception { + + Object implementation = + Class.forName(implementationClassName).newInstance(); + Class interfaceClass = interfaceClassName == null ? null : + (Class)Class.forName(interfaceClassName); + + // Create the StandardMBean + StandardMBean standardMBean = new StandardMBean( + implementation, + interfaceClass, + isMXBean); + + // Register the StandardMBean + mbeanServer.registerMBean(standardMBean, name); + } + + /** + * Instantiates and registers a StandardMBean in the MBean server. + * The object will use standard JMX design pattern to determine + * the management interface associated with the given implementation. + */ + @SuppressWarnings("unchecked") + public void createStandardMBean( + String implementationClassName, + boolean isMXBean, + ObjectName name) + throws Exception { + + createStandardMBean(implementationClassName, null, isMXBean, name); + } +} diff --git a/jdk/test/javax/management/query/ServerDelegateMBean.java b/jdk/test/javax/management/query/ServerDelegateMBean.java new file mode 100644 index 00000000000..88f0b3f5675 --- /dev/null +++ b/jdk/test/javax/management/query/ServerDelegateMBean.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.security.Principal; +import java.util.List; + +import javax.management.remote.JMXServiceURL ; +import javax.management.ObjectName; + +@SqeDescriptorKey("INTERFACE ServerDelegateMBean") +public interface ServerDelegateMBean { + @SqeDescriptorKey("ATTRIBUTE Address") + public void addAddress(JMXServiceURL url); + + @SqeDescriptorKey("ATTRIBUTE Address") + public List getAddresses(); + + public String getPort(); + public void setPort(String p); + + public String getJavaVersion(); + + public void sqeJmxwsCredentialsProviderCalled(); + public int getSqeJmxwsCredentialsProviderCallCount(); + + public void setJmxwsCredentialsProviderUrl(String url); + public String getJmxwsCredentialsProviderUrl(); + + public void testJMXAuthenticatorCalled(); + public int getTestJMXAuthenticatorCallCount(); + + public void setTestJMXAuthenticatorPrincipal(Principal principal); + public String getTestJMXAuthenticatorPrincipalString(); + + public void createStandardMBean( + String implementationClassName, + String interfaceClassName, + boolean isMXBean, + ObjectName name) + throws Exception; + + public void createStandardMBean( + String implementationClassName, + boolean isMXBean, + ObjectName name) + throws Exception; +} diff --git a/jdk/test/javax/management/query/SqeDescriptorKey.java b/jdk/test/javax/management/query/SqeDescriptorKey.java new file mode 100644 index 00000000000..60e4926218b --- /dev/null +++ b/jdk/test/javax/management/query/SqeDescriptorKey.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import javax.management.DescriptorKey; + +/** + * That annotation is usable everywhere DescriptorKey is (and even more). + * It is for use to test that you can retrieve the SqeDescriptorKey into the + * appropriate Descriptor instances as built by the JMX runtime. + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +public @interface SqeDescriptorKey { + @DescriptorKey("sqeDescriptorKey") + String value(); + + // List descriptor fields that may be added or may be updated + // when retrieving an MBeanInfo using a JMXWS connection compared to the + // MBeanInfo returned by a local MBeanServer. + // The annotation format is : + // = + // The values actually handled by the test suite are : + // openType=SimpleType.VOID + @DescriptorKey("descriptorFields") + String[] descriptorFields() default {}; +} diff --git a/jdk/test/javax/management/query/SupportedQueryTypesTest.java b/jdk/test/javax/management/query/SupportedQueryTypesTest.java new file mode 100644 index 00000000000..fab8128a317 --- /dev/null +++ b/jdk/test/javax/management/query/SupportedQueryTypesTest.java @@ -0,0 +1,471 @@ +/* + * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8058865 + * @summary Tests most of the existing query types. + * @author Olivier Lagneau + * @modules java.management + * @compile TestQuery.java + * @run main/othervm/timeout=300 -DDEBUG_STANDARD SupportedQueryTypesTest -mbeanClassName TestQuery + */ + +import java.util.Map ; +import java.util.HashMap; +import java.util.Set; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Properties; +import java.lang.reflect.Method; + +import java.lang.management.ManagementFactory; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.MBeanServerConnection; +import javax.management.ObjectInstance; +import javax.management.ObjectName ; +import javax.management.QueryExp; + +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; + +public class SupportedQueryTypesTest { + + protected String mbeanClassName = null; + + private MBeanServerConnection mbsc = null; + + + /* + * First Debug properties and arguments are collect in expected + * map (argName, value) format, then calls original test's run method. + */ + public static void main(String args[]) throws Exception { + + System.out.println("================================================="); + + // Parses parameters + Utils.parseDebugProperties(); + Map map = Utils.parseParameters(args) ; + + // Run test + SupportedQueryTypesTest test = new SupportedQueryTypesTest(); + test.run(map); + + } + + public void run(Map args) { + int errorCount = 0; + + ObjectName on = null; + ObjectName serverDelegateObjectName = null; + + JMXConnectorServer cs = null; + JMXConnector cc = null; + + System.out.println("SupportedQueryTypesTest::run: Start") ; + try { + // JMX MbeanServer used inside single VM as if remote. + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + + JMXServiceURL url = new JMXServiceURL("rmi", null, 0); + cs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs); + cs.start(); + + JMXServiceURL addr = cs.getAddress(); + cc = JMXConnectorFactory.connect(addr); + mbsc = cc.getMBeanServerConnection(); + + + // Create and register the ServerDelegate MBean on the remote MBeanServer + String serverDelegateClassName = ServerDelegate.class.getName(); + serverDelegateObjectName = + new ObjectName("defaultDomain:class=" + serverDelegateClassName); + mbsc.createMBean(serverDelegateClassName, serverDelegateObjectName); + + // Retrieve the MBean class name + mbeanClassName = (String) args.get("-mbeanClassName") ; + on = new ObjectName("defaultDomain:class=" + mbeanClassName); + + // Create and register the MBean on the remote MBeanServer + System.out.println("SupportedQueryTypesTest::run: CREATE " + + mbeanClassName + " on the remote MBeanServer with name " + + on); + mbsc.createMBean(mbeanClassName, on); + + // Create a QueryFactory and setup which query we'll use. + QueryFactory queries = new QueryFactory(mbeanClassName); + queries.buildQueries(); + int maxIndex = queries.getSize(); + int minIndex = 1; + + // Create a reference Set to check later on + // the queryNames() results + Set referenceNameSet = new HashSet(); + referenceNameSet.add(on); + + // Create a reference Set to check later on + // the queryMBeans() results + ObjectInstance oi = new ObjectInstance(on, mbeanClassName); + Set referenceInstanceSet = + new HashSet(); + referenceInstanceSet.add(oi); + + // Perform the queryNames and queryMBeans requests + for (int i = minIndex; i <= maxIndex; i++ ) { + QueryExp query = queries.getQuery(i); + System.out.println("----"); + System.out.println("SupportedQueryTypesTest::run: Query # " + i); + System.out.println("query " + query); + errorCount += + doQueryNames(query, referenceNameSet); + errorCount += + doQueryMBeans(query, referenceInstanceSet); + } + + } catch(Exception e) { + Utils.printThrowable(e, true); + errorCount++; + + } finally { + // Do unregister the MBean + try { + if (mbsc.isRegistered(on)) { + mbsc.unregisterMBean(on); + } + if (mbsc.isRegistered(serverDelegateObjectName)) { + mbsc.unregisterMBean(serverDelegateObjectName); + } + } catch (Exception e) { + Utils.printThrowable(e, true) ; + errorCount++; + } + + try { + // Close JMX Connector Client + cc.close(); + // Stop connertor server + cs.stop(); + + } catch (Exception e) { + Utils.printThrowable(e, true) ; + errorCount++; + } + } + + System.out.println(""); + System.out.println("SupportedQueryTypesTest::run: Done") ; + + // Handle result + if (errorCount == 0) { + System.out.println("SupportedQueryTypesTest::run: (OK)"); + } else { + String message = "SupportedQueryTypesTest::run: (ERROR) Got " + + + errorCount + " error(s)"; + System.out.println(message); + throw new RuntimeException(message); + } + } + + + private int doQueryNames(QueryExp query, Set referenceSet) { + int errorCount = 0; + System.out.println(" <*> Perform queryNames call "); + + try { + // Call queryNames on the remote MBeanServer + Set remoteSet = mbsc.queryNames(null, query); + + // Compare the 2 Set + errorCount += checkSet(remoteSet, referenceSet); + + // Cleaning + remoteSet.clear(); + + } catch (Exception e) { + Utils.printThrowable(e, true); + errorCount++; + } + + if ( errorCount == 0 ) { + System.out.println("\t(OK)"); + } else { + System.out.println("\t(ERROR) Query failed"); + } + + return errorCount; + } + + + private int doQueryMBeans(QueryExp query, Set referenceSet) { + int errorCount = 0; + System.out.println(" <*> Perform queryMBeans call "); + + try { + // Call queryMBeans on the remote MBeanServer + Set remoteSet = mbsc.queryMBeans(null, query); + + // Compare the 2 Set + errorCount += checkSet(remoteSet, referenceSet); + + // Cleaning + remoteSet.clear(); + + } catch (Exception e) { + Utils.printThrowable(e, true); + errorCount++; + } + + if ( errorCount == 0 ) { + System.out.println("\t(OK)"); + } else { + System.out.println("\t(ERROR) Query failed"); + } + + return errorCount; + } + + /** + * Pretty print of a Set content. + * When the Set isn't empty, toString() is called on each element. + *
    The variable's name used to hold that Set is given via the setName + * parameter and used in the output. + */ + private static void printSet(Set printableSet, String setName) { + if ( printableSet.size() == 0 ) { + System.out.println("The Set " + setName + " is empty"); + } else { + System.out.println("The Set " + setName + " contains :"); + + for (Iterator it = printableSet.iterator(); it.hasNext();) { + Object elem = it.next(); + System.out.println("\t" + elem.toString()); + } + } + } + + + /** + * This method check the Set remoteSet is equal to + * the reference Set referenceSet, + * which means same size and content (order doesn't matter). + *
    It returns 0 when the check is fine, otherwise 1. + */ + private int checkSet(Set remoteSet, Set referenceSet) { + if ( ! remoteSet.equals(referenceSet) ) { + System.out.println("SupportedQueryTypesTest::checkSet:" + + " (ERROR) Set aren't as expected"); + printSet(remoteSet, "remoteSet"); + printSet(referenceSet, "referenceSet"); + return 1; + } else { + return 0; + } + } + + // Utility inner class coming from JMX Tonga test suite. + private static class Utils { + + // DEBUG is printed depending on the DEBUG and DEBUG_LEVEL JAVA property + static final String DEBUG_HEADER = "[debug] "; + + // DEBUG levels + static int selectedDebugLevel = 0; + static final int DEBUG_STANDARD = 1; + static final int DEBUG_VERBOSE = 2; // Mainly used for stress tests + static final int DEBUG_ALL = DEBUG_STANDARD | DEBUG_VERBOSE; + + static void parseDebugProperties() { + int level = 0; + Properties p = System.getProperties(); + + // get selected levels + if (p.getProperty("DEBUG_STANDARD") != null) { + level |= DEBUG_STANDARD; + } + + if (p.getProperty("DEBUG_VERBOSE") != null) { + level |= DEBUG_VERBOSE; + } + + if (p.getProperty("DEBUG_ALL") != null) { + level |= DEBUG_ALL; + } + + selectedDebugLevel = level; + } + + /** + * Reproduces the original parsing and collection of test parameters + * from the DTonga JMX test suite. + * + * Collects passed args and returns them in a map(argname, value) structure, + * which will be then propagated as necessary to various called methods. + */ + static Map parseParameters(String args[]) + throws Exception { + debug(DEBUG_STANDARD, "TestRoot::parseParameters: Start"); + HashMap map = new HashMap<>(); + + for ( int i = 0; i < args.length; i++ ) { + if ( args[i].trim().startsWith("-") ) { + if ((i+1) < args.length && !args[i+1].startsWith("-") ) { + debug(DEBUG_STANDARD, + "TestRoot::parseParameters: added in map = " + + args[i] + + " with value " + + args[i+1]) ; + map.put(args[i].trim(), args[i+1].trim()) ; + } else if ((i+1) < args.length && args[i+1].startsWith("-") || + (i+1) == args.length ) { + debug(DEBUG_STANDARD, + "TestRoot::parseParameters: added in map = " + + args[i] + + " with null value") ; + map.put(args[i].trim(), null) ; + } else { + System.out.println( + "TestRoot::parseParameters: (WARNING) not added in map = " + + args[i]) ; + } + } + } + + debug(DEBUG_STANDARD, "TestRoot::parseParameters: Done") ; + return map ; + } + + /** + * This method is to be used in all tests to print anything + * that is temporary. + * Printing is done only when debug is activated by the property DEBUG. + * Printing depends also on the DEBUG_LEVEL property. + * Here it encapsulates a System.out.println. + */ + static void debug(int level, String line) { + if ((selectedDebugLevel & level) != 0) { + System.out.println(DEBUG_HEADER + line); + } + } + + /** + * Do print stack trace when withStack is true. + * Does try to call getTargetException() and getTargetError() then + * print embedded stacks in the case of an Exception wrapping + * another Exception or an Error. Recurse until no more wrapping + * is found. + */ + static void printThrowable(Throwable theThro, boolean withStack) { + try { + if (withStack) { + theThro.printStackTrace(System.out); + } + if (theThro instanceof Exception) { + Exception t = (Exception) theThro; + Method target = null; + String blank = " "; + try { + target = t.getClass().getMethod("getTargetException", + (java.lang.Class[]) null); + } catch (Exception ee) { + // OK: getTargetException method could be there or not + } + System.out.println(blank + t.getClass() + "==>" + t.getMessage()); + while (target != null) { + try { + t = (Exception) target.invoke(t, + (java.lang.Object[]) null); + } catch (Exception ee) { + t = null; + } + try { + if (t != null) { + blank = blank + " "; + System.out.println(blank + t.getClass() + "==>" + + t.getMessage()); + try { + target = + t.getClass().getMethod("getTargetException", + (java.lang.Class[]) null); + } catch (Exception ee) { + // OK: getTargetException method could be there or not } + } + } else { + target = null; + } + } catch (Exception ee) { + target = null; + } + } + + // We may have exceptions wrapping an Error then it is + // getTargetError that is likely to be called + try { + target = ((Exception) theThro).getClass().getMethod("getTargetError", + (java.lang.Class[]) null); + } catch (Exception ee) { + // OK: getTargetError method could be there or not + } + Throwable err = theThro; + while (target != null) { + try { + err = (Error) target.invoke(err, + (java.lang.Object[]) null); + } catch (Exception ee) { + err = null; + } + try { + if (err != null) { + blank = blank + " "; + System.out.println(blank + err.getClass() + "==>" + + err.getMessage()); + if (withStack) { + err.printStackTrace(System.out); + } + try { + target = err.getClass().getMethod("getTargetError", + (java.lang.Class[]) null); + } catch (Exception ee) { + // OK: getTargetError method could be there or not + } + } else { + target = null; + } + } catch (Exception ee) { + target = null; + } + } + } else { + System.out.println("Throwable is : " + theThro); + } + } catch (Throwable x) { + System.out.println("Exception : raised in printException : " + x); + } + } + } + +} diff --git a/jdk/test/javax/management/query/TestQuery.java b/jdk/test/javax/management/query/TestQuery.java new file mode 100644 index 00000000000..31b896ed7ff --- /dev/null +++ b/jdk/test/javax/management/query/TestQuery.java @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * Class TestQuery + * MBean used for testing the types wired when using QueryExp. + * It is heavily linked to QueryFactory. + */ +public class TestQuery extends QueryData implements TestQueryMBean { + + /** + * Attribute : BooleanAtt + */ + private boolean booleanAtt = booleanValue; + + /** + * Attribute : DoubleAtt + */ + private double doubleAtt = doubleValue; + + /** + * Attribute : FloatAtt + */ + private float floatAtt = floatValue; + + /** + * Attribute : IntAtt + */ + private int intAtt = intValue; + + /** + * Attribute : IntegerAtt + */ + private Integer integerAtt = integerValue; + + /** + * Attribute : LongAtt + */ + private long longAtt = longValue; + + /** + * Attribute : StringAtt + */ + private String stringAtt = stringValue; + + public TestQuery() { + } + + /** + * Get Att of type boolean + */ + public boolean getBooleanAtt() { + return booleanAtt; + } + + /** + * Set Att of type boolean + */ + public void setBooleanAtt(boolean value) { + booleanAtt = value; + } + + /** + * Get Att of type double + */ + public double getDoubleAtt() { + return doubleAtt; + } + + /** + * Set Att of type double + */ + public void setDoubleAtt(double value) { + doubleAtt = value; + } + + /** + * Get Att of type float + */ + public float getFloatAtt() { + return floatAtt; + } + + /** + * Set Att of type float + */ + public void setFloatAtt(float value) { + floatAtt = value; + } + + /** + * Get Att of type int + */ + public int getIntAtt() { + return intAtt; + } + + /** + * Set Att of type int + */ + public void setIntAtt(int value) { + intAtt = value; + } + + /** + * Get Att of type Integer + */ + public Integer getIntegerAtt() { + return integerAtt; + } + + /** + * Set Att of type Integer + */ + public void setIntegerAtt(Integer value) { + integerAtt = value; + } + + /** + * Get Att of type long + */ + public long getLongAtt() { + return longAtt; + } + + /** + * Set Att of type long + */ + public void setLongAtt(long value) { + longAtt = value; + } + + /** + * Get Att of type String + */ + public String getStringAtt() { + return stringAtt; + } + + /** + * Set Att of type String + */ + public void setStringAtt(String value) { + stringAtt = value; + } + +} diff --git a/jdk/test/javax/management/query/TestQueryMBean.java b/jdk/test/javax/management/query/TestQueryMBean.java new file mode 100644 index 00000000000..9cf321f9030 --- /dev/null +++ b/jdk/test/javax/management/query/TestQueryMBean.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * Interface TestQueryMBean + * MBean used for testing the types wired when using QueryExp. + * It is heavily linked to QueryFactory. + */ +public interface TestQueryMBean +{ + /** + * Get Att of type boolean + */ + public boolean getBooleanAtt(); + + /** + * Set Att of type boolean + */ + public void setBooleanAtt(boolean value); + + /** + * Get Att of type double + */ + public double getDoubleAtt(); + + /** + * Set Att of type double + */ + public void setDoubleAtt(double value); + + /** + * Get Att of type float + */ + public float getFloatAtt(); + + /** + * Set Att of type float + */ + public void setFloatAtt(float value); + + /** + * Get Att of type int + */ + public int getIntAtt(); + + /** + * Set Att of type int + */ + public void setIntAtt(int value); + + /** + * Get Att of type Integer + */ + public Integer getIntegerAtt(); + + /** + * Set Att of type Integer + */ + public void setIntegerAtt(Integer value); + + /** + * Get Att of type long + */ + public long getLongAtt(); + + /** + * Set Att of type long + */ + public void setLongAtt(long value); + + /** + * Get Att of type String + */ + public String getStringAtt(); + + /** + * Set Att of type String + */ + public void setStringAtt(String value); + +} diff --git a/jdk/test/javax/management/security/AuthorizationTest.java b/jdk/test/javax/management/security/AuthorizationTest.java new file mode 100644 index 00000000000..54309ab059b --- /dev/null +++ b/jdk/test/javax/management/security/AuthorizationTest.java @@ -0,0 +1,613 @@ +/* + * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8058865 + * @summary Checks various authentication behavior from remote jmx client + * @author Olivier Lagneau + * @modules java.management + * @library /lib/testlibrary + * @compile Simple.java + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dusername=username1 -Dpassword=password1 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dusername=username2 -Dpassword=password2 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials -expectedCreateException -expectedSetException -expectedInvokeException + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dusername=username6 -Dpassword=password6 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials -expectedCreateException -expectedGetException -expectedSetException -expectedInvokeException + * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username1 -Dpassword=password1 AuthorizationTest -server -mapType x.password.file -populate -client -mapType credentials + * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username3 -Dpassword=password3 AuthorizationTest -server -mapType x.password.file -populate -client -mapType credentials -expectedGetException + * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username5 -Dpassword=password5 AuthorizationTest -server -mapType x.password.file -populate -client -mapType credentials -expectedCreateException -expectedGetException -expectedSetException -expectedInvokeException + * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username6 -Dpassword=password6 AuthorizationTest -server -mapType x.password.file -populate -client -mapType credentials -expectedCreateException -expectedGetException -expectedSetException -expectedInvokeException + * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username1 -Dpassword=password1 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials + * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username2 -Dpassword=password2 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials -expectedCreateException -expectedSetException -expectedInvokeException + * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username3 -Dpassword=password3 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials -expectedCreateException -expectedGetException -expectedSetException -expectedInvokeException + * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username4 -Dpassword=password4 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials -expectedGetException -expectedSetException + * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username5 -Dpassword=password5 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials -expectedCreateException -expectedGetException -expectedSetException -expectedInvokeException + */ + +import java.io.File; +import java.util.Map ; +import java.util.HashMap ; +import java.util.List; +import java.util.ArrayList; +import java.util.Arrays; + +import java.lang.management.ManagementFactory; + +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory ; +import javax.management.MBeanServerConnection; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; + +import javax.management.Attribute ; +import javax.management.ObjectName ; + +import jdk.testlibrary.ProcessTools; +import jdk.testlibrary.JDKToolFinder; + +public class AuthorizationTest { + + static final String SERVER_CLASS_NAME = "AuthorizationTest"; + static final String CLIENT_CLASS_NAME = "AuthorizationTest$ClientSide"; + static final String CLIENT_CLASS_MAIN = CLIENT_CLASS_NAME; + + static final String USERNAME_PROPERTY = "username"; + static final String PASSWORD_PROPERTY = "password"; + + private JMXConnectorServer cs; + + /* + * First Debug properties and arguments are collect in expected + * map (argName, value) format, then calls original test's run method. + */ + public static void main(String args[]) throws Exception { + + System.out.println("================================================="); + + // Parses parameters + Utils.parseDebugProperties(); + + // Supported parameters list format is : + // "MainClass [-server ...] [-client ...] + // with either "-parami valuei" or "-parami" + HashMap serverMap = new HashMap<>() ; + int clientArgsIndex = + Utils.parseServerParameters(args, SERVER_CLASS_NAME, serverMap); + + // Extract and records client params + String[] clientParams = null; + if (clientArgsIndex < args.length) { + int clientParamsSize = args.length - clientArgsIndex; + clientParams = new String[clientParamsSize]; + System.arraycopy(args, clientArgsIndex, clientParams, 0, clientParamsSize); + } else { + clientParams = new String[0]; + } + + // Run test + AuthorizationTest test = new AuthorizationTest(); + test.run(serverMap, clientParams); + + } + + /* + * Create the MBeansServer side of the test and returns its address + */ + private JMXServiceURL createServerSide(Map serverMap) + throws Exception { + final int NINETY_SECONDS = 90; + + System.out.println("AuthorizationTest::createServerSide: Start") ; + + MBeanServer mbs = MBeanServerFactory.newMBeanServer(); + JMXServiceURL url = new JMXServiceURL("rmi", null, 0); + + // Creates connection environment from server side params + HashMap env = new HashMap<>(); + String value = null; + + if ((value = (String)serverMap.get("-mapType")) != null) { + if (value.contains("x.access.file")) { + String accessFileStr = System.getProperty("test.src") + + File.separator + "access.properties"; + env.put("jmx.remote.x.access.file", accessFileStr); + System.out.println("Added " + accessFileStr + " file as jmx.remote.x.access.file"); + } + if (value.contains("x.password.file")) { + String passwordFileStr = System.getProperty("test.src") + + File.separator + "password.properties"; + env.put("jmx.remote.x.password.file", passwordFileStr); + System.out.println("Added " + passwordFileStr + " file as jmx.remote.x.password.file"); + } + } + + if (serverMap.containsKey("-populate")) { + String populateClassName = "Simple"; + ObjectName on = + new ObjectName("defaultDomain:class=Simple"); + + Utils.debug(Utils.DEBUG_STANDARD, "create and register Simple MBean") ; + mbs.createMBean(populateClassName, on); + } + + cs = JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs); + cs.start(); + + Utils.waitReady(cs, NINETY_SECONDS); + + JMXServiceURL addr = cs.getAddress(); + + System.out.println("AuthorizationTest::createServerSide: Done.") ; + + return addr; + } + + /* + * Creating command-line for running subprocess JVM: + * + * JVM command line is like: + * {test_jdk}/bin/java {defaultopts} -cp {test.class.path} {testopts} main + * + * {defaultopts} are the default java options set by the framework. + * + */ + private List buildCommandLine(String args[]) { + List opts = new ArrayList<>(); + opts.add(JDKToolFinder.getJDKTool("java")); + opts.addAll(Arrays.asList(jdk.testlibrary.Utils.getTestJavaOpts())); + + String usernameValue = System.getProperty(USERNAME_PROPERTY); + if (usernameValue != null) { + opts.add("-D" + USERNAME_PROPERTY + "=" + usernameValue); + } + String passwordValue = System.getProperty(PASSWORD_PROPERTY); + if (passwordValue != null) { + opts.add("-D" + PASSWORD_PROPERTY + "=" + passwordValue); + } + + opts.add("-cp"); + opts.add(System.getProperty("test.class.path", "test.class.path")); + opts.add(CLIENT_CLASS_MAIN); + opts.addAll(Arrays.asList(args)); + return opts; + } + + /** + * Runs AuthorizationTest$ClientSide with the passed options and redirects + * subprocess standard I/O to the current (parent) process. This provides a + * trace of what happens in the subprocess while it is runnning (and before + * it terminates). + * + * @param serviceUrlStr string representing the JMX service Url to connect to. + */ + private int runClientSide(String args[], String serviceUrlStr) throws Exception { + + // Building command-line + List opts = buildCommandLine(args); + opts.add("-serviceUrl"); + opts.add(serviceUrlStr); + + // Launch separate JVM subprocess + int exitCode = 0; + String[] optsArray = opts.toArray(new String[0]); + ProcessBuilder pb = new ProcessBuilder(optsArray); + Process p = ProcessTools.startProcess("AuthorizationTest$ClientSide", pb); + + // Handling end of subprocess + try { + exitCode = p.waitFor(); + if (exitCode != 0) { + System.out.println( + "Subprocess unexpected exit value of [" + exitCode + + "]. Expected 0.\n"); + } + } catch (InterruptedException e) { + System.out.println("Parent process interrupted with exception : \n " + e + " :" ); + + // Parent thread unknown state, killing subprocess. + p.destroyForcibly(); + + throw new RuntimeException( + "Parent process interrupted with exception : \n " + e + " :" ); + + } finally { + if (p.isAlive()) { + p.destroyForcibly(); + } + return exitCode; + } + + } + + public void run(Map serverArgs, String clientArgs[]) { + + System.out.println("AuthorizationTest::run: Start") ; + int errorCount = 0; + + try { + // Initialise the server side + JMXServiceURL urlToUse = createServerSide(serverArgs); + + // Run client side + errorCount = runClientSide(clientArgs, urlToUse.toString()); + + if ( errorCount == 0 ) { + System.out.println("AuthorizationTest::run: Done without any error") ; + } else { + System.out.println("AuthorizationTest::run: Done with " + + errorCount + + " error(s)") ; + throw new RuntimeException("errorCount = " + errorCount); + } + + cs.stop(); + + } catch(Exception e) { + throw new RuntimeException(e); + } + + } + + private static class ClientSide { + + private JMXConnector cc = null; + private MBeanServerConnection mbsc = null; + + public static void main(String args[]) throws Exception { + + // Parses parameters + Utils.parseDebugProperties(); + + // Supported parameters list format is : "MainClass [-client ...] + // with either "-parami valuei" or "-parami" + HashMap clientMap = new HashMap<>() ; + Utils.parseClientParameters(args, CLIENT_CLASS_NAME, clientMap); + + // Run test + ClientSide test = new ClientSide(); + test.run(clientMap); + + } + + public void run(Map args) { + + int errorCount = 0 ; + + try { + boolean expectedCreateException = + (args.containsKey("-expectedCreateException")) ? true : false ; + boolean expectedGetException = + (args.containsKey("-expectedGetException")) ? true : false ; + boolean expectedSetException = + (args.containsKey("-expectedSetException")) ? true : false ; + boolean expectedInvokeException = + (args.containsKey("-expectedInvokeException")) ? true : false ; + // JSR262 (see bug 6440374) + // There is no special JSR262 protocol operation for connect. + // The first request sent initiate the connection. + // In the JSR262 current implementation, getDefaultDomain is sent to + // the server in order to get the server part of the connection ID. + // => the connection may fail if no access permission on get requests. + boolean expectedConnectException = + (args.containsKey("-expectedConnectException")) ? true : false ; + // Before connection, + // remove the element of the Map with null values (not supported by RMI) + // See bug 4982668 + args.remove("-expectedCreateException"); + args.remove("-expectedGetException"); + args.remove("-expectedSetException"); + args.remove("-expectedInvokeException"); + args.remove("-expectedConnectException"); + + + // Here do connect to the JMX Server + String username = System.getProperty("username"); + Utils.debug(Utils.DEBUG_STANDARD, + "ClientSide::run: CONNECT on behalf of \"" + username + "\""); + doConnect(args, expectedConnectException); + + // If the connection did not fail, perform some requests. + // At this stage the mbeanserver connection is up and running + if (mbsc != null) { + ObjectName on = new ObjectName("defaultDomain:class=Simple"); + + // Create request + Utils.debug(Utils.DEBUG_STANDARD, + "ClientSide::run: CREATE on behalf of \"" + + username + "\""); + errorCount += doCreateRequest(mbsc, + new ObjectName("defaultDomain:class=Simple,user=" + username), + expectedCreateException); + + // Get request + Utils.debug(Utils.DEBUG_STANDARD, + "ClientSide::run: GET on behalf of \"" + + username + "\""); + errorCount += doGetRequest(mbsc, on, expectedGetException); + + // Set request + Utils.debug(Utils.DEBUG_STANDARD, + "ClientSide::run: SET on behalf of \"" + + username + "\""); + errorCount += doSetRequest(mbsc, on, expectedSetException); + + // Invoke request + Utils.debug(Utils.DEBUG_STANDARD, + "ClientSide::run: INVOKE on behalf of \"" + + username + "\""); + errorCount += doInvokeRequest(mbsc, on, expectedInvokeException); + } + + } catch(Exception e) { + Utils.printThrowable(e, true) ; + errorCount++; + } finally { + // Terminate the JMX Client + try { + cc.close(); + } catch (Exception e) { + Utils.printThrowable(e, true) ; + errorCount++; + } + } + + System.out.println("ClientSide::run: Done") ; + + // Handle result + if (errorCount == 0) { + System.out.println("ClientSide::run: (OK) authorization test succeeded."); + } else { + String message = "AuthorizationTest$ClientSide::run: (ERROR) " + + " authorization test failed with " + + errorCount + " error(s)"; + System.out.println(message); + throw new RuntimeException(message); + } + } + + protected void doConnect(Map args, + boolean expectedException) { + + String msgTag = "ClientSide::doConnect"; + boolean throwRuntimeException = false; + String message = ""; + + try { + Utils.debug(Utils.DEBUG_STANDARD, + "ClientSide::doConnect: Connect the client"); + + // Collect connection environment + HashMap env = new HashMap<>(); + + Object value = args.get("-mapType"); + if (value != null) { + String username = System.getProperty("username"); + String password = System.getProperty("password"); + Utils.debug(Utils.DEBUG_STANDARD, + msgTag + "add \"jmx.remote.credentials\" = \"" + + username + "\", \"" + password + "\""); + env.put("jmx.remote.credentials", + new String[] { username , password }); + } + + // Get a connection to remote mbean server + JMXServiceURL addr = new JMXServiceURL((String)args.get("-serviceUrl")); + cc = JMXConnectorFactory.connect(addr,env); + mbsc = cc.getMBeanServerConnection(); + + if (expectedException) { + message = "ClientSide::doConnect: (ERROR) " + + "Connect did not fail with expected SecurityException"; + System.out.println(message); + throwRuntimeException = true; + } else { + System.out.println("ClientSide::doConnect: (OK) Connect succeed"); + } + } catch(Exception e) { + Utils.printThrowable(e, true); + if (expectedException) { + if (e instanceof java.lang.SecurityException) { + System.out.println("ClientSide::doConnect: (OK) " + + "Connect failed with expected SecurityException"); + } else { + message = "ClientSide::doConnect: (ERROR) " + + "Create failed with " + e.getClass() + + " instead of expected SecurityException"; + System.out.println(message); + throwRuntimeException = true; + } + } else { + message = "ClientSide::doConnect: (ERROR) " + + "Connect failed"; + System.out.println(message); + throwRuntimeException = true; + } + } + + // If the connection failed, or if the connection succeeded but should not, + // no need to go further => throw RuntimeException and exit the test + if (throwRuntimeException) { + throw new RuntimeException(message); + } + } + + protected int doCreateRequest(MBeanServerConnection mbsc, + ObjectName on, + boolean expectedException) { + int errorCount = 0; + + try { + Utils.debug(Utils.DEBUG_STANDARD, + "ClientSide::doCreateRequest: Create and register the MBean") ; + + mbsc.createMBean("Simple", on) ; + + if (expectedException) { + System.out.println("ClientSide::doCreateRequest: " + + "(ERROR) Create did not fail with expected SecurityException"); + errorCount++; + } else { + System.out.println("ClientSide::doCreateRequest: (OK) Create succeed") ; + } + } catch(Exception e) { + Utils.printThrowable(e, true) ; + if (expectedException) { + if (e instanceof java.lang.SecurityException) { + System.out.println("ClientSide::doCreateRequest: " + + "(OK) Create failed with expected SecurityException") ; + } else { + System.out.println("ClientSide::doCreateRequest: " + + "(ERROR) Create failed with " + + e.getClass() + " instead of expected SecurityException"); + errorCount++; + } + } else { + System.out.println("ClientSide::doCreateRequest: " + + "(ERROR) Create failed"); + errorCount++; + } + } + return errorCount; + } + + protected int doGetRequest(MBeanServerConnection mbsc, + ObjectName on, + boolean expectedException) { + int errorCount = 0; + + try { + Utils.debug(Utils.DEBUG_STANDARD, + "ClientSide::doGetRequest: Get attributes of the MBean") ; + + mbsc.getAttribute(on, "Attribute"); + + if (expectedException) { + System.out.println("ClientSide::doGetRequest: " + + "(ERROR) Get did not fail with expected SecurityException"); + errorCount++; + } else { + System.out.println("ClientSide::doGetRequest: (OK) Get succeed") ; + } + } catch(Exception e) { + Utils.printThrowable(e, true) ; + if (expectedException) { + if (e instanceof java.lang.SecurityException) { + System.out.println("ClientSide::doGetRequest: " + + "(OK) Get failed with expected SecurityException") ; + } else { + System.out.println("ClientSide::doGetRequest: " + + "(ERROR) Get failed with " + + e.getClass() + " instead of expected SecurityException"); + errorCount++; + } + } else { + System.out.println("ClientSide::doGetRequest: (ERROR) Get failed"); + errorCount++; + } + } + + return errorCount; + } + + protected int doSetRequest(MBeanServerConnection mbsc, + ObjectName on, + boolean expectedException) { + int errorCount = 0; + + try { + Utils.debug(Utils.DEBUG_STANDARD, + "ClientSide::doSetRequest: Set attributes of the MBean") ; + + Attribute attribute = new Attribute("Attribute", "My value") ; + mbsc.setAttribute(on, attribute) ; + + if (expectedException) { + System.out.println("ClientSide::doSetRequest: " + + "(ERROR) Set did not fail with expected SecurityException"); + errorCount++; + } else { + System.out.println("ClientSide::doSetRequest: (OK) Set succeed") ; + } + } catch(Exception e) { + Utils.printThrowable(e, true) ; + if (expectedException) { + if (e instanceof java.lang.SecurityException) { + System.out.println("ClientSide::doSetRequest: " + + "(OK) Set failed with expected SecurityException") ; + } else { + System.out.println("ClientSide::doSetRequest: " + + "(ERROR) Set failed with " + + e.getClass() + " instead of expected SecurityException"); + errorCount++; + } + } else { + System.out.println("ClientSide::doSetRequest: (ERROR) Set failed"); + errorCount++; + } + } + return errorCount; + } + + protected int doInvokeRequest(MBeanServerConnection mbsc, + ObjectName on, + boolean expectedException) { + int errorCount = 0; + + try { + Utils.debug(Utils.DEBUG_STANDARD, + "ClientSide::doInvokeRequest: Invoke operations on the MBean") ; + + mbsc.invoke(on, "operation", null, null) ; + + if (expectedException) { + System.out.println("ClientSide::doInvokeRequest: " + + "(ERROR) Invoke did not fail with expected SecurityException"); + errorCount++; + } else { + System.out.println("ClientSide::doInvokeRequest: (OK) Invoke succeed") ; + } + } catch(Exception e) { + Utils.printThrowable(e, true) ; + if (expectedException) { + if (e instanceof java.lang.SecurityException) { + System.out.println("ClientSide::doInvokeRequest: " + + "(OK) Invoke failed with expected SecurityException") ; + } else { + System.out.println("ClientSide::doInvokeRequest: " + + " (ERROR) Invoke failed with " + + e.getClass() + " instead of expected SecurityException"); + errorCount++; + } + } else { + System.out.println("ClientSide::doInvokeRequest: " + + "(ERROR) Invoke failed"); + errorCount++; + } + } + return errorCount; + } + + } +} diff --git a/jdk/test/javax/management/security/MBS_Light.java b/jdk/test/javax/management/security/MBS_Light.java new file mode 100644 index 00000000000..1ed459eebcb --- /dev/null +++ b/jdk/test/javax/management/security/MBS_Light.java @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.security.AccessControlContext; +import java.security.AccessController; +import javax.security.auth.Subject; +import java.security.Principal; +import java.util.Iterator; +import java.util.Set; + +import javax.management.MBeanRegistration ; +import javax.management.MBeanServer ; +import javax.management.ObjectName ; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationListener; +import javax.management.Notification; + +public class MBS_Light extends NotificationBroadcasterSupport + implements MBS_LightMBean, MBeanRegistration, NotificationListener +{ + private RjmxMBeanParameter param = null ; + private String aString = "notset" ; + private int anInt = 0 ; + private MBeanServer mbs = null ; + private ObjectName objname = null ; + private Exception anException = null ; + private Error anError = null ; + private int count = 0; + private SimpleListener listener = new SimpleListener(); + + @SqeDescriptorKey("NO PARAMETER CONSTRUCTOR MBS_Light") + public MBS_Light() { + } + + @SqeDescriptorKey("ONE RjmxMBeanParameter PARAMETER CONSTRUCTOR MBS_Light") + public MBS_Light(@SqeDescriptorKey("CONSTRUCTOR PARAMETER param") + RjmxMBeanParameter param) { + this.param = param ; + } + + @SqeDescriptorKey("ONE String PARAMETER CONSTRUCTOR MBS_Light") + public MBS_Light(@SqeDescriptorKey("CONSTRUCTOR PARAMETER param")String param) { + this.aString = param ; + } + + // Getter for property param + public RjmxMBeanParameter getParam() { + return this.param ; + } + + // Setter for property param + public void setParam(RjmxMBeanParameter param) { + this.param = param ; + } + + // Getter for property aString + public String getAstring() { + return this.aString ; + } + + // Setter for property aString + public void setAstring(String aString) { + this.aString = aString ; + } + + // Getter for property anInt + public int getAnInt() { + return this.anInt ; + } + + // Setter for property anInt + public void setAnInt(int anInt) { + this.anInt = anInt ; + } + + // Getter for property anException + public Exception getAnException() { + return this.anException ; + } + + // Setter for property anException + public void setAnException(Exception anException) { + this.anException = anException ; + } + + // Getter for property anError + public Error getAnError() { + return this.anError ; + } + + // Setter for property anError + public void setAnError(Error anError) { + this.anError = anError ; + } + + // An operation + public RjmxMBeanParameter operate1(String name) { + return new RjmxMBeanParameter(name) ; + } + + // An operation + public String operate2(RjmxMBeanParameter param) { + return param.name ; + } + + // An operation + public void throwError() { + throw new Error("JSR-160-ERROR"); + } + + // An operation + public void throwException() throws Exception { + throw new Exception("JSR-160-EXCEPTION"); + } + + // MBeanRegistration method + public void postDeregister() { + } + + // MBeanRegistration method + public void postRegister(Boolean registrationDone) { + } + + // MBeanRegistration method + public void preDeregister() + throws Exception + { + } + + // MBeanRegistration method + public ObjectName preRegister(MBeanServer server, ObjectName name) + throws Exception + { + this.mbs = server ; + if ( name == null ) { + this.objname = new ObjectName("protocol:class=MBS_Light") ; + } + else { + this.objname = name ; + } + return this.objname ; + } + + public synchronized void handleNotification(Notification notification, + Object handback) { + Utils.debug(Utils.DEBUG_STANDARD, + "MBS_Light::handleNotification: " + notification); + listener.handleNotification(notification, handback); + } + + // Send a notification + public void sendNotification() { + Notification notification = + new Notification("JSR160-TCK-NOTIFICATION", this, count++); + sendNotification(notification); + } + + public Object waitForNotificationHB() { + return listener.waitForNotificationHB(); + } + + // Receive multi notifications and send back handbacks + public synchronized Object[] waitForMultiNotifications(String nb) { + return listener.waitForMultiNotifications(Integer.valueOf(nb).intValue()); + } + + // Receive a notification + public synchronized String waitForNotification() { + return listener.waitForNotification(); + } + + // Is the notification received + public synchronized Boolean notificationReceived() { + return Boolean.valueOf(listener.isNotificationReceived()); + } + + // The authorization Id + public String getAuthorizationId() { + AccessControlContext acc = AccessController.getContext(); + Subject subject = Subject.getSubject(acc); + Set principals = subject.getPrincipals(); + Iterator i = principals.iterator(); + StringBuffer buffer = new StringBuffer(); + while(i.hasNext()) { + Principal p = i.next(); + buffer.append(p.getName()); + if(i.hasNext()) + buffer.append(" "); + } + + return buffer.toString(); + } +} diff --git a/jdk/test/javax/management/security/MBS_LightMBean.java b/jdk/test/javax/management/security/MBS_LightMBean.java new file mode 100644 index 00000000000..a380af9dcff --- /dev/null +++ b/jdk/test/javax/management/security/MBS_LightMBean.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +@SqeDescriptorKey("INTERFACE MBS_LightMBean") +public interface MBS_LightMBean { + // Getter for property param + @SqeDescriptorKey("ATTRIBUTE Param") + public RjmxMBeanParameter getParam() ; + + // Setter for property param + @SqeDescriptorKey("ATTRIBUTE Param") + public void setParam(RjmxMBeanParameter param) ; + + // Getter for property aString + @SqeDescriptorKey("ATTRIBUTE Astring") + public String getAstring() ; + + // Setter for property aString + @SqeDescriptorKey("ATTRIBUTE Astring") + public void setAstring(String aString) ; + + // Getter for property anInt + @SqeDescriptorKey("ATTRIBUTE AnInt") + public int getAnInt() ; + + // Setter for property anInt + @SqeDescriptorKey("ATTRIBUTE AnInt") + public void setAnInt(int anInt) ; + + // Getter for property anException + @SqeDescriptorKey("ATTRIBUTE AnException") + public Exception getAnException() ; + + // Setter for property anException + @SqeDescriptorKey("ATTRIBUTE AnException") + public void setAnException(Exception anException) ; + + // Getter for property anError + @SqeDescriptorKey("ATTRIBUTE AnError") + public Error getAnError() ; + + // Setter for property anError + @SqeDescriptorKey("ATTRIBUTE AnError") + public void setAnError(Error anError) ; + + // An operation + @SqeDescriptorKey("OPERATION operate1") + public RjmxMBeanParameter operate1( + @SqeDescriptorKey("OPERATION PARAMETER name")String name) ; + + // An operation + @SqeDescriptorKey("OPERATION operate2") + public String operate2( + @SqeDescriptorKey("OPERATION PARAMETER param")RjmxMBeanParameter param) ; + + // Throws an error + @SqeDescriptorKey("OPERATION throwError") + public void throwError(); + + // Throws an exception + @SqeDescriptorKey("OPERATION throwException") + public void throwException() throws Exception; + + // Send a notification + @SqeDescriptorKey("OPERATION sendNotification") + public void sendNotification(); + + // Receive a notification and return the type + @SqeDescriptorKey("OPERATION waitForNotification") + public String waitForNotification(); + + // Receive a notification and return the HandBack + @SqeDescriptorKey("OPERATION waitForNotificationHB") + public Object waitForNotificationHB(); + + // Receive multi notifications and return the HandBacks + @SqeDescriptorKey("OPERATION waitForMultiNotifications") + public Object[] waitForMultiNotifications( + @SqeDescriptorKey("OPERATION PARAMETER nb")String nb); + + // Is the notification received + @SqeDescriptorKey("OPERATION notificationReceived") + public Boolean notificationReceived(); + + // Return the current authorization Id + @SqeDescriptorKey("OPERATION getAuthorizationId") + public String getAuthorizationId(); +} diff --git a/jdk/test/javax/management/security/RjmxMBeanParameter.java b/jdk/test/javax/management/security/RjmxMBeanParameter.java new file mode 100644 index 00000000000..c96e9a12e97 --- /dev/null +++ b/jdk/test/javax/management/security/RjmxMBeanParameter.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.Serializable ; + +/** + * That class is used to modelize a parameter to be used as MBean property + * value or MBean operation parameter or returned value. + */ +public class RjmxMBeanParameter implements Serializable { + public String name = "unset" ; + + public RjmxMBeanParameter() { + } + + public RjmxMBeanParameter(String name) { + this.name = name ; + } + + public boolean equals(Object obj) { + if ( this.name.equals(((RjmxMBeanParameter)obj).name) ) { + return true ; + } else { + return false ; + } + } +} diff --git a/jdk/test/javax/management/security/SecurityTest.java b/jdk/test/javax/management/security/SecurityTest.java new file mode 100644 index 00000000000..6d245bd6ab4 --- /dev/null +++ b/jdk/test/javax/management/security/SecurityTest.java @@ -0,0 +1,800 @@ +/* + * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8058865 + * @summary Checks various secure ways of connecting from remote jmx client + * @author Olivier Lagneau + * @modules java.management + * @library /lib/testlibrary + * @compile MBS_Light.java ServerDelegate.java TestSampleLoginModule.java + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dusername=SQE_username -Dpassword=SQE_password SecurityTest -server -mapType x.password.file -client -mapType credentials + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dusername=UNKNOWN_username -Dpassword=SQE_password SecurityTest -server -mapType x.password.file -client -mapType credentials -expectedThrowable java.lang.SecurityException + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dusername=SQE_username -Dpassword=WRONG_password SecurityTest -server -mapType x.password.file -client -mapType credentials -expectedThrowable java.lang.SecurityException + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dsusername=TestJMXAuthenticatorUsername -Dspassword=TestJMXAuthenticatorPassword -Dusername=TestJMXAuthenticatorUsername -Dpassword=TestJMXAuthenticatorPassword SecurityTest -server -mapType x.authenticator -client -mapType credentials + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dsusername=TestJMXAuthenticatorUsername -Dspassword=TestJMXAuthenticatorPassword -Dusername=AnotherTestJMXAuthenticatorUsername -Dpassword=TestJMXAuthenticatorPassword SecurityTest -server -mapType x.authenticator -client -mapType credentials -expectedThrowable java.lang.SecurityException + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dlogin.config.file=${test.src}/login.config -Dpassword.file=password.properties -Dusername=usernameFileLoginModule -Dpassword=passwordFileLoginModule SecurityTest -server -mapType x.login.config.PasswordFileAuthentication -client -mapType credentials + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dlogin.config.file=${test.src}/login.config.UNKNOWN -Dpassword.file=password.properties -Dusername=usernameFileLoginModule -Dpassword=passwordFileLoginModule SecurityTest -server -mapType x.login.config.PasswordFileAuthentication -client -mapType credentialss -expectedThrowable java.lang.SecurityException + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dlogin.config.file=${test.src}/login.config -Dpassword.file=password.properties -Dusername=usernameFileLoginModule -Dpassword=passwordFileLoginModule SecurityTest -server -mapType x.login.config.UnknownAuthentication -client -mapType credentials -expectedThrowable java.lang.SecurityException + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dlogin.config.file=${test.src}/login.config -Dsusername=usernameSampleLoginModule -Dspassword=passwordSampleLoginModule -Dpassword.file=password.properties -Dusername=usernameSampleLoginModule -Dpassword=passwordSampleLoginModule SecurityTest -server -mapType x.login.config.SampleLoginModule -client -mapType credentials + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dlogin.config.file=${test.src}/login.config -Dsusername=usernameSampleLoginModule -Dspassword=passwordSampleLoginModule -Dpassword.file=password.properties -Dusername=AnotherUsernameSampleLoginModule -Dpassword=passwordSampleLoginModule SecurityTest -server -mapType x.login.config.SampleLoginModule -client -mapType credentials -expectedThrowable java.lang.SecurityException + * @run main/othervm/timeout=300 -DDEBUG_STANDARD SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop + * @run main/othervm/timeout=300 -DDEBUG_STANDARD SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword WRONG_password -expectedThrowable java.io.IOException + * @run main/othervm/timeout=300 -DDEBUG_STANDARD SecurityTest -server -mapType rmi.server.socket.factory.ssl -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop -expectedThrowable java.io.IOException + * @run main/othervm/timeout=300 -DDEBUG_STANDARD SecurityTest -server -mapType rmi.client.socket.factory.ssl -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop -expectedThrowable java.io.IOException + * @run main/othervm/timeout=300 -DDEBUG_STANDARD SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl -keystore keystoreAgent -keystorepassword glopglop -client -expectedThrowable java.io.IOException + * @run main/othervm/timeout=300 -DDEBUG_STANDARD SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.need.client.authentication -keystore keystoreAgent -keystorepassword glopglop -truststore truststoreAgent -truststorepassword glopglop -client -keystore keystoreClient -keystorepassword glopglop -truststore truststoreClient -truststorepassword glopglop + * @run main/othervm/timeout=300 -DDEBUG_STANDARD SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.need.client.authentication -keystore keystoreAgent -keystorepassword glopglop -truststore truststoreAgent -truststorepassword glopglop -client -keystore keystoreClient -keystorepassword WRONG_password -truststore truststoreClient -truststorepassword glopglop -expectedThrowable java.io.IOException + * @run main/othervm/timeout=300 -DDEBUG_STANDARD SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.need.client.authentication -keystore keystoreAgent -keystorepassword glopglop -truststore truststoreAgent -truststorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop -expectedThrowable java.io.IOException + * @run main/othervm/timeout=300 -DDEBUG_STANDARD SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.need.client.authentication -keystore keystoreAgent -keystorepassword glopglop -client -keystore keystoreClient -keystorepassword glopglop -truststore truststoreClient -truststorepassword glopglop -expectedThrowable java.io.IOException + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Djavax.rmi.ssl.client.enabledCipherSuites=SSL_RSA_WITH_RC4_128_MD5 SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.enabled.cipher.suites.md5 -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Djavax.rmi.ssl.client.enabledCipherSuites=SSL_RSA_WITH_RC4_128_SHA SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.enabled.cipher.suites.md5 -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop -expectedThrowable java.io.IOException + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Djavax.rmi.ssl.client.enabledCipherSuites=SSL_RSA_WITH_RC4_128_MD5 SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.enabled.cipher.suites.sha -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop -expectedThrowable java.io.IOException + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Djavax.rmi.ssl.client.enabledProtocols=SSLv3 SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.enabled.protocols.sslv3 -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Djavax.rmi.ssl.client.enabledProtocols=TLSv1 SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.enabled.protocols.sslv3 -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop -expectedThrowable java.io.IOException + * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Djavax.rmi.ssl.client.enabledProtocols=SSLv3 SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.enabled.protocols.tlsv1 -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop -expectedThrowable java.io.IOException + */ + +import java.io.File; +import java.util.Map ; +import java.util.HashMap ; +import java.util.List; +import java.util.ArrayList; +import java.util.Arrays; + +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory ; +import javax.management.MBeanServerConnection; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; + +import javax.management.Attribute ; +import javax.management.ObjectName ; + +import javax.rmi.ssl.SslRMIClientSocketFactory; +import javax.rmi.ssl.SslRMIServerSocketFactory; + +import java.security.Security; + +import jdk.testlibrary.ProcessTools; +import jdk.testlibrary.JDKToolFinder; + +public class SecurityTest { + + static final String SERVER_CLASS_NAME = "SecurityTest"; + static final String CLIENT_CLASS_NAME = "SecurityTest$ClientSide"; + static final String CLIENT_CLASS_MAIN = CLIENT_CLASS_NAME; + + static final String USERNAME_PROPERTY = "username"; + static final String PASSWORD_PROPERTY = "password"; + + static final String SERVER_DELEGATE_MBEAN_NAME = + "defaultDomain:class=ServerDelegate"; + + static final String RMI_SERVER_SOCKET_FACTORY_SSL = "rmi.server.socket.factory.ssl"; + static final String RMI_CLIENT_SOCKET_FACTORY_SSL = "rmi.client.socket.factory.ssl"; + static final String KEYSTORE_PROPNAME = "javax.net.ssl.keyStore"; + static final String KEYSTORE_PWD_PROPNAME = "javax.net.ssl.keyStorePassword"; + static final String TRUSTSTORE_PROPNAME = "javax.net.ssl.trustStore"; + static final String TRUSTSTORE_PWD_PROPNAME = "javax.net.ssl.trustStorePassword"; + + static final String RMI_SSL_CLIENT_ENABLEDCIPHERSUITES = + "javax.rmi.ssl.client.enabledCipherSuites"; + static final String RMI_SSL_CLIENT_ENABLEDPROTOCOLS = + "javax.rmi.ssl.client.enabledProtocols"; + + private JMXConnectorServer cs; + + // Construct and set keyStore properties from given map + static void setKeyStoreProperties(Map map) { + + String keyStore = (String) map.get("-keystore"); + keyStore = buildSourcePath(keyStore); + System.setProperty(KEYSTORE_PROPNAME, keyStore); + System.out.println("keyStore location = \"" + keyStore + "\""); + + String password = (String) map.get("-keystorepassword"); + System.setProperty(KEYSTORE_PWD_PROPNAME, password); + System.out.println("keyStore password = " + password); + + } + + // Construct and set trustStore properties from given map + static void setTrustStoreProperties(Map map) { + + String trustStore = (String) map.get("-truststore"); + trustStore = buildSourcePath(trustStore); + System.setProperty(TRUSTSTORE_PROPNAME, trustStore); + System.out.println("trustStore location = \"" + trustStore + "\""); + + String password = (String) map.get("-truststorepassword"); + System.setProperty(TRUSTSTORE_PWD_PROPNAME, password); + System.out.println("trustStore password = " + password); + + } + + /* + * First Debug properties and arguments are collect in expected + * map (argName, value) format, then calls original test's run method. + */ + public static void main(String args[]) throws Exception { + + System.out.println("================================================="); + + // Parses parameters + Utils.parseDebugProperties(); + + // Supported parameters list format is : + // "MainClass [-server ...] [-client ...] + // with either "-parami valuei" or "-parami" + HashMap serverMap = new HashMap<>() ; + int clientArgsIndex = + Utils.parseServerParameters(args, SERVER_CLASS_NAME, serverMap); + + // Extract and records client params + String[] clientParams = null; + if (clientArgsIndex < args.length) { + int clientParamsSize = args.length - clientArgsIndex; + clientParams = new String[clientParamsSize]; + System.arraycopy(args, clientArgsIndex, clientParams, 0, clientParamsSize); + } else { + clientParams = new String[0]; + } + + // Run test + SecurityTest test = new SecurityTest(); + test.run(serverMap, clientParams); + + } + + // Return full path of filename in the test sopurce directory + private static String buildSourcePath(String filename) { + return System.getProperty("test.src") + File.separator + filename; + } + + /* + * Collects security run params for server side. + */ + private HashMap setServerSecurityEnv(Map map) + throws Exception { + + // Creates Authentication environment from server side params + HashMap env = new HashMap<>(); + + // Retrieve and set keystore and truststore config if any + if (map.containsKey("-keystore") && + map.get("-keystore") != null) { + setKeyStoreProperties(map); + } + System.out.println("Done keystore properties"); + + if (map.containsKey("-truststore") && + map.get("-truststore") != null) { + setTrustStoreProperties(map); + } + System.out.println("Done truststore properties"); + + String value = null; + if ((value = (String)map.get("-mapType")) != null) { + + // Case of remote password file with all authorized credentials + if (value.contains("x.password.file")) { + String passwordFileStr = buildSourcePath("password.properties"); + env.put("jmx.remote.x.password.file", passwordFileStr); + System.out.println("Added " + passwordFileStr + + " file as jmx.remote.x.password.file"); + } + + // Case of dedicated authenticator class : TestJMXAuthenticator + if (value.contains("x.authenticator")) { + env.put("jmx.remote.authenticator", new TestJMXAuthenticator()) ; + System.out.println( + "Added \"jmx.remote.authenticator\" = TestJMXAuthenticator"); + } + + // Case of security config file with standard Authentication + if (value.contains("x.login.config.PasswordFileAuthentication")) { + String loginConfig = System.getProperty("login.config.file"); + + // Override the default JAAS configuration + System.setProperty("java.security.auth.login.config", + "file:" + loginConfig); + System.out.println("Overrided default JAAS configuration with " + + "\"java.security.auth.login.config\" = \"" + loginConfig + "\"") ; + + env.put("jmx.remote.x.login.config", "PasswordFileAuthentication") ; + System.out.println( + "Added \"jmx.remote.x.login.config\" = " + + "\"PasswordFileAuthentication\"") ; + + // redirects "password.file" property to file in ${test.src} + String passwordFileStr = + buildSourcePath(System.getProperty("password.file")); + System.setProperty("password.file", passwordFileStr); + System.out.println( + "Redirected \"password.file\" property value to = " + + passwordFileStr) ; + } + + // Case of security config file with unexisting athentication config + if (value.contains("x.login.config.UnknownAuthentication")) { + String loginConfig = System.getProperty("login.config.file"); + + // Override the default JAAS configuration + System.setProperty("java.security.auth.login.config", + "file:" + loginConfig); + System.out.println("Overrided default JAAS configuration with " + + "\"java.security.auth.login.config\" = \"" + loginConfig + "\"") ; + + env.put("jmx.remote.x.login.config", "UnknownAuthentication") ; + System.out.println( + "Added \"jmx.remote.x.login.config\" = " + + "\"UnknownAuthentication\"") ; + + // redirects "password.file" property to file in ${test.src} + String passwordFileStr = + buildSourcePath(System.getProperty("password.file")); + System.setProperty("password.file", passwordFileStr); + System.out.println( + "Redirected \"password.file\" property value to = " + + passwordFileStr) ; + } + + // Case of security config file with dedicated login module + if (value.contains("x.login.config.SampleLoginModule")) { + String loginConfig = System.getProperty("login.config.file"); + + // Override the default JAAS configuration + System.setProperty("java.security.auth.login.config", + "file:" + loginConfig); + System.out.println("Overrided default JAAS configuration with " + + "\"java.security.auth.login.config\" = \"" + loginConfig + "\"") ; + + env.put("jmx.remote.x.login.config", "SampleLoginModule") ; + System.out.println( + "Added \"jmx.remote.x.login.config\" = " + + "\"SampleLoginModule\"") ; + } + + // Simple rmi ssl authentication + if (value.contains(RMI_CLIENT_SOCKET_FACTORY_SSL)) { + env.put("jmx.remote.rmi.client.socket.factory", + new SslRMIClientSocketFactory()) ; + System.out.println( + "Added \"jmx.remote.rmi.client.socket.factory\"" + + " = SslRMIClientSocketFactory") ; + } + + if (value.contains(RMI_SERVER_SOCKET_FACTORY_SSL)) { + if (value.contains( + "rmi.server.socket.factory.ssl.need.client.authentication")) { + // rmi ssl authentication with client authentication + env.put("jmx.remote.rmi.server.socket.factory", + new SslRMIServerSocketFactory(null, null, true)) ; + System.out.println( + "Added \"jmx.remote.rmi.server.socket.factory\"" + + " = SslRMIServerSocketFactory with client authentication") ; + + } else if (value.contains("rmi.server.socket.factory.ssl.enabled.cipher.suites.md5")) { + // Allows all ciphering and protocols for testing purpose + Security.setProperty("jdk.tls.disabledAlgorithms", ""); + + env.put("jmx.remote.rmi.server.socket.factory", + new SslRMIServerSocketFactory( + new String[] {"SSL_RSA_WITH_RC4_128_MD5"}, null, false)); + System.out.println( + "Added \"jmx.remote.rmi.server.socket.factory\"" + + " = SslRMIServerSocketFactory with SSL_RSA_WITH_RC4_128_MD5 cipher suite"); + + } else if (value.contains("rmi.server.socket.factory.ssl.enabled.cipher.suites.sha")) { + // Allows all ciphering and protocols for testing purpose + Security.setProperty("jdk.tls.disabledAlgorithms", ""); + + env.put("jmx.remote.rmi.server.socket.factory", + new SslRMIServerSocketFactory( + new String[] { "SSL_RSA_WITH_RC4_128_SHA" }, null, false)) ; + System.out.println( + "Added \"jmx.remote.rmi.server.socket.factory\"" + + " = SslRMIServerSocketFactory with SSL_RSA_WITH_RC4_128_SHA cipher suite") ; + + } else if (value.contains("rmi.server.socket.factory.ssl.enabled.protocols.sslv3")) { + // Allows all ciphering and protocols for testing purpose + Security.setProperty("jdk.tls.disabledAlgorithms", ""); + + env.put("jmx.remote.rmi.server.socket.factory", + new SslRMIServerSocketFactory(null, new String[] {"SSLv3"}, false)) ; + System.out.println( + "Added \"jmx.remote.rmi.server.socket.factory\"" + + " = SslRMIServerSocketFactory with SSLv3 protocol") ; + + } else if (value.contains("rmi.server.socket.factory.ssl.enabled.protocols.tlsv1")) { + // Allows all ciphering and protocols for testing purpose + Security.setProperty("jdk.tls.disabledAlgorithms", ""); + + env.put("jmx.remote.rmi.server.socket.factory", + new SslRMIServerSocketFactory(null, new String[] {"TLSv1"}, false)) ; + System.out.println( + "Added \"jmx.remote.rmi.server.socket.factory\"" + + " = SslRMIServerSocketFactory with TLSv1 protocol") ; + + } else { + env.put("jmx.remote.rmi.server.socket.factory", + new SslRMIServerSocketFactory()); + System.out.println( + "Added \"jmx.remote.rmi.server.socket.factory\"" + + " = SslRMIServerSocketFactory"); + } + } + } + + return env; + } + + /* + * Create the MBeansServer side of the test and returns its address + */ + private JMXServiceURL createServerSide(Map serverMap) + throws Exception { + final int NINETY_SECONDS = 90; + + System.out.println("SecurityTest::createServerSide: Start") ; + + // Prepare server side security env + HashMap env = setServerSecurityEnv(serverMap); + + // Create and start mbean server and connector server + MBeanServer mbs = MBeanServerFactory.newMBeanServer(); + JMXServiceURL url = new JMXServiceURL("rmi", null, 0); + cs = JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs); + cs.start(); + + // Waits availibility of connector server + Utils.waitReady(cs, NINETY_SECONDS); + + JMXServiceURL addr = cs.getAddress(); + + System.out.println("SecurityTest::createServerSide: Done.") ; + + return addr; + } + + /* + * Creating command-line for running subprocess JVM: + * + * JVM command line is like: + * {test_jdk}/bin/java {defaultopts} -cp {test.class.path} {testopts} main + * + * {defaultopts} are the default java options set by the framework. + * + */ + private List buildCommandLine(String args[]) { + + System.out.println("SecurityTest::buildCommandLine: Start") ; + + List opts = new ArrayList<>(); + opts.add(JDKToolFinder.getJDKTool("java")); + opts.addAll(Arrays.asList(jdk.testlibrary.Utils.getTestJavaOpts())); + + // We need to forward some properties to the client side + opts.add("-Dtest.src=" + System.getProperty("test.src")); + + String usernameValue = System.getProperty(USERNAME_PROPERTY); + if (usernameValue != null) { + System.out.println("SecurityTest::buildCommandLine: "+ + " forward username property to client side"); + opts.add("-D" + USERNAME_PROPERTY + "=" + usernameValue); + } + String passwordValue = System.getProperty(PASSWORD_PROPERTY); + if (passwordValue != null) { + System.out.println("SecurityTest::buildCommandLine: "+ + " forward password property to client side"); + opts.add("-D" + PASSWORD_PROPERTY + "=" + passwordValue); + } + + String enabledCipherSuites = + System.getProperty(RMI_SSL_CLIENT_ENABLEDCIPHERSUITES); + if (enabledCipherSuites != null) { + System.out.println("SecurityTest::buildCommandLine: "+ + " forward enabledCipherSuites property to client side"); + opts.add("-D" + RMI_SSL_CLIENT_ENABLEDCIPHERSUITES + + "=" + enabledCipherSuites); + } + + String enabledProtocols = + System.getProperty(RMI_SSL_CLIENT_ENABLEDPROTOCOLS); + if (enabledProtocols != null) { + System.out.println("SecurityTest::buildCommandLine: "+ + " forward enabledProtocols property to client side"); + opts.add("-D" + RMI_SSL_CLIENT_ENABLEDPROTOCOLS + + "=" + enabledProtocols); + } + + opts.add("-cp"); + opts.add(System.getProperty("test.class.path", "test.class.path")); + opts.add(CLIENT_CLASS_MAIN); + opts.addAll(Arrays.asList(args)); + + System.out.println("SecurityTest::buildCommandLine: Done.") ; + + return opts; + } + + /** + * Runs SecurityTest$ClientSide with the passed options and redirects + * subprocess standard I/O to the current (parent) process. This provides a + * trace of what happens in the subprocess while it is runnning (and before + * it terminates). + * + * @param serviceUrlStr string representing the JMX service Url to connect to. + */ + private int runClientSide(String args[], String serviceUrlStr) throws Exception { + + System.out.println("SecurityTest::runClientSide: Start") ; + + // Building command-line + List opts = buildCommandLine(args); + opts.add("-serviceUrl"); + opts.add(serviceUrlStr); + + // Launch separate JVM subprocess + int exitCode = 0; + String[] optsArray = opts.toArray(new String[0]); + ProcessBuilder pb = new ProcessBuilder(optsArray); + Process p = ProcessTools.startProcess("SecurityTest$ClientSide", pb); + + // Handling end of subprocess + try { + exitCode = p.waitFor(); + if (exitCode != 0) { + System.out.println( + "Subprocess unexpected exit value of [" + exitCode + + "]. Expected 0.\n"); + } + } catch (InterruptedException e) { + System.out.println("Parent process interrupted with exception : \n " + e + " :" ); + + // Parent thread unknown state, killing subprocess. + p.destroyForcibly(); + + throw new RuntimeException( + "Parent process interrupted with exception : \n " + e + " :" ); + + } finally { + if (p.isAlive()) { + p.destroyForcibly(); + } + + System.out.println("SecurityTest::runClientSide: Done") ; + + return exitCode; + } + + } + + public void run(Map serverArgs, String clientArgs[]) { + + System.out.println("SecurityTest::run: Start") ; + int errorCount = 0; + + try { + // Initialise the server side + JMXServiceURL urlToUse = createServerSide(serverArgs); + + // Run client side + errorCount = runClientSide(clientArgs, urlToUse.toString()); + + if ( errorCount == 0 ) { + System.out.println("SecurityTest::run: Done without any error") ; + } else { + System.out.println( + "SecurityTest::run: Done with " + errorCount + " error(s)"); + throw new RuntimeException("errorCount = " + errorCount); + } + + cs.stop(); + + } catch(Exception e) { + throw new RuntimeException(e); + } + + } + + private static class ClientSide { + + private JMXConnector cc = null; + private MBeanServerConnection mbsc = null; + + public static void main(String args[]) throws Exception { + + // Parses parameters + Utils.parseDebugProperties(); + + // Supported parameters list format is : "MainClass [-client ...] + // with either "-parami valuei" or "-parami" + HashMap clientMap = new HashMap<>() ; + Utils.parseClientParameters(args, CLIENT_CLASS_NAME, clientMap); + + // Run test + ClientSide test = new ClientSide(); + test.run(clientMap); + } + + public void run(Map args) { + + System.out.println("ClientSide::run: Start"); + int errorCount = 0; + + try { + // Setup client side parameters + HashMap env = new HashMap<>(); + + // If needed allows all ciphering and protocols for testing purpose + if (System.getProperty(RMI_SSL_CLIENT_ENABLEDCIPHERSUITES) != null) { + Security.setProperty("jdk.tls.disabledAlgorithms", ""); + } + + // If needed allows all ciphering and protocols for testing purpose + if (System.getProperty(RMI_SSL_CLIENT_ENABLEDPROTOCOLS) != null) { + Security.setProperty("jdk.tls.disabledAlgorithms", ""); + } + + // Retrieve and set keystore and truststore config if any + if (args.containsKey("-keystore") && + args.get("-keystore") != null) { + SecurityTest.setKeyStoreProperties(args); + } + if (args.containsKey("-truststore") && + args.get("-truststore") != null) { + SecurityTest.setTrustStoreProperties(args); + } + + Object value = args.get("-mapType"); + if ((value != null) && + value.equals("credentials")) { + String username = System.getProperty("username"); + String password = System.getProperty("password"); + Utils.debug(Utils.DEBUG_STANDARD, + "add \"jmx.remote.credentials\" = \"" + + username + "\", \"" + password + "\""); + env.put("jmx.remote.credentials", + new String[] { username , password }); + } + + String expectedThrowable = (String) args.get("-expectedThrowable"); + + String authCallCountName = "-expectedAuthenticatorCallCount"; + int authCallCountValue = 0; + if (args.containsKey(authCallCountName)) { + authCallCountValue = + (new Integer((String) args.get(authCallCountName))).intValue(); + } + + try { + // Get a connection to remote mbean server + JMXServiceURL addr = new JMXServiceURL((String)args.get("-serviceUrl")); + cc = JMXConnectorFactory.connect(addr,env); + mbsc = cc.getMBeanServerConnection(); + + // In case we should have got an exception + if (expectedThrowable != null) { + System.out.println("ClientSide::run: (ERROR) " + + " Connect did not fail with expected " + expectedThrowable); + errorCount++; + } else { + System.out.println("ClientSide::run: (OK) Connect succeed"); + } + } catch (Throwable e) { + Utils.printThrowable(e, true); + if (expectedThrowable != null) { + if (Utils.compareThrowable(e, expectedThrowable)) { + System.out.println("ClientSide::run: (OK) " + + "Connect failed with expected " + expectedThrowable); + } else { + System.out.println("ClientSide::run: (ERROR) Connect failed with " + + e.getClass() + " instead of expected " + + expectedThrowable); + errorCount++; + } + } else { + System.out.println("ClientSide::run: (ERROR) " + + "Connect failed with exception"); + errorCount++; + } + } + + // Depending on the client state, + // perform some requests + if (mbsc != null && errorCount == 0) { + // Perform some little JMX requests + System.out.println("ClientSide::run: Start sending requests"); + + doRequests(); + + // In case authentication has been used we check how it did. + if (authCallCountValue != 0) { + errorCount += checkAuthenticator(mbsc, authCallCountValue); + } + } + } catch (Exception e) { + Utils.printThrowable(e, true); + errorCount++; + } finally { + // Terminate the JMX Client if any + if (cc != null) { + try { + cc.close(); + } catch (Exception e) { + Utils.printThrowable(e, true) ; + errorCount++; + } + } + } + + System.out.println("ClientSide::run: Done"); + + // Handle result + if (errorCount != 0) { + throw new RuntimeException(); + } + } + + private void doRequests() throws Exception { + + // Send some requests to the remote JMX server + ObjectName objName1 = + new ObjectName("TestDomain:class=MBS_Light,rank=1"); + String mbeanClass = "MBS_Light"; + Exception exception = new Exception("MY TEST EXCEPTION"); + Attribute attException = new Attribute("AnException", exception); + Error error = new Error("MY TEST ERROR"); + Attribute attError = new Attribute("AnError", error); + String opParamString = "TOTORO"; + RjmxMBeanParameter opParam = new RjmxMBeanParameter(opParamString); + Object[] params1 = {opParamString}; + String[] sig1 = {"java.lang.String"}; + Object[] params2 = {opParam}; + String[] sig2 = {"RjmxMBeanParameter"}; + + // Create and register the MBean + Utils.debug(Utils.DEBUG_STANDARD, + "ClientSide::doRequests: Create and register the MBean"); + mbsc.createMBean(mbeanClass, objName1); + if (!mbsc.isRegistered(objName1)) { + throw new Exception("Unable to register an MBean"); + } + + // Set attributes of the MBean + Utils.debug(Utils.DEBUG_STANDARD, + "ClientSide::doRequests: Set attributes of the MBean"); + mbsc.setAttribute(objName1, attException); + mbsc.setAttribute(objName1, attError); + + // Get attributes of the MBean + Utils.debug(Utils.DEBUG_STANDARD, + "ClientSide::doRequests: Get attributes of the MBean"); + Exception retException = + (Exception) mbsc.getAttribute(objName1,"AnException"); + if (!retException.getMessage().equals(exception.getMessage())) { + System.out.println("Expected = " + exception); + System.out.println("Got = " + retException); + throw new Exception("Attribute AnException not as expected"); + } + Error retError = (Error) mbsc.getAttribute(objName1, "AnError"); + if (!retError.getMessage().equals(error.getMessage())) { + System.out.println("Expected = " + error); + System.out.println("Got = " + retError); + throw new Exception("Attribute AnError not as expected"); + } + + // Invoke operations on the MBean + Utils.debug(Utils.DEBUG_STANDARD, + "ClientSide::doRequests: Invoke operations on the MBean"); + RjmxMBeanParameter res1 = + (RjmxMBeanParameter) mbsc.invoke(objName1, "operate1", params1, sig1); + if (!res1.equals(opParam)) { + System.out.println("Expected = " + opParam); + System.out.println("Got = " + res1); + throw new Exception("Operation operate1 behaved badly"); + } + String res2 = + (String) mbsc.invoke(objName1, "operate2", params2, sig2); + if (!res2.equals(opParamString)) { + System.out.println("Expected = " + opParamString); + System.out.println("Got = " + res2); + throw new Exception("Operation operate2 behaved badly"); + } + + // Unregister the MBean + Utils.debug(Utils.DEBUG_STANDARD, + "ClientSide::doRequests: Unregister the MBean"); + mbsc.unregisterMBean(objName1); + if (mbsc.isRegistered(objName1)) { + throw new Exception("Unable to unregister an MBean"); + } + } + + /** + * Make some check about the instance of TestJMXAuthenticator. + * The authenticator is supposed to have set some properties on + * a ServerDelegate MBean. + * We compare the number of times it has been called with the expected value. + * We also check the Principal that has been given to the authenticator + * was not null. + * That method is of use to authentication with the JSR 262. + * @param mbs + * @param expectedAuthenticatorCallCount + * @return The number of errors encountered. + * @throws java.lang.Exception + */ + protected int checkAuthenticator(MBeanServerConnection mbs, + int expectedAuthenticatorCallCount) throws Exception { + int errorCount = 0; + + // Ensure the authenticator has been called the right number + // of times. + int callCount = + ((Integer) mbs.getAttribute( + new ObjectName(SERVER_DELEGATE_MBEAN_NAME), + "TestJMXAuthenticatorCallCount")).intValue(); + + if (callCount == expectedAuthenticatorCallCount) { + System.out.println("---- OK Authenticator has been called " + + expectedAuthenticatorCallCount + " time"); + } else { + errorCount++; + System.out.println("---- ERROR Authenticator has been called " + callCount + + " times in place of " + expectedAuthenticatorCallCount); + } + + // Ensure the provider has been called with + // a non null Principal. + String principalString = + (String) mbs.getAttribute( + new ObjectName(SERVER_DELEGATE_MBEAN_NAME), + "TestJMXAuthenticatorPrincipalString"); + + if (principalString == null) { + errorCount++; + System.out.println("---- ERROR Authenticator has been called" + + " with a null Principal"); + } else { + if (principalString.length() > 0) { + System.out.println("---- OK Authenticator has been called" + + " with the Principal " + principalString); + } else { + errorCount++; + System.out.println("---- ERROR Authenticator has been called" + + " with an empty Principal"); + } + } + + return errorCount; + } + + } + +} diff --git a/jdk/test/javax/management/security/ServerDelegate.java b/jdk/test/javax/management/security/ServerDelegate.java new file mode 100644 index 00000000000..6ce5face871 --- /dev/null +++ b/jdk/test/javax/management/security/ServerDelegate.java @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.security.Principal; +import java.util.ArrayList; +import java.util.List; + +import javax.management.remote.JMXServiceURL ; +import javax.management.MBeanRegistration; +import javax.management.MBeanServer; +import javax.management.ObjectName; +import javax.management.StandardMBean; + +/** + * This class defines an MBean that can be registered and used on client side + * to handle informations or properties of the remote server. + * + * For example, this MBean can store IOR addresses + * of RMI/IIOP connector(s) used in a test. + * + * That MBean might not be used for testing purpose itself. + */ +public class ServerDelegate implements ServerDelegateMBean, MBeanRegistration { + + private MBeanServer mbeanServer = null; + private List addresses = null; + private String port; + private static String javaVersion = System.getProperty("java.version"); + private int sqeJmxwsCredentialsProviderCallCount = 0; + private String jmxwsCredentialsProviderUrl = null; + private int testJMXAuthenticatorCallCount = 0; + private Principal testJMXAuthenticatorPrincipal = null; + + @SqeDescriptorKey("NO PARAMETER CONSTRUCTOR ServerDelegate") + public ServerDelegate() { + addresses = new ArrayList(); + } + + public ObjectName preRegister(MBeanServer server, ObjectName name) + throws Exception { + // Initialize MBeanServer attribute + mbeanServer = server; + return name; + } + public void postRegister(Boolean registrationDone) { + } + public void preDeregister() throws Exception { + } + public void postDeregister() { + } + + public void addAddress(JMXServiceURL url) { + addresses.add(url) ; + } + + public List getAddresses() { + return addresses ; + } + + public void setPort(String p) { + port = p ; + } + + public String getPort() { + return port ; + } + + public String getJavaVersion() { + return javaVersion; + } + + public void sqeJmxwsCredentialsProviderCalled() { + sqeJmxwsCredentialsProviderCallCount++; + } + + public int getSqeJmxwsCredentialsProviderCallCount() { + return sqeJmxwsCredentialsProviderCallCount; + } + + public void setJmxwsCredentialsProviderUrl(String url) { + jmxwsCredentialsProviderUrl = url; + } + + public String getJmxwsCredentialsProviderUrl() { + return jmxwsCredentialsProviderUrl; + } + + public void testJMXAuthenticatorCalled() { + testJMXAuthenticatorCallCount++; + } + + public int getTestJMXAuthenticatorCallCount() { + return testJMXAuthenticatorCallCount; + } + + public void setTestJMXAuthenticatorPrincipal(Principal principal) { + testJMXAuthenticatorPrincipal = principal; + } + + public String getTestJMXAuthenticatorPrincipalString() { + if ( testJMXAuthenticatorPrincipal != null ) { + return testJMXAuthenticatorPrincipal.toString(); + } + + return null; + } + + /** + * Instantiates and registers a StandardMBean in the MBean server. + * + * @param implementationClassName + * The implementation class name of the MBean. + * @param interfaceClassName + * The management interface class name of the MBean. + * @param isMXBean + * If true, the resultant MBean is an MXBean. + * @param name + * The object name of the StandardMBean. + */ + @SuppressWarnings("unchecked") + public void createStandardMBean( + String implementationClassName, + String interfaceClassName, + boolean isMXBean, + ObjectName name) + throws Exception { + + Object implementation = + Class.forName(implementationClassName).newInstance(); + Class interfaceClass = interfaceClassName == null ? null : + (Class)Class.forName(interfaceClassName); + + // Create the StandardMBean + StandardMBean standardMBean = new StandardMBean( + implementation, + interfaceClass, + isMXBean); + + // Register the StandardMBean + mbeanServer.registerMBean(standardMBean, name); + } + + /** + * Instantiates and registers a StandardMBean in the MBean server. + * The object will use standard JMX design pattern to determine + * the management interface associated with the given implementation. + */ + @SuppressWarnings("unchecked") + public void createStandardMBean( + String implementationClassName, + boolean isMXBean, + ObjectName name) + throws Exception { + + createStandardMBean(implementationClassName, null, isMXBean, name); + } +} diff --git a/jdk/test/javax/management/security/ServerDelegateMBean.java b/jdk/test/javax/management/security/ServerDelegateMBean.java new file mode 100644 index 00000000000..88f0b3f5675 --- /dev/null +++ b/jdk/test/javax/management/security/ServerDelegateMBean.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.security.Principal; +import java.util.List; + +import javax.management.remote.JMXServiceURL ; +import javax.management.ObjectName; + +@SqeDescriptorKey("INTERFACE ServerDelegateMBean") +public interface ServerDelegateMBean { + @SqeDescriptorKey("ATTRIBUTE Address") + public void addAddress(JMXServiceURL url); + + @SqeDescriptorKey("ATTRIBUTE Address") + public List getAddresses(); + + public String getPort(); + public void setPort(String p); + + public String getJavaVersion(); + + public void sqeJmxwsCredentialsProviderCalled(); + public int getSqeJmxwsCredentialsProviderCallCount(); + + public void setJmxwsCredentialsProviderUrl(String url); + public String getJmxwsCredentialsProviderUrl(); + + public void testJMXAuthenticatorCalled(); + public int getTestJMXAuthenticatorCallCount(); + + public void setTestJMXAuthenticatorPrincipal(Principal principal); + public String getTestJMXAuthenticatorPrincipalString(); + + public void createStandardMBean( + String implementationClassName, + String interfaceClassName, + boolean isMXBean, + ObjectName name) + throws Exception; + + public void createStandardMBean( + String implementationClassName, + boolean isMXBean, + ObjectName name) + throws Exception; +} diff --git a/jdk/test/javax/management/security/Simple.java b/jdk/test/javax/management/security/Simple.java new file mode 100644 index 00000000000..c41da3f435a --- /dev/null +++ b/jdk/test/javax/management/security/Simple.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +//import java.beans.ConstructorProperties; +import javax.management.ConstructorParameters; + +/** + * This class defines a simple standard MBean. + */ +public class Simple implements SimpleMBean { + + private String attribute = "initial_value"; + private boolean operationInvoked = false; + private boolean operation2Invoked = false; + + @SqeDescriptorKey("NO PARAMETER CONSTRUCTOR Simple") + public Simple() { + } + + @SqeDescriptorKey("TWO PARAMETERS CONSTRUCTOR Simple") + @ConstructorParameters({"unused1", "unused2"}) + public Simple(@SqeDescriptorKey("CONSTRUCTOR PARAMETER unused1")int unused1, + @SqeDescriptorKey("CONSTRUCTOR PARAMETER unused2")int unused2) { + } + + public String getAttribute() { + return attribute; + } + public void setAttribute(String s) { + attribute = s; + } + public boolean getOperationInvoked() { + return operationInvoked; + } + public boolean getOperation2Invoked() { + return operation2Invoked; + } + + public void operation() { + operationInvoked = true; + return; + } + + public String operation2(int i) { + operation2Invoked = true; + return String.valueOf(i); + } + + public void reset() { + attribute = "initial_value"; + operationInvoked = false; + operation2Invoked = false; + } +} diff --git a/jdk/test/javax/management/security/SimpleListener.java b/jdk/test/javax/management/security/SimpleListener.java new file mode 100644 index 00000000000..0510150150a --- /dev/null +++ b/jdk/test/javax/management/security/SimpleListener.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// JDK +import java.util.Vector; + +// JMX +import javax.management.NotificationListener; +import javax.management.Notification; + +public class SimpleListener implements NotificationListener { + private boolean received = false; + private String type = null; + private Object handback = null; + private Vector handbacks = new Vector(); + private int nbrec = 0; + + public synchronized void handleNotification(Notification notification, + Object handback) { + Utils.debug(Utils.DEBUG_STANDARD, + "SimpleListener::handleNotification :" + notification); + try { + received = true; + type = notification.getType(); + this.handback = handback; + handbacks.add(handback); + nbrec++; + notify(); + } catch(Exception e) { + System.out.println("(ERROR) SimpleListener::handleNotification :" + + " Caught exception " + + e) ; + } + } + + public synchronized boolean isNotificationReceived() { + boolean ret = received; + reset(); + return ret; + } + + public synchronized Object[] waitForMultiNotifications(int nb) { + while(true) { + if(nbrec < nb) { + Utils.debug(Utils.DEBUG_STANDARD, + "SimpleListener::waitForMultiNotifications wait"); + try { + wait(); + } catch(InterruptedException ie) { + // OK : we wait for being interrupted + } + Utils.debug(Utils.DEBUG_STANDARD, + "SimpleListener::waitForMultiNotifications wait over"); + } + else + break; + } + Object[] ret = handbacks.toArray(); + reset(); + return ret; + } + + private void reset() { + received = false; + handback = null; + handbacks.removeAllElements(); + type = null; + } + + public synchronized Object waitForNotificationHB() { + while(true) { + if(!received) { + Utils.debug(Utils.DEBUG_STANDARD, + "SimpleListener::waitForNotificationHB wait"); + try { + wait(); + } catch(InterruptedException ie) { + // OK : we wait for being interrupted + } + Utils.debug(Utils.DEBUG_STANDARD, + "SimpleListener::waitForNotificationHB received"); + } + else + break; + } + Object ret = handback; + reset(); + return ret; + } + + public synchronized String waitForNotification() { + while(true) { + if(!received) { + Utils.debug(Utils.DEBUG_STANDARD, + "SimpleListener::waitForNotification wait"); + try { + wait(); + } catch(InterruptedException ie) { + // OK : we wait for being interrupted + } + Utils.debug(Utils.DEBUG_STANDARD, + "SimpleListener::waitForNotification received"); + } + else + break; + } + String ret = type; + reset(); + return ret; + } +} diff --git a/jdk/test/javax/management/security/SimpleMBean.java b/jdk/test/javax/management/security/SimpleMBean.java new file mode 100644 index 00000000000..676ac4aaa2d --- /dev/null +++ b/jdk/test/javax/management/security/SimpleMBean.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * This interface defines a simple standard MBean. + */ +@SqeDescriptorKey("INTERFACE SimpleMBean") +public interface SimpleMBean { + + @SqeDescriptorKey("ATTRIBUTE Attribute") + public String getAttribute(); + + @SqeDescriptorKey("ATTRIBUTE Attribute") + public void setAttribute(String s); + + @SqeDescriptorKey("ATTRIBUTE OperationInvoked") + public boolean getOperationInvoked(); + + @SqeDescriptorKey("ATTRIBUTE Operation2Invoked") + public boolean getOperation2Invoked(); + + // Void operation + // The associated MBeanOperationInfo is mapped to OpenMBeanOperationInfo + // => openType is added to the descriptor + @SqeDescriptorKey(value = "OPERATION operation", + descriptorFields = {"openType=SimpleType.VOID"}) + public void operation(); + + @SqeDescriptorKey("OPERATION operation2") + public String operation2(int i); + + // Void operation + // The associated MBeanOperationInfo is mapped to OpenMBeanOperationInfo + // => openType is added to the descriptor + @SqeDescriptorKey(value = "OPERATION reset", + descriptorFields = {"openType=SimpleType.VOID"}) + public void reset(); +} diff --git a/jdk/test/javax/management/security/SqeDescriptorKey.java b/jdk/test/javax/management/security/SqeDescriptorKey.java new file mode 100644 index 00000000000..60e4926218b --- /dev/null +++ b/jdk/test/javax/management/security/SqeDescriptorKey.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import javax.management.DescriptorKey; + +/** + * That annotation is usable everywhere DescriptorKey is (and even more). + * It is for use to test that you can retrieve the SqeDescriptorKey into the + * appropriate Descriptor instances as built by the JMX runtime. + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +public @interface SqeDescriptorKey { + @DescriptorKey("sqeDescriptorKey") + String value(); + + // List descriptor fields that may be added or may be updated + // when retrieving an MBeanInfo using a JMXWS connection compared to the + // MBeanInfo returned by a local MBeanServer. + // The annotation format is : + // = + // The values actually handled by the test suite are : + // openType=SimpleType.VOID + @DescriptorKey("descriptorFields") + String[] descriptorFields() default {}; +} diff --git a/jdk/test/javax/management/security/TestJMXAuthenticator.java b/jdk/test/javax/management/security/TestJMXAuthenticator.java new file mode 100644 index 00000000000..cdea05d3e43 --- /dev/null +++ b/jdk/test/javax/management/security/TestJMXAuthenticator.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.security.Principal; + +import javax.management.Attribute; +import javax.management.MBeanServer; +import javax.management.ObjectName; +import javax.management.remote.JMXAuthenticator; +import javax.management.remote.JMXPrincipal; +import javax.security.auth.Subject; + +public final class TestJMXAuthenticator implements JMXAuthenticator { + + private String protocol = ""; + private MBeanServer mbs = null; + + public TestJMXAuthenticator() { + } + + public TestJMXAuthenticator(String protocol) { + this.protocol = protocol; + } + + public TestJMXAuthenticator(String protocol, MBeanServer mbs) { + this.protocol = protocol; + this.mbs = mbs; + } + + public Subject authenticate(Object credentials) { + + String credentials_username = ""; + String credentials_password = ""; + Principal aPrincipal = null; + + credentials_username = ((String[]) credentials)[0]; + credentials_password = ((String[]) credentials)[1]; + + String authenticated_username = System.getProperty("susername"); + String authenticated_password = System.getProperty("spassword"); + String principal = System.getProperty("principal"); + + System.out.println("TestJMXAuthenticator::authenticate: Start"); + System.out.println("TestJMXAuthenticator::authenticate: credentials username = " + + credentials_username); + System.out.println("TestJMXAuthenticator::authenticate: credentials password = " + + credentials_password); + System.out.println("TestJMXAuthenticator::authenticate: authenticated username = " + + authenticated_username); + System.out.println("TestJMXAuthenticator::authenticate: authenticated password = " + + authenticated_password); + System.out.println("TestJMXAuthenticator::authenticate: principal used for " + + "authorization = " + principal); + + if (credentials_username.equals(authenticated_username) && + credentials_password.equals(authenticated_password)) { + System.out.println("TestJMXAuthenticator::authenticate: " + + "Authenticator should succeed"); + } else { + System.out.println("TestJMXAuthenticator::authenticate: " + + "Authenticator should reject"); + throw new SecurityException("TestJMXAuthenticator throws EXCEPTION"); + } + + // At this point, authentication has succeeded + // (no SecurityException thrown). + // + // If no authorization is required, the returned subject (empty or not) + // is useless. + // Otherwise, the returned subject must define a principal + // and authorization will be performed against this principal. + // + // Note that this custom JMXAuthenticator is used for test purpose and + // the username used to perform authentication may be different from the + // username used to perform authorization. + // + Subject subject = new Subject(); + + if (principal != null) { + System.out.println("TestJMXAuthenticator::authenticate: " + + "Add " + principal + " principal to the returned subject"); + subject.getPrincipals().add(new JMXPrincipal(principal)); + } + + return subject; + } +} diff --git a/jdk/test/javax/management/security/TestSampleLoginModule.java b/jdk/test/javax/management/security/TestSampleLoginModule.java new file mode 100644 index 00000000000..81d342d8704 --- /dev/null +++ b/jdk/test/javax/management/security/TestSampleLoginModule.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.util.Map; + +import javax.security.auth.Subject; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.login.LoginException; +import javax.security.auth.spi.LoginModule; + + +public final class TestSampleLoginModule implements LoginModule { + + private Subject subject; + private CallbackHandler callbackHandler; + private Map sharedState; + private Map options; + + public TestSampleLoginModule() { + } + + public void initialize(Subject subject, + CallbackHandler callbackHandler, + Map sharedState, + Map options) { + + this.subject = subject; + this.callbackHandler = callbackHandler; + this.sharedState = sharedState; + this.options = options; + } + + /* + * Authenticate the user by comparing the values of the java properties + * (username and password) against the values of the credentials. + * */ + public boolean login() throws LoginException { + + String credentials_username = null; + String credentials_password = null; + String authenticated_username = System.getProperty("susername"); + String authenticated_password = System.getProperty("spassword"); + + System.out.println("TestSampleLoginModule::login: Start"); + + // First retreive the credentials {username, password} from + // the callback handler + Callback[] callbacks = new Callback[2]; + callbacks[0] = new NameCallback("username"); + callbacks[1] = new PasswordCallback("password", false); + try { + callbackHandler.handle(callbacks); + credentials_username = ((NameCallback)callbacks[0]).getName(); + credentials_password = new String(((PasswordCallback)callbacks[1]). + getPassword()); + } catch (Exception e) { + throw new LoginException(e.toString()); + } + + System.out.println("TestSampleLoginModule::login: credentials username = " + + credentials_username); + System.out.println("TestSampleLoginModule::login: credentials password = " + + credentials_password); + System.out.println("TestSampleLoginModule::login: authenticated username = " + + authenticated_username); + System.out.println("TestSampleLoginModule::login: authenticated password = " + + authenticated_password); + + if (credentials_username.equals(authenticated_username) && + credentials_password.equals(authenticated_password)) { + System.out.println("TestSampleLoginModule::login: " + + "Authentication should succeed"); + return true; + } else { + System.out.println("TestSampleLoginModule::login: " + + "Authentication should reject"); + throw new LoginException("TestSampleLoginModule throws EXCEPTION"); + } + } + + public boolean commit() throws LoginException { + return true; + } + + public boolean abort() throws LoginException { + return true; + } + + public boolean logout() throws LoginException { + return true; + } +} diff --git a/jdk/test/javax/management/security/Utils.java b/jdk/test/javax/management/security/Utils.java new file mode 100644 index 00000000000..c0af037a4d6 --- /dev/null +++ b/jdk/test/javax/management/security/Utils.java @@ -0,0 +1,424 @@ +/* + * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.util.Map; +import java.util.HashMap; +import java.util.Properties; +import java.util.StringTokenizer; +import java.lang.reflect.Method; +import javax.management.remote.JMXConnectorServerMBean; + +// utility class for MXBean* tests coming from JMX Tonga test suite +class Utils { + + private static final String SERVER_SIDE_NAME = "-server"; + private static final String CLIENT_SIDE_NAME = "-client"; + + // DEBUG is printed depending on the DEBUG and DEBUG_LEVEL JAVA property + private static final String DEBUG_HEADER = "[debug] "; + + // DEBUG levels + private static int selectedDebugLevel = 0; + static final int DEBUG_STANDARD = 1; + static final int DEBUG_VERBOSE = 2; // Mainly used for stress tests + static final int DEBUG_ALL = DEBUG_STANDARD | DEBUG_VERBOSE; + + static void parseDebugProperties() { + int level = 0; + Properties p = System.getProperties(); + + // get selected levels + if (p.getProperty("DEBUG_STANDARD") != null) { + level |= DEBUG_STANDARD; + } + + if (p.getProperty("DEBUG_VERBOSE") != null) { + level |= DEBUG_VERBOSE; + } + + if (p.getProperty("DEBUG_ALL") != null) { + level |= DEBUG_ALL; + } + + selectedDebugLevel = level; + } + + /** + * Reproduces the original parsing and collection of test parameters + * from the DTonga JMX test suite. + * + * Collects passed args and returns them in a map(argname, value) structure, + * which will be then propagated as necessary to various called methods. + */ + static Map parseParameters(String args[]) + throws Exception { + Utils.debug(DEBUG_STANDARD, "TestRoot::parseParameters: Start"); + HashMap map = new HashMap<>(); + + for ( int i = 0; i < args.length; i++ ) { + if ( args[i].trim().startsWith("-") ) { + if ((i+1) < args.length && !args[i+1].startsWith("-") ) { + Utils.debug(DEBUG_STANDARD, + "TestRoot::parseParameters: added in map = " + + args[i] + + " with value " + + args[i+1]) ; + map.put(args[i].trim(), args[i+1].trim()) ; + } else if ((i+1) < args.length && args[i+1].startsWith("-") || + (i+1) == args.length ) { + Utils.debug(DEBUG_STANDARD, + "TestRoot::parseParameters: added in map = " + + args[i] + + " with null value") ; + map.put(args[i].trim(), null) ; + } else { + System.out.println( + "TestRoot::parseParameters: (WARNING) not added in map = " + + args[i]) ; + } + } + } + + Utils.debug(DEBUG_STANDARD, "TestRoot::parseParameters: Done") ; + return map ; + } + + // Parse server parameters and put them in passed serverMap + static int parseServerParameters(String args[], + String serverSideName, + Map serverMap ) + throws Exception { + Utils.debug(Utils.DEBUG_STANDARD, + serverSideName + "::parseServerParameters: Start"); + + int nextIndex = 0; + boolean seenServerFlag = false; + + for ( int i = 0; i < args.length; i++ ) { + // Case of reaching "-server" flag parameter + if (args[i].equals(SERVER_SIDE_NAME)) { + if (!seenServerFlag) { + seenServerFlag = true; + continue; + } else { + // Already parsing server params, invalid params list + Utils.debug(Utils.DEBUG_STANDARD, + serverSideName + "::parseParameters: Invalid " + + args[i] + " parameter detected in " + + SERVER_SIDE_NAME + " parameters list"); + nextIndex = -1; + throw new RuntimeException("Invalid Parameter list"); + } + } + + // Case of reaching "-client" flag parameter + if (args[i].equals(CLIENT_SIDE_NAME)) { + // While parsing server parameters, then parsing is done. + Utils.debug(Utils.DEBUG_STANDARD, + serverSideName + "::parseServerParameters: Parsing of " + + SERVER_SIDE_NAME + " parameters done."); + return i; + } + + i = parseParamAtIndex(args, i, serverMap); + nextIndex = i; + } + + Utils.debug(Utils.DEBUG_STANDARD, + serverSideName + "::parseServerParameters: Parsing of parameters done"); + + return nextIndex; + } + + // Parse client parameters and put them in passed clientMap + static void parseClientParameters(String args[], + String clientSideName, + Map clientMap ) + throws Exception { + + Utils.debug(Utils.DEBUG_STANDARD, + clientSideName + "::parseClientParameters: Start"); + + boolean seenClientFlag = false; + + for ( int i = 0; i < args.length; i++ ) { + // Case of reaching "-client" flag parameter + if (args[i].equals(CLIENT_SIDE_NAME)) { + if (!seenClientFlag) { + seenClientFlag = true; + continue; + } else { + // Already parsing client params, invalid params list + Utils.debug(Utils.DEBUG_STANDARD, + clientSideName + "::parseClientParameters: Invalid " + + CLIENT_SIDE_NAME + " parameter detected in " + + CLIENT_SIDE_NAME + " parameters list."); + throw new RuntimeException("Invalid parameter in " + + clientSideName + " parameter list"); + } + } + + // Case of reaching "-server" flag parameter + if (args[i].equals(SERVER_SIDE_NAME)) { + // While parsing client parameters, invalid parameter list. + Utils.debug(Utils.DEBUG_STANDARD, + clientSideName + "::parseClientParameters: Invalid " + + SERVER_SIDE_NAME + " parameter inside " + + CLIENT_SIDE_NAME + " parameters list."); + throw new RuntimeException("Invalid parameter in " + + clientSideName + " parameter list"); + } + + i = parseParamAtIndex(args, i, clientMap); + } + + Utils.debug(Utils.DEBUG_STANDARD, + clientSideName + "::parseClientParameters: Parsing of parameters done."); + } + + // Add param found at index to passed map + // We only accept either "-param value" or "-param" form. + // The "value" form is invalid but just ignored. + private static int parseParamAtIndex(String args[], + int index, + Map map) { + + if (args[index].trim().startsWith("-") ) { + // Case of a "-param value" form + if ((index+1) < args.length && !args[index+1].startsWith("-") ) { + Utils.debug(Utils.DEBUG_STANDARD, + "TestRoot::parseParamAtIndex: added in map = " + + args[index] + + " with value " + + args[index+1]) ; + // adding ("param", value) to the passed map + map.put(args[index].trim(), args[index+1].trim()) ; + // value should not be parsed a second time + return index+1; + } + // Case of a "-param" form (flag parameter) + else if (((index+1) < args.length && args[index+1].startsWith("-")) || + (index+1) == args.length ) { + Utils.debug(Utils.DEBUG_STANDARD, + "TestRoot::parseParamAtIndex: added in map = " + + args[index] + + " with null value") ; + // adding ("param", null) to passed map + map.put(args[index].trim(), null) ; + } + } else { + // Unsupported "value" alone parameter + Utils.debug(Utils.DEBUG_STANDARD, + "TestRoot::parseParamAtIndex: invalid " + + " value-alone \"" + args[index] + "\" parameter." + + " Parameter ignored."); + } + + return index; + } + + /** + * This method is to be used in all tests to print anything + * that is temporary. + * Printing is done only when debug is activated by the property DEBUG. + * Printing depends also on the DEBUG_LEVEL property. + * Here it encapsulates a System.out.println. + */ + static void debug(int level, String line) { + if ((selectedDebugLevel & level) != 0) { + System.out.println(DEBUG_HEADER + line); + } + } + + /** + * Do print stack trace when withStack is true. + * Does try to call getTargetException() and getTargetError() then + * print embedded stacks in the case of an Exception wrapping + * another Exception or an Error. Recurse until no more wrapping + * is found. + */ + static void printThrowable(Throwable theThro, boolean withStack) { + try { + if (withStack) { + theThro.printStackTrace(System.out); + } + if (theThro instanceof Exception) { + Exception t = (Exception) theThro; + Method target = null; + String blank = " "; + try { + target = t.getClass().getMethod("getTargetException", + (java.lang.Class[]) null); + } catch (Exception ee) { + // OK: getTargetException method could be there or not + } + System.out.println(blank + t.getClass() + "==>" + t.getMessage()); + while (target != null) { + try { + t = (Exception) target.invoke(t, + (java.lang.Object[]) null); + } catch (Exception ee) { + t = null; + } + try { + if (t != null) { + blank = blank + " "; + System.out.println(blank + t.getClass() + "==>" + + t.getMessage()); + try { + target = + t.getClass().getMethod("getTargetException", + (java.lang.Class[]) null); + } catch (Exception ee) { + // OK: getTargetException method could be there or not } + } + } else { + target = null; + } + } catch (Exception ee) { + target = null; + } + } + + // We may have exceptions wrapping an Error then it is + // getTargetError that is likely to be called + try { + target = ((Exception) theThro).getClass().getMethod("getTargetError", + (java.lang.Class[]) null); + } catch (Exception ee) { + // OK: getTargetError method could be there or not + } + Throwable err = theThro; + while (target != null) { + try { + err = (Error) target.invoke(err, + (java.lang.Object[]) null); + } catch (Exception ee) { + err = null; + } + try { + if (err != null) { + blank = blank + " "; + System.out.println(blank + err.getClass() + "==>" + + err.getMessage()); + if (withStack) { + err.printStackTrace(System.out); + } + try { + target = err.getClass().getMethod("getTargetError", + (java.lang.Class[]) null); + } catch (Exception ee) { + // OK: getTargetError method could be there or not + } + } else { + target = null; + } + } catch (Exception ee) { + target = null; + } + } + } else { + System.out.println("Throwable is : " + theThro); + } + } catch (Throwable x) { + System.out.println("Exception : raised in printException : " + x); + } + } + + /** + * Wait up to maxTimeInSeconds second(s) the given JMX connector server + * comes up (which means isActive returns true). + * If it fails to do so we throw a RunTime exception. + */ + static void waitReady(JMXConnectorServerMBean server, + int maxTimeInSeconds) throws Exception { + int elapsed = 0; + + while (!server.isActive() && elapsed < maxTimeInSeconds) { + Thread.sleep(1000); + elapsed++; + } + + if (server.isActive()) { + String message = "Utils::waitReady: JMX connector server came up"; + if ( elapsed == 0) { + message += " immediately"; + } else { + message += " after " + elapsed + " seconds"; + } + message += " [" + server.getAddress() + "]"; + Utils.debug(DEBUG_STANDARD, message); + } else { + String message = "Utils::waitReady: (ERROR) JMX connector" + + " server didn't come up after " + elapsed + " seconds [" + + server.getAddress() + "]"; + System.out.println(message); + throw new RuntimeException(message); + } + } + + /** + * This method is used to compare the specified Throwable and possibly + * the derived causes to the specified String argument. + * The expected String argument format is : + * throwable_1;throwable_2;...;throwable_N + * where throwable_i can be : + * - either a throwable class name + * - or the "*" character meaning several unknown throwable class names + * This character must be followed by a throwable class name + */ + static boolean compareThrowable( + Throwable t, + String expectedThrowable) { + + // First parse the expectedThrowable String + StringTokenizer tokenizer = new StringTokenizer(expectedThrowable, ";"); + String token = null; + + try { + while (tokenizer.hasMoreTokens()) { + token = tokenizer.nextToken(); + if (!token.equals("*")) { + if (!Class.forName(token).isInstance(t)) { + return false; + } + } else { + token = tokenizer.nextToken(); + while (!Class.forName(token).isInstance(t)) { + t = t.getCause(); + if (t == null) { + return false; + } + } + } + t = t.getCause(); + } + } catch (ClassNotFoundException cnfe) { + String msg = "Expected throwable class(es) " + expectedThrowable + + " cannot be located"; + System.out.println(msg); + throw new IllegalArgumentException(msg); + } + return true; + } +} diff --git a/jdk/test/javax/management/security/access.properties b/jdk/test/javax/management/security/access.properties new file mode 100644 index 00000000000..eaf89a8b143 --- /dev/null +++ b/jdk/test/javax/management/security/access.properties @@ -0,0 +1,11 @@ +# Access control file for SQE tests. + +# Default username +SQE_username readwrite create Simple + +# Functional authorization tests +username1 readwrite create Simple +username2 readonly +username3 readonly +username4 readwrite create Simple +username5 readwrite create Simple diff --git a/jdk/test/javax/management/security/java.policy.authorization b/jdk/test/javax/management/security/java.policy.authorization new file mode 100644 index 00000000000..8b712765fca --- /dev/null +++ b/jdk/test/javax/management/security/java.policy.authorization @@ -0,0 +1,98 @@ +// Standard extensions get all permissions by default + +grant codeBase "file:${java.home}/lib/ext/*" { + permission java.security.AllPermission; +}; + +// default permissions granted to all domains +grant { + // Allows any thread to stop itself using the java.lang.Thread.stop() + // method that takes no argument. + // Note that this permission is granted by default only to remain + // backwards compatible. + // It is strongly recommended that you either remove this permission + // from this policy file or further restrict it to code sources + // that you specify, because Thread.stop() is potentially unsafe. + // See "http://java.sun.com/notes" for more information. + permission java.lang.RuntimePermission "stopThread"; + + // allows anyone to listen on un-privileged ports + permission java.net.SocketPermission "localhost:1024-", "listen"; + + // "standard" properies that can be read by anyone + + permission java.util.PropertyPermission "java.version", "read"; + permission java.util.PropertyPermission "java.vendor", "read"; + permission java.util.PropertyPermission "java.vendor.url", "read"; + permission java.util.PropertyPermission "java.class.version", "read"; + permission java.util.PropertyPermission "os.name", "read"; + permission java.util.PropertyPermission "os.version", "read"; + permission java.util.PropertyPermission "os.arch", "read"; + permission java.util.PropertyPermission "file.separator", "read"; + permission java.util.PropertyPermission "path.separator", "read"; + permission java.util.PropertyPermission "line.separator", "read"; + + permission java.util.PropertyPermission "java.specification.version", "read"; + permission java.util.PropertyPermission "java.specification.vendor", "read"; + permission java.util.PropertyPermission "java.specification.name", "read"; + + permission java.util.PropertyPermission "java.vm.specification.version", "read"; + permission java.util.PropertyPermission "java.vm.specification.vendor", "read"; + permission java.util.PropertyPermission "java.vm.specification.name", "read"; + permission java.util.PropertyPermission "java.vm.version", "read"; + permission java.util.PropertyPermission "java.vm.vendor", "read"; + permission java.util.PropertyPermission "java.vm.name", "read"; + + permission java.io.FilePermission "*","read,write"; + +}; + +grant codeBase "file:/-" { + permission java.security.AllPermission; + permission java.io.FilePermission "*","read,write"; +}; + +grant principal javax.management.remote.JMXPrincipal "SQE_username" { + permission javax.management.MBeanServerPermission "*"; + permission javax.management.MBeanPermission "Simple", "instantiate"; + permission javax.management.MBeanPermission "Simple", "registerMBean"; +}; + +grant principal javax.management.remote.JMXPrincipal "username1" { + // + // JMXPrincipals "username1" has all permissions. + // + permission java.security.AllPermission; +}; + +grant principal javax.management.remote.JMXPrincipal "username2" { + // + // JMXPrincipals "username2" has all permissions. + // + permission java.security.AllPermission; +}; + +grant principal javax.management.remote.JMXPrincipal "username3" { + // + // JMXPrincipals "username3" has some permissions. + // + permission javax.management.MBeanPermission "Simple", "instantiate"; + permission javax.management.MBeanPermission "Simple", "registerMBean"; + permission javax.management.MBeanPermission "Simple", "setAttribute"; + permission javax.management.MBeanPermission "Simple", "invoke"; +}; + +grant principal javax.management.remote.JMXPrincipal "username4" { + // + // JMXPrincipals "username4" has all permissions. + // + permission javax.management.MBeanPermission "Simple", "instantiate"; + permission javax.management.MBeanPermission "Simple", "registerMBean"; + permission javax.management.MBeanPermission "Simple", "invoke"; +}; + +grant principal javax.management.remote.JMXPrincipal "username5" { + // + // JMXPrincipals "username5" has no permissions. + // +}; diff --git a/jdk/test/javax/management/security/keystoreAgent b/jdk/test/javax/management/security/keystoreAgent new file mode 100644 index 0000000000000000000000000000000000000000..cfb02e00c38b8e28e4dcf4b0fdc60b80bda58990 GIT binary patch literal 1325 zcmezO_TO6u1_mY|W&~r_#Prm>5+L*M{3&VyK$+bJO-$Pj_}I9#*%(3A9Z;R-&Gn3BG43#*>V^m=g{dlEL;e)p}a%T$OS$go)pZYj?d8^K< zIc^QA>1+9wt*(2Xm)U&v{ek0ApL?1sO1VyXnX%k2a&pQ0d-%#3;ZWfc9tn|>WNvHq77(l(VrM)ddOPoZTq=I=4;DssR6+(yX4A~kR6 zw69UtJHIK<2>rEt`oj;AZTGJ|+sn*p<#?z6*#1cMezDg3pP34jr=%5rsy^g>{91PK zq76lQy@|)&<5OPf1?o%}|9j%yo?8X1`}e1A``kC3X)3!s2e0@ns|u&rFQlszbuu&B z!izeu3(9lY^jQ3kRXeod;xFkP7Vfd_D{sCwlV6(j&O$*?ET64KVd?J2p*y}c?!J8b zKx*gR<4h|%%uhWtO8BFYEV3v_XSmhGh=Ud-I<@G;iMU@9XLHKhD=Q z-w3vPrX=jy;c<%d$>P)dmVKQy$?vOI+}U8gOHyxcN$$M+py<$>%|-9tPhxm;Prl+T zr(Cll_c}glp5Ge03R|uo-g&(5hIvOgz4Qgno#sevUhCF=lF zvW7tu<3uJFhqt9?40zc%wc0$|zVk9Nvam83L>qD&aI!Invaks=xdj>W8SsEO9Kx)g z#i>Q9hJptCAU?Y=hkH?KUVc(esv(a77f6Upm^rvK&yd%E8^q%lX7Mr#c7;hY3p0E9 zMi|J6^BR~Km>XCani-jy8byio8e1Be85=;kw6(l(KC)kd*_XMom%*U1lc}+hVeYPp z^7|)oO+Ikj)`~}m`CwS*EO*OUyy@`~f2KY=GRMrsYL9A)#FDPv<+HzY1eJv8zVc{X z#ah1bK0{l-zTANmSsR4@a<8bky!b~#mE@vdO?wy2=E<;XDrvFP-B+&H@4``V-2R-x z1PwK(HM@j9O}xHFNx5(C7kftDcMo?oMFnIsF*7nSB0CQl70f_)2`-+P{V34y%!Us^ z^{MiIuT{S@;M`Tbr_e7&XiB&BzqOW@3!k|=p5Jn^@TSq5%YiKmr&^!d*mPaev6gj( z&&|esy?G|nisS{GpPJ2%R!x^PpR_Hz*Wtn}!&$F{Zr+G%HnUpZ$SgFA<)pGes_xC` s1RYCmjWelRZulO{m{9H0U*5Di<--A8#+8qI>{e=jSUrVB$KkIw0BqD5(*OVf literal 0 HcmV?d00001 diff --git a/jdk/test/javax/management/security/keystoreClient b/jdk/test/javax/management/security/keystoreClient new file mode 100644 index 0000000000000000000000000000000000000000..f0e0b7f57187520d475f57004e05ab8418f308d4 GIT binary patch literal 1327 zcmezO_TO6u1_mY|W&~rlV$@BAs&e}Pha44RmB81S)iX|pl1FlsRgGBUC< zurx6(>Gsk;Sbn%ibz|G+b3czQnUma9n4fTi{d$GGnOoAbfE=z(YEH|V-QBqQb@r>N zPcUo#X=u1lvGqW4=XH~nN=|XYIvpZ6%3d6ey5-+zwEe^nrxj6iuJAFY?RwbZZ`*k6 zqWOiyw{It$$#I=>)}X`3P0gnB+3CQy-)B9wn8Luv2$2(Vd^Toh1ZvOoPL{S9Vb!sR(;-_hMoFlbGdJ=W8P@u-NZR@;V6wawl4@wFJA(Yexdr!3z_1^LQ4y2vOB z@^7E4le*z_c zYQ1Bfo_<7-t6$fr=W(*XAFNn5{pU;ZyH;$f_CGr6roC6G-+s<$gT`j|^zQX}i+?tS zuHpQco6Zq;`Oe{Z`M;B&dmenXWVNxNwnp}wDbaeBf33X{ z^L?}UpAJiyB8|$qzNFdtZ+#OvdB579^EOGu4n1FiI^{}xovLqo>PP#k&lT}a>a8jJn7Ko8X4(VSYm7JEJaJw( zO=z*n`&6TKi#JbqZ8CdVH0$~6yIUqc7M-kJ)R5h}V$GqzybFQG2~prQ%^IO+YG4UW z$~wTLtYOf^IFX6P;ce+f170>xtu~Lg@4SqREUXL$(T3axoNUaYENsF|Zb62820S1R zhcK&WacWVjp`Zajh|ey};a-%Qm!FiAYRF^21rp*CW)3dRGvqbk2JyIsS-gybU15^U z!pvU25e9PNyapx)<^~pqW=3YF=27Cj#+C+V#s*L>Z7pw{kL*`q{$+0LWiV*$WNK_= z__FfU6roS@d?wSXCUu|u5~!fcQ?FBa> z)7%gK%SsPOr`BBg#3#;QePM@Jkb;Trl(lcH3|`cUzgSqnsk&#vXREg2lczuQT&%xQ z6Cri5wQy;p@vkh!FOMT{rsTio`1C%uk(~#O3TB|Y`fks?q-kzy z!Fc?*!u*M@ZFR<%?=9TkIoH?Is>i)p(sn8_{3kk5bz#NiNT^(;;;N;MQT;0N*9 zg*n`dQuFeYa#9U>47fl-T*A!3rFn+D2HYSXw=j#BQLrmal3AG9%QwP6PMp`k#K7FZ z!qCjf%+x$eoY&aWz|7bH%B8L4jq{QH%E-#V+}O)t(Ade;*vRl@<*6w`pXB*Wrd3Vq zKKUh3L6xU!cbVdoe4z>ID`G0jo7zm&tk@ep{>jhyBlgnKW#Oi|AN-e<9*|D0x$=om zoWJ_Q4zC~u6Wb|k-&h&Es1tv&uz*u_&xFrbZN(=~f9Sbbf1@Ts>R@Z((njN7S&CmC zN8U`yf6ejr&0P}<+rOR@_A@avGB6@L4;U59KzH@so_k5t+|+{c_;H2#6J6Wtj4$6? zxV>|(ucuXyd$FYPs&{G4Z*BjY&)T_PzsUN#9W&?tqX!h$h={+Ew^2}<&lJkJvfzA9 z{?zE?Q@kAqWt`@|R8YSDce2}Dy_WYa9eM7loX%z5-lbNr`wu)FR?K>|cBMU2oL9`*a0Vf-CC<~h~lUtA>p8*eu!y(M-S)5vwYA9&H58|^6 zbGR3!=H(~lq#E)VaDjxlgqed&^9*?nxIsK_VHPi=U{{zVvoN!lZ-jxIIIn?;fw_T& zp_!4HsZo?Tud$_pnXv(sOIynu=Og=-k(GhDv6sQ1v6HE>kzwwxiSqj=aZNsO+t!Ll zhxuSw=PY;2S-k1-5r3vWJ2J=2#A=Ufio}wx-Q}~ta|D%y>Av!4T*X?x@IFIZzrNgo z6ImOC{&KIVxV-pBLY3sAUrl=#%;w3kYAR{5)7@9D*YCnnaNPc!!UPR9r!~8TK25y7 zMoGDE?iYJT-gggoG(`nuGBGnUFd{n-7!}MwcL^?@nEfcw@63h|LG`Kff3H=)GvM4+ zyrVGoQ3AyVv2uEW=r^gl^u5Yc{i5-pDL8i{+%UK&tM|=mZ^0ZjCdkTW Date: Wed, 23 Dec 2015 13:19:58 -0500 Subject: [PATCH 61/62] 8143413: add toEpochSecond methods for efficient access Reviewed-by: rriggs, scolebourne --- .../share/classes/java/time/LocalDate.java | 27 +++++++++++++++++++ .../share/classes/java/time/LocalTime.java | 24 +++++++++++++++++ .../share/classes/java/time/OffsetTime.java | 24 ++++++++++++++++- .../java/time/tck/java/time/TCKLocalDate.java | 25 +++++++++++++++++ .../java/time/tck/java/time/TCKLocalTime.java | 26 ++++++++++++++++++ .../time/tck/java/time/TCKOffsetTime.java | 24 +++++++++++++++++ 6 files changed, 149 insertions(+), 1 deletion(-) diff --git a/jdk/src/java.base/share/classes/java/time/LocalDate.java b/jdk/src/java.base/share/classes/java/time/LocalDate.java index 0d47deeac3a..0c6c755b813 100644 --- a/jdk/src/java.base/share/classes/java/time/LocalDate.java +++ b/jdk/src/java.base/share/classes/java/time/LocalDate.java @@ -147,6 +147,10 @@ public final class LocalDate * This could be used by an application as a "far future" date. */ public static final LocalDate MAX = LocalDate.of(Year.MAX_VALUE, 12, 31); + /** + * The epoch year {@code LocalDate}, '1970-01-01'. + */ + public static final LocalDate EPOCH = LocalDate.of(1970, 1, 1); /** * Serialization version. @@ -1864,6 +1868,29 @@ public long toEpochDay() { return total - DAYS_0000_TO_1970; } + /** + * Converts this {@code LocalDate} to the number of seconds since the epoch + * of 1970-01-01T00:00:00Z. + *

    + * This combines this local date with the specified time and + * offset to calculate the epoch-second value, which is the + * number of elapsed seconds from 1970-01-01T00:00:00Z. + * Instants on the time-line after the epoch are positive, earlier + * are negative. + * + * @param time the local time, not null + * @param offset the zone offset, not null + * @return the number of seconds since the epoch of 1970-01-01T00:00:00Z, may be negative + * @since 9 + */ + public long toEpochSecond(LocalTime time, ZoneOffset offset) { + Objects.requireNonNull(time, "time"); + Objects.requireNonNull(offset, "offset"); + long secs = toEpochDay() * SECONDS_PER_DAY + time.toSecondOfDay(); + secs -= offset.getTotalSeconds(); + return secs; + } + //----------------------------------------------------------------------- /** * Compares this date to another date. diff --git a/jdk/src/java.base/share/classes/java/time/LocalTime.java b/jdk/src/java.base/share/classes/java/time/LocalTime.java index 6b41e8579ad..7cd4adf693f 100644 --- a/jdk/src/java.base/share/classes/java/time/LocalTime.java +++ b/jdk/src/java.base/share/classes/java/time/LocalTime.java @@ -1490,6 +1490,30 @@ public long toNanoOfDay() { return total; } + /** + * Converts this {@code LocalTime} to the number of seconds since the epoch + * of 1970-01-01T00:00:00Z. + *

    + * This combines this local time with the specified date and + * offset to calculate the epoch-second value, which is the + * number of elapsed seconds from 1970-01-01T00:00:00Z. + * Instants on the time-line after the epoch are positive, earlier + * are negative. + * + * @param date the local date, not null + * @param offset the zone offset, not null + * @return the number of seconds since the epoch of 1970-01-01T00:00:00Z, may be negative + * @since 9 + */ + public long toEpochSecond(LocalDate date, ZoneOffset offset) { + Objects.requireNonNull(date, "date"); + Objects.requireNonNull(offset, "offset"); + long epochDay = date.toEpochDay(); + long secs = epochDay * 86400 + toSecondOfDay(); + secs -= offset.getTotalSeconds(); + return secs; + } + //----------------------------------------------------------------------- /** * Compares this time to another time. diff --git a/jdk/src/java.base/share/classes/java/time/OffsetTime.java b/jdk/src/java.base/share/classes/java/time/OffsetTime.java index fca51fd8941..1df445da650 100644 --- a/jdk/src/java.base/share/classes/java/time/OffsetTime.java +++ b/jdk/src/java.base/share/classes/java/time/OffsetTime.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -1232,6 +1232,28 @@ private long toEpochNano() { return nod - offsetNanos; } + /** + * Converts this {@code OffsetTime} to the number of seconds since the epoch + * of 1970-01-01T00:00:00Z. + *

    + * This combines this offset time with the specified date to calculate the + * epoch-second value, which is the number of elapsed seconds from + * 1970-01-01T00:00:00Z. + * Instants on the time-line after the epoch are positive, earlier + * are negative. + * + * @param date the localdate, not null + * @return the number of seconds since the epoch of 1970-01-01T00:00:00Z, may be negative + * @since 9 + */ + public long toEpochSecond(LocalDate date) { + Objects.requireNonNull(date, "date"); + long epochDay = date.toEpochDay(); + long secs = epochDay * 86400 + time.toSecondOfDay(); + secs -= offset.getTotalSeconds(); + return secs; + } + //----------------------------------------------------------------------- /** * Compares this {@code OffsetTime} to another time. diff --git a/jdk/test/java/time/tck/java/time/TCKLocalDate.java b/jdk/test/java/time/tck/java/time/TCKLocalDate.java index 6218d150241..afe7ddcdb58 100644 --- a/jdk/test/java/time/tck/java/time/TCKLocalDate.java +++ b/jdk/test/java/time/tck/java/time/TCKLocalDate.java @@ -2156,6 +2156,31 @@ public void test_toEpochDay() { assertEquals(LocalDate.of(-1, 12, 31).toEpochDay(), -678942 - 40587); } + //----------------------------------------------------------------------- + // toEpochSecond + //----------------------------------------------------------------------- + @DataProvider(name="epochSecond") + Object[][] provider_toEpochSecond() { + return new Object[][] { + {LocalDate.of(1858, 11, 17).toEpochSecond(LocalTime.MIDNIGHT, OFFSET_PONE), -3506720400L}, + {LocalDate.of(1, 1, 1).toEpochSecond(LocalTime.NOON, OFFSET_PONE), -62135557200L}, + {LocalDate.of(1995, 9, 27).toEpochSecond(LocalTime.of(5, 30), OFFSET_PTWO), 812172600L}, + {LocalDate.of(1970, 1, 1).toEpochSecond(LocalTime.MIDNIGHT, OFFSET_MTWO), 7200L}, + {LocalDate.of(-1, 12, 31).toEpochSecond(LocalTime.NOON, OFFSET_PONE), -62167266000L}, + {LocalDate.of(1, 1, 1).toEpochSecond(LocalTime.MIDNIGHT, OFFSET_PONE), + Instant.ofEpochSecond(-62135600400L).getEpochSecond()}, + {LocalDate.of(1995, 9, 27).toEpochSecond(LocalTime.NOON, OFFSET_PTWO), + Instant.ofEpochSecond(812196000L).getEpochSecond()}, + {LocalDate.of(1995, 9, 27).toEpochSecond(LocalTime.of(5, 30), OFFSET_MTWO), + LocalDateTime.of(1995, 9, 27, 5, 30).toEpochSecond(OFFSET_MTWO)}, + }; + } + + @Test(dataProvider="epochSecond") + public void test_toEpochSecond(long actual, long expected) { + assertEquals(actual, expected); + } + //----------------------------------------------------------------------- // compareTo() //----------------------------------------------------------------------- diff --git a/jdk/test/java/time/tck/java/time/TCKLocalTime.java b/jdk/test/java/time/tck/java/time/TCKLocalTime.java index 1736aca7cc4..ff577095842 100644 --- a/jdk/test/java/time/tck/java/time/TCKLocalTime.java +++ b/jdk/test/java/time/tck/java/time/TCKLocalTime.java @@ -2433,6 +2433,32 @@ public void test_toSecondOfDay() { } } + //----------------------------------------------------------------------- + // toEpochSecond() + //-------------------------------------------------------------------------- + @DataProvider(name="epochSecond") + Object[][] provider__toEpochSecond() { + return new Object[][] { + {LocalTime.of(0, 0).toEpochSecond(LocalDate.of(1970, 1, 1), OFFSET_PTWO), -7200L}, + {LocalTime.of(11, 30).toEpochSecond(LocalDate.of(1965, 12, 31), OFFSET_PTWO), -126282600L}, + {LocalTime.of(11, 30).toEpochSecond(LocalDate.of(1995, 5, 3), OFFSET_MTWO), 799507800L}, + {LocalTime.of(0, 0).toEpochSecond(LocalDate.of(1970, 1, 1), OFFSET_PTWO), + Instant.ofEpochSecond(-7200).getEpochSecond()}, + {LocalTime.of(11, 30).toEpochSecond(LocalDate.of(1969, 12, 31), OFFSET_MTWO), + Instant.ofEpochSecond(-37800L).getEpochSecond()}, + {LocalTime.of(11, 30).toEpochSecond(LocalDate.of(1970, 1, 1), OFFSET_PTWO), + LocalDateTime.of(1970, 1, 1, 11, 30).toEpochSecond(OFFSET_PTWO)}, + }; + } + + @Test(dataProvider="epochSecond") + public void test_toEpochSecond(long actual, long expected) { + assertEquals(actual, expected); + } + + //----------------------------------------------------------------------- + // toSecondOfDay_fromNanoOfDay_symmetry() + //----------------------------------------------------------------------- @Test public void test_toSecondOfDay_fromNanoOfDay_symmetry() { LocalTime t = LocalTime.of(0, 0); diff --git a/jdk/test/java/time/tck/java/time/TCKOffsetTime.java b/jdk/test/java/time/tck/java/time/TCKOffsetTime.java index 0882dfcb19e..016cfc3e9d8 100644 --- a/jdk/test/java/time/tck/java/time/TCKOffsetTime.java +++ b/jdk/test/java/time/tck/java/time/TCKOffsetTime.java @@ -134,6 +134,7 @@ public class TCKOffsetTime extends AbstractDateTimeTest { private static final ZoneId ZONE_GAZA = ZoneId.of("Asia/Gaza"); private static final ZoneOffset OFFSET_PONE = ZoneOffset.ofHours(1); private static final ZoneOffset OFFSET_PTWO = ZoneOffset.ofHours(2); + private static final ZoneOffset OFFSET_MTWO = ZoneOffset.ofHours(-2); private static final LocalDate DATE = LocalDate.of(2008, 12, 3); private OffsetTime TEST_11_30_59_500_PONE; @@ -1148,6 +1149,29 @@ public void test_format_formatter_null() { OffsetTime.of(11, 30, 0, 0, OFFSET_PONE).format(null); } + //----------------------------------------------------------------------- + // toEpochSecond() + //----------------------------------------------------------------------- + @DataProvider(name="epochSecond") + Object[][] provider_toEpochSecond() { + return new Object[][] { + {OffsetTime.of(0, 0, 0, 0, OFFSET_PTWO).toEpochSecond(LocalDate.of(1970, 1, 1)), -7200L}, + {OffsetTime.of(11, 30, 0, 0, OFFSET_MTWO).toEpochSecond(LocalDate.of(1995, 9, 27)), 812208600L}, + {OffsetTime.of(0, 0, 0, 0, OFFSET_PONE).toEpochSecond(LocalDate.of(1970, 1, 1)), + Instant.ofEpochSecond(-3600).getEpochSecond()}, + {OffsetTime.of(11, 30, 0, 0, OFFSET_PTWO).toEpochSecond(LocalDate.of(1965, 12, 31)), + Instant.ofEpochSecond(-126282600L).getEpochSecond()}, + {OffsetTime.of(11, 30, 0, 0, OFFSET_MTWO).toEpochSecond(LocalDate.of(1970, 1, 1)), + OffsetDateTime.of(LocalDate.of(1970, 1, 1), LocalTime.of(11, 30), OFFSET_MTWO) + .toEpochSecond()}, + }; + } + + @Test(dataProvider="epochSecond") + public void test_toEpochSecond(long actual, long expected) { + assertEquals(actual, expected); + } + //----------------------------------------------------------------------- // compareTo() //----------------------------------------------------------------------- From 37fa4dba28df7a2ee09cb7271307c48494eb6ab6 Mon Sep 17 00:00:00 2001 From: Xue-Lei Andrew Fan Date: Thu, 24 Dec 2015 15:22:04 +0000 Subject: [PATCH 62/62] 8146192: Add test for JDK-8049321 Reviewed-by: mullan --- .../net/ssl/TLSv12/SignatureAlgorithms.java | 595 ++++++++++++++++++ 1 file changed, 595 insertions(+) create mode 100644 jdk/test/javax/net/ssl/TLSv12/SignatureAlgorithms.java diff --git a/jdk/test/javax/net/ssl/TLSv12/SignatureAlgorithms.java b/jdk/test/javax/net/ssl/TLSv12/SignatureAlgorithms.java new file mode 100644 index 00000000000..cf5ba477324 --- /dev/null +++ b/jdk/test/javax/net/ssl/TLSv12/SignatureAlgorithms.java @@ -0,0 +1,595 @@ +/* + * 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. + */ + +// +// SunJSSE does not support dynamic system properties, no way to re-use +// system properties in samevm/agentvm mode. +// + +/* + * @test + * @bug 8049321 + * @summary Support SHA256WithDSA in JSSE + * @modules java.base/sun.misc + * @run main/othervm SignatureAlgorithms PKIX "SHA-224,SHA-256" + * TLS_DHE_DSS_WITH_AES_128_CBC_SHA + * @run main/othervm SignatureAlgorithms PKIX "SHA-1,SHA-224" + * TLS_DHE_DSS_WITH_AES_128_CBC_SHA + * @run main/othervm SignatureAlgorithms PKIX "SHA-1,SHA-256" + * TLS_DHE_DSS_WITH_AES_128_CBC_SHA + * @run main/othervm SignatureAlgorithms PKIX "SHA-224,SHA-256" + * TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 + * @run main/othervm SignatureAlgorithms PKIX "SHA-1,SHA-224" + * TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 + * @run main/othervm SignatureAlgorithms PKIX "SHA-1,SHA-256" + * TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 + */ + +import java.net.*; +import java.util.*; +import java.io.*; +import javax.net.ssl.*; +import java.security.Security; +import java.security.KeyStore; +import java.security.KeyFactory; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.security.cert.CertificateFactory; +import java.security.spec.*; +import java.security.interfaces.*; +import sun.misc.BASE64Decoder; + + +public class SignatureAlgorithms { + + /* + * ============================================================= + * Set the various variables needed for the tests, then + * specify what tests to run on each side. + */ + + /* + * Should we run the client or server in a separate thread? + * Both sides can throw exceptions, but do you have a preference + * as to which side should be the main thread. + */ + static boolean separateServerThread = true; + + /* + * Where do we find the keystores? + */ + // Certificates and key (DSA) used in the test. + static String trustedCertStr = + "-----BEGIN CERTIFICATE-----\n" + + "MIIDYTCCAyGgAwIBAgIJAK8/gw6zg/DPMAkGByqGSM44BAMwOzELMAkGA1UEBhMC\n" + + "VVMxDTALBgNVBAoTBEphdmExHTAbBgNVBAsTFFN1bkpTU0UgVGVzdCBTZXJpdmNl\n" + + "MB4XDTE1MTIwMzEzNTIyNVoXDTM2MTExMjEzNTIyNVowOzELMAkGA1UEBhMCVVMx\n" + + "DTALBgNVBAoTBEphdmExHTAbBgNVBAsTFFN1bkpTU0UgVGVzdCBTZXJpdmNlMIIB\n" + + "uDCCASwGByqGSM44BAEwggEfAoGBAPH+b+GSMX6KS7jXDRevzc464DFG4X+uxu5V\n" + + "b3U4yhsU8A8cuH4gwin6L/IDkmZQ7N0zC0jRsiGVSMsFETTq10F39pH2eBfUv/hJ\n" + + "cLfBnIjBEtVqV/dExK88Hul2sZ4mQihQ4issPl7hsroS9EWYicnX0oNAqAB9PO5Y\n" + + "zKbfpL7TAhUA13WW48rln2UP/LaAgtnzKhqcNtMCgYEA3Rv0GirTbAaor8iURd82\n" + + "b5FlDTevOCTuq7ZIpfZVV30neS7cBYNet6m/3/4cfUlbbrqhbqIJ2I+I81drnN0Y\n" + + "lyN4KkuxEcB6OTwfWkIUj6rvPaCQrBH8Q213bDq3HHtYNaP8OoeQUyVXW+SEGADC\n" + + "J1+z8uqP3lIB6ltdgOiV/GQDgYUAAoGBAOXRppuJSGdt6AiZkb81P1DCUgIUlZFI\n" + + "J9GxWrjbbHDmGllMwPNhK6dU7LJKJJuYVPW+95rUGlSJEjRqSlHuyHkNb6e3e7qx\n" + + "tmx1/oIyq+oLult50hBS7uBvLLR0JbIKjBzzkudL8Rjze4G/Wq7KDM2T1JOP49tW\n" + + "eocCvaC8h8uQo4GtMIGqMB0GA1UdDgQWBBT17HcqLllsqnZzP+kElcGcBGmubjBr\n" + + "BgNVHSMEZDBigBT17HcqLllsqnZzP+kElcGcBGmubqE/pD0wOzELMAkGA1UEBhMC\n" + + "VVMxDTALBgNVBAoTBEphdmExHTAbBgNVBAsTFFN1bkpTU0UgVGVzdCBTZXJpdmNl\n" + + "ggkArz+DDrOD8M8wDwYDVR0TAQH/BAUwAwEB/zALBgNVHQ8EBAMCAQYwCQYHKoZI\n" + + "zjgEAwMvADAsAhQ6Y1I6LtIEBMqNo8o6GIe4LLEJuwIUbVQUKi8tvtWyRoxm8AFV\n" + + "0axJYUU=\n" + + "-----END CERTIFICATE-----"; + + static String[] targetCertStr = { + // DSA-SHA1 + "-----BEGIN CERTIFICATE-----\n" + + "MIIDKTCCAumgAwIBAgIJAOy5c0b+8stFMAkGByqGSM44BAMwOzELMAkGA1UEBhMC\n" + + "VVMxDTALBgNVBAoTBEphdmExHTAbBgNVBAsTFFN1bkpTU0UgVGVzdCBTZXJpdmNl\n" + + "MB4XDTE1MTIwMzEzNTIyNVoXDTM1MDgyMDEzNTIyNVowTzELMAkGA1UEBhMCVVMx\n" + + "DTALBgNVBAoMBEphdmExHTAbBgNVBAsMFFN1bkpTU0UgVGVzdCBTZXJpdmNlMRIw\n" + + "EAYDVQQDDAlsb2NhbGhvc3QwggG3MIIBLAYHKoZIzjgEATCCAR8CgYEA8f5v4ZIx\n" + + "fopLuNcNF6/NzjrgMUbhf67G7lVvdTjKGxTwDxy4fiDCKfov8gOSZlDs3TMLSNGy\n" + + "IZVIywURNOrXQXf2kfZ4F9S/+Elwt8GciMES1WpX90TErzwe6XaxniZCKFDiKyw+\n" + + "XuGyuhL0RZiJydfSg0CoAH087ljMpt+kvtMCFQDXdZbjyuWfZQ/8toCC2fMqGpw2\n" + + "0wKBgQDdG/QaKtNsBqivyJRF3zZvkWUNN684JO6rtkil9lVXfSd5LtwFg163qb/f\n" + + "/hx9SVtuuqFuognYj4jzV2uc3RiXI3gqS7ERwHo5PB9aQhSPqu89oJCsEfxDbXds\n" + + "Orcce1g1o/w6h5BTJVdb5IQYAMInX7Py6o/eUgHqW12A6JX8ZAOBhAACgYB+zYqn\n" + + "jJwG4GZpBIN/6qhzbp0flChsV+Trlu0SL0agAQzb6XdI/4JnO87Pgbxaxh3VNAj3\n" + + "3+Ghr1NLBuBfTKzJ4j9msWT3EpLupkMyNtXvBYM0iyMrll67lSjMdv++wLEw35Af\n" + + "/bzVcjGyA5Q0i0cuEzDmHTVfi0OydynbwSLxtKNjMGEwCwYDVR0PBAQDAgPoMB0G\n" + + "A1UdDgQWBBQXJI8AxM0qsYCbbkIMuI5zJ+nMEDAfBgNVHSMEGDAWgBT17HcqLlls\n" + + "qnZzP+kElcGcBGmubjASBgNVHREBAf8ECDAGhwR/AAABMAkGByqGSM44BAMDLwAw\n" + + "LAIUXgyJ0xll4FrZAKXi8bj7Kiz+SA4CFH9WCSZIBYA9lmJkiTgRS7iM/6IC\n" + + "-----END CERTIFICATE-----", + + // DSA-SHA224 + "-----BEGIN CERTIFICATE-----\n" + + "MIIDLzCCAuugAwIBAgIJAOy5c0b+8stGMAsGCWCGSAFlAwQDATA7MQswCQYDVQQG\n" + + "EwJVUzENMAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2\n" + + "Y2UwHhcNMTUxMjAzMTU0NDM5WhcNMzUwODIwMTU0NDM5WjBPMQswCQYDVQQGEwJV\n" + + "UzENMAsGA1UECgwESmF2YTEdMBsGA1UECwwUU3VuSlNTRSBUZXN0IFNlcml2Y2Ux\n" + + "EjAQBgNVBAMMCWxvY2FsaG9zdDCCAbcwggEsBgcqhkjOOAQBMIIBHwKBgQDx/m/h\n" + + "kjF+iku41w0Xr83OOuAxRuF/rsbuVW91OMobFPAPHLh+IMIp+i/yA5JmUOzdMwtI\n" + + "0bIhlUjLBRE06tdBd/aR9ngX1L/4SXC3wZyIwRLValf3RMSvPB7pdrGeJkIoUOIr\n" + + "LD5e4bK6EvRFmInJ19KDQKgAfTzuWMym36S+0wIVANd1luPK5Z9lD/y2gILZ8yoa\n" + + "nDbTAoGBAN0b9Boq02wGqK/IlEXfNm+RZQ03rzgk7qu2SKX2VVd9J3ku3AWDXrep\n" + + "v9/+HH1JW266oW6iCdiPiPNXa5zdGJcjeCpLsRHAejk8H1pCFI+q7z2gkKwR/ENt\n" + + "d2w6txx7WDWj/DqHkFMlV1vkhBgAwidfs/Lqj95SAepbXYDolfxkA4GEAAKBgA81\n" + + "CJKEv+pwiqYgxtw/9rkQ9748WP3mKrEC06kjUG+94/Z9dQloNFFfj6LiO1bymc5l\n" + + "6QIR8XCi4Po3N80K3+WxhBGFhY+RkVWTh43JV8epb41aH2qiWErarBwBGEh8LyGT\n" + + "i30db+Nkz2gfvyz9H/9T0jmYgfLEOlMCusali1qHo2MwYTALBgNVHQ8EBAMCA+gw\n" + + "HQYDVR0OBBYEFBqSP0S4+X+zOCTEnlp2hbAjV/W5MB8GA1UdIwQYMBaAFPXsdyou\n" + + "WWyqdnM/6QSVwZwEaa5uMBIGA1UdEQEB/wQIMAaHBH8AAAEwCwYJYIZIAWUDBAMB\n" + + "AzEAMC4CFQChiRaOnAnsCSJFwdpK22jSxU/mhQIVALgLbj/G39+1Ej8UuSWnEQyU\n" + + "4DA+\n" + + "-----END CERTIFICATE-----", + + // DSA-SHA256 + "-----BEGIN CERTIFICATE-----\n" + + "MIIDLTCCAuugAwIBAgIJAOy5c0b+8stHMAsGCWCGSAFlAwQDAjA7MQswCQYDVQQG\n" + + "EwJVUzENMAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2\n" + + "Y2UwHhcNMTUxMjAzMTU0NjUxWhcNMzUwODIwMTU0NjUxWjBPMQswCQYDVQQGEwJV\n" + + "UzENMAsGA1UECgwESmF2YTEdMBsGA1UECwwUU3VuSlNTRSBUZXN0IFNlcml2Y2Ux\n" + + "EjAQBgNVBAMMCWxvY2FsaG9zdDCCAbcwggEsBgcqhkjOOAQBMIIBHwKBgQDx/m/h\n" + + "kjF+iku41w0Xr83OOuAxRuF/rsbuVW91OMobFPAPHLh+IMIp+i/yA5JmUOzdMwtI\n" + + "0bIhlUjLBRE06tdBd/aR9ngX1L/4SXC3wZyIwRLValf3RMSvPB7pdrGeJkIoUOIr\n" + + "LD5e4bK6EvRFmInJ19KDQKgAfTzuWMym36S+0wIVANd1luPK5Z9lD/y2gILZ8yoa\n" + + "nDbTAoGBAN0b9Boq02wGqK/IlEXfNm+RZQ03rzgk7qu2SKX2VVd9J3ku3AWDXrep\n" + + "v9/+HH1JW266oW6iCdiPiPNXa5zdGJcjeCpLsRHAejk8H1pCFI+q7z2gkKwR/ENt\n" + + "d2w6txx7WDWj/DqHkFMlV1vkhBgAwidfs/Lqj95SAepbXYDolfxkA4GEAAKBgEF7\n" + + "2qiYxGrjX4KCOy0k5nK/RYlgLy4gYDChihQpiaa+fbA5JOBOxPWsh7rdtmJuDrEJ\n" + + "keacU223+DIhOKC49fa+EvhLNqo6U1oPn8n/yvBsvvnWkcynw5KfNzaLlaPmzugh\n" + + "v9xl/GhyZNAXc1QUcW3C+ceHVNrKnkfbTKZz5eRSo2MwYTALBgNVHQ8EBAMCA+gw\n" + + "HQYDVR0OBBYEFNMkPrt40oO9Dpy+bcbQdEvOlNlyMB8GA1UdIwQYMBaAFPXsdyou\n" + + "WWyqdnM/6QSVwZwEaa5uMBIGA1UdEQEB/wQIMAaHBH8AAAEwCwYJYIZIAWUDBAMC\n" + + "Ay8AMCwCFCvA2QiKSe/n+6GqSYQwgQ/zL5M9AhQfSiuWdMJKWpgPJKakvzhBUbMb\n" + + "vA==\n" + + "-----END CERTIFICATE-----"}; + + // Private key in the format of PKCS#8, key size is 1024 bits. + static String[] targetPrivateKey = { + // For cert DSA-SHA1 + "MIIBSwIBADCCASwGByqGSM44BAEwggEfAoGBAPH+b+GSMX6KS7jXDRevzc464DFG\n" + + "4X+uxu5Vb3U4yhsU8A8cuH4gwin6L/IDkmZQ7N0zC0jRsiGVSMsFETTq10F39pH2\n" + + "eBfUv/hJcLfBnIjBEtVqV/dExK88Hul2sZ4mQihQ4issPl7hsroS9EWYicnX0oNA\n" + + "qAB9PO5YzKbfpL7TAhUA13WW48rln2UP/LaAgtnzKhqcNtMCgYEA3Rv0GirTbAao\n" + + "r8iURd82b5FlDTevOCTuq7ZIpfZVV30neS7cBYNet6m/3/4cfUlbbrqhbqIJ2I+I\n" + + "81drnN0YlyN4KkuxEcB6OTwfWkIUj6rvPaCQrBH8Q213bDq3HHtYNaP8OoeQUyVX\n" + + "W+SEGADCJ1+z8uqP3lIB6ltdgOiV/GQEFgIUOiB7J/lrFrNduQ8nDNTe8VspoAI=", + + // For cert DSA-SHA224 + "MIIBSwIBADCCASwGByqGSM44BAEwggEfAoGBAPH+b+GSMX6KS7jXDRevzc464DFG\n" + + "4X+uxu5Vb3U4yhsU8A8cuH4gwin6L/IDkmZQ7N0zC0jRsiGVSMsFETTq10F39pH2\n" + + "eBfUv/hJcLfBnIjBEtVqV/dExK88Hul2sZ4mQihQ4issPl7hsroS9EWYicnX0oNA\n" + + "qAB9PO5YzKbfpL7TAhUA13WW48rln2UP/LaAgtnzKhqcNtMCgYEA3Rv0GirTbAao\n" + + "r8iURd82b5FlDTevOCTuq7ZIpfZVV30neS7cBYNet6m/3/4cfUlbbrqhbqIJ2I+I\n" + + "81drnN0YlyN4KkuxEcB6OTwfWkIUj6rvPaCQrBH8Q213bDq3HHtYNaP8OoeQUyVX\n" + + "W+SEGADCJ1+z8uqP3lIB6ltdgOiV/GQEFgIUOj9F5mxWd9W1tiLSdsOAt8BUBzE=", + + // For cert DSA-SHA256 + "MIIBSwIBADCCASwGByqGSM44BAEwggEfAoGBAPH+b+GSMX6KS7jXDRevzc464DFG\n" + + "4X+uxu5Vb3U4yhsU8A8cuH4gwin6L/IDkmZQ7N0zC0jRsiGVSMsFETTq10F39pH2\n" + + "eBfUv/hJcLfBnIjBEtVqV/dExK88Hul2sZ4mQihQ4issPl7hsroS9EWYicnX0oNA\n" + + "qAB9PO5YzKbfpL7TAhUA13WW48rln2UP/LaAgtnzKhqcNtMCgYEA3Rv0GirTbAao\n" + + "r8iURd82b5FlDTevOCTuq7ZIpfZVV30neS7cBYNet6m/3/4cfUlbbrqhbqIJ2I+I\n" + + "81drnN0YlyN4KkuxEcB6OTwfWkIUj6rvPaCQrBH8Q213bDq3HHtYNaP8OoeQUyVX\n" + + "W+SEGADCJ1+z8uqP3lIB6ltdgOiV/GQEFgIUQ2WGgg+OO39Aujj0e4lM4pP4/9g="}; + + + static char passphrase[] = "passphrase".toCharArray(); + + /* + * Turn on SSL debugging? + */ + static boolean debug = false; + + /* + * Is the server ready to serve? + */ + volatile boolean serverReady = false; + + /* + * Define the server side of the test. + * + * If the server prematurely exits, serverReady will be set to true + * to avoid infinite hangs. + */ + void doServerSide() throws Exception { + + SSLContext context = generateSSLContext( + null, targetCertStr, targetPrivateKey); + SSLServerSocketFactory sslssf = context.getServerSocketFactory(); + try (SSLServerSocket sslServerSocket = + (SSLServerSocket)sslssf.createServerSocket(serverPort)) { + + serverPort = sslServerSocket.getLocalPort(); + + /* + * Signal Client, we're ready for his connect. + */ + serverReady = true; + + try (SSLSocket sslSocket = (SSLSocket)sslServerSocket.accept()) { + sslSocket.setEnabledCipherSuites( + sslSocket.getSupportedCipherSuites()); + InputStream sslIS = sslSocket.getInputStream(); + OutputStream sslOS = sslSocket.getOutputStream(); + + sslIS.read(); + sslOS.write('A'); + sslOS.flush(); + + dumpSignatureAlgorithms(sslSocket); + } + } + } + + /* + * Define the client side of the test. + * + * If the server prematurely exits, serverReady will be set to true + * to avoid infinite hangs. + */ + void doClientSide() throws Exception { + + /* + * Wait for server to get started. + */ + while (!serverReady) { + Thread.sleep(50); + } + + SSLContext context = generateSSLContext(trustedCertStr, null, null); + SSLSocketFactory sslsf = context.getSocketFactory(); + + try (SSLSocket sslSocket = + (SSLSocket)sslsf.createSocket("localhost", serverPort)) { + + // enable TLSv1.2 only + sslSocket.setEnabledProtocols(new String[] {"TLSv1.2"}); + + // enable a block cipher + sslSocket.setEnabledCipherSuites(new String[] {cipherSuite}); + + InputStream sslIS = sslSocket.getInputStream(); + OutputStream sslOS = sslSocket.getOutputStream(); + + sslOS.write('B'); + sslOS.flush(); + sslIS.read(); + + dumpSignatureAlgorithms(sslSocket); + } + } + + static void dumpSignatureAlgorithms(SSLSocket sslSocket) throws Exception { + + boolean isClient = sslSocket.getUseClientMode(); + String mode = "[" + (isClient ? "Client" : "Server") + "]"; + ExtendedSSLSession session = + (ExtendedSSLSession)sslSocket.getSession(); + String[] signAlgs = session.getLocalSupportedSignatureAlgorithms(); + System.out.println( + mode + " local supported signature algorithms: " + + Arrays.asList(signAlgs)); + + if (!isClient) { + signAlgs = session.getPeerSupportedSignatureAlgorithms(); + System.out.println( + mode + " peer supported signature algorithms: " + + Arrays.asList(signAlgs)); + } else { + Certificate[] serverCerts = session.getPeerCertificates(); + + // server should always send the authentication cert. + String sigAlg = ((X509Certificate)serverCerts[0]).getSigAlgName(); + System.out.println( + mode + " the signature algorithm of server certificate: " + + sigAlg); + if (sigAlg.contains("SHA1")) { + if (disabledAlgorithms.contains("SHA-1")) { + throw new Exception( + "Not the expected server certificate. " + + "SHA-1 should be disabled"); + } + } else if (sigAlg.contains("SHA224")) { + if (disabledAlgorithms.contains("SHA-224")) { + throw new Exception( + "Not the expected server certificate. " + + "SHA-224 should be disabled"); + } + } else { // SHA-256 + if (disabledAlgorithms.contains("SHA-256")) { + throw new Exception( + "Not the expected server certificate. " + + "SHA-256 should be disabled"); + } + } + } + } + + /* + * ============================================================= + * The remainder is just support stuff + */ + private static String tmAlgorithm; // trust manager + private static String disabledAlgorithms; // disabled algorithms + private static String cipherSuite; // cipher suite + + private static void parseArguments(String[] args) { + tmAlgorithm = args[0]; + disabledAlgorithms = args[1]; + cipherSuite = args[2]; + } + + private static SSLContext generateSSLContext(String trustedCertStr, + String[] keyCertStrs, String[] keySpecStrs) throws Exception { + + // generate certificate from cert string + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + + // create a key store + KeyStore ks = KeyStore.getInstance("JKS"); + ks.load(null, null); + + // import the trused cert + Certificate trusedCert = null; + ByteArrayInputStream is = null; + if (trustedCertStr != null) { + is = new ByteArrayInputStream(trustedCertStr.getBytes()); + trusedCert = cf.generateCertificate(is); + is.close(); + + ks.setCertificateEntry("DSA Signer", trusedCert); + } + + if (keyCertStrs != null && keyCertStrs.length != 0) { + for (int i = 0; i < keyCertStrs.length; i++) { + String keyCertStr = keyCertStrs[i]; + String keySpecStr = keySpecStrs[i]; + + // generate the private key. + PKCS8EncodedKeySpec priKeySpec = new PKCS8EncodedKeySpec( + new BASE64Decoder().decodeBuffer(keySpecStr)); + KeyFactory kf = KeyFactory.getInstance("DSA"); + DSAPrivateKey priKey = + (DSAPrivateKey)kf.generatePrivate(priKeySpec); + + // generate certificate chain + is = new ByteArrayInputStream(keyCertStr.getBytes()); + Certificate keyCert = cf.generateCertificate(is); + is.close(); + + Certificate[] chain = null; + if (trusedCert != null) { + chain = new Certificate[2]; + chain[0] = keyCert; + chain[1] = trusedCert; + } else { + chain = new Certificate[1]; + chain[0] = keyCert; + } + + // import the key entry. + ks.setKeyEntry("DSA Entry " + i, priKey, passphrase, chain); + } + } + + // create SSL context + TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmAlgorithm); + tmf.init(ks); + + SSLContext ctx = SSLContext.getInstance("TLS"); + if (keyCertStrs != null && keyCertStrs.length != 0) { + KeyManagerFactory kmf = KeyManagerFactory.getInstance("NewSunX509"); + kmf.init(ks, passphrase); + + ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + ks = null; + } else { + ctx.init(null, tmf.getTrustManagers(), null); + } + + return ctx; + } + + + // use any free port by default + volatile int serverPort = 0; + + volatile Exception serverException = null; + volatile Exception clientException = null; + + public static void main(String[] args) throws Exception { + /* + * debug option + */ + if (debug) { + System.setProperty("javax.net.debug", "all"); + } + + /* + * Get the customized arguments. + */ + parseArguments(args); + + + /* + * Ignore testing on Windows if only SHA-224 is available. + */ + if ((Security.getProvider("SunMSCAPI") != null) && + (disabledAlgorithms.contains("SHA-1")) && + (disabledAlgorithms.contains("SHA-256"))) { + + System.out.println( + "Windows system does not support SHA-224 algorithms yet. " + + "Ignore the testing"); + + return; + } + + /* + * Expose the target algorithms by diabling unexpected algorithms. + */ + Security.setProperty( + "jdk.certpath.disabledAlgorithms", disabledAlgorithms); + + /* + * Reset the security property to make sure that the algorithms + * and keys used in this test are not disabled by default. + */ + Security.setProperty( "jdk.tls.disabledAlgorithms", ""); + + /* + * Start the tests. + */ + new SignatureAlgorithms(); + } + + Thread clientThread = null; + Thread serverThread = null; + + /* + * Primary constructor, used to drive remainder of the test. + * + * Fork off the other side, then do your work. + */ + SignatureAlgorithms() throws Exception { + try { + if (separateServerThread) { + startServer(true); + startClient(false); + } else { + startClient(true); + startServer(false); + } + } catch (Exception e) { + // swallow for now. Show later + } + + /* + * Wait for other side to close down. + */ + if (separateServerThread) { + serverThread.join(); + } else { + clientThread.join(); + } + + /* + * When we get here, the test is pretty much over. + * Which side threw the error? + */ + Exception local; + Exception remote; + String whichRemote; + + if (separateServerThread) { + remote = serverException; + local = clientException; + whichRemote = "server"; + } else { + remote = clientException; + local = serverException; + whichRemote = "client"; + } + + /* + * If both failed, return the curthread's exception, but also + * print the remote side Exception + */ + if ((local != null) && (remote != null)) { + System.out.println(whichRemote + " also threw:"); + remote.printStackTrace(); + System.out.println(); + throw local; + } + + if (remote != null) { + throw remote; + } + + if (local != null) { + throw local; + } + } + + void startServer(boolean newThread) throws Exception { + if (newThread) { + serverThread = new Thread() { + public void run() { + try { + doServerSide(); + } catch (Exception e) { + /* + * Our server thread just died. + * + * Release the client, if not active already... + */ + System.err.println("Server died..." + e); + serverReady = true; + serverException = e; + } + } + }; + serverThread.start(); + } else { + try { + doServerSide(); + } catch (Exception e) { + serverException = e; + } finally { + serverReady = true; + } + } + } + + void startClient(boolean newThread) throws Exception { + if (newThread) { + clientThread = new Thread() { + public void run() { + try { + doClientSide(); + } catch (Exception e) { + /* + * Our client thread just died. + */ + System.err.println("Client died..." + e); + clientException = e; + } + } + }; + clientThread.start(); + } else { + try { + doClientSide(); + } catch (Exception e) { + clientException = e; + } + } + } +}