diff --git a/.github/actions/build-jtreg/action.yml b/.github/actions/build-jtreg/action.yml index 3abfa260c17..0ba9937fb45 100644 --- a/.github/actions/build-jtreg/action.yml +++ b/.github/actions/build-jtreg/action.yml @@ -65,4 +65,4 @@ runs: with: name: bundles-jtreg-${{ steps.version.outputs.value }} path: jtreg/installed - retention-days: 1 + retention-days: 5 diff --git a/.github/actions/upload-bundles/action.yml b/.github/actions/upload-bundles/action.yml index b35ee3a42e9..62a8fa58865 100644 --- a/.github/actions/upload-bundles/action.yml +++ b/.github/actions/upload-bundles/action.yml @@ -73,5 +73,5 @@ runs: with: name: bundles-${{ inputs.platform }}${{ inputs.debug-suffix }} path: bundles - retention-days: 1 + retention-days: 5 if: steps.bundles.outputs.bundles-found == 'true' diff --git a/src/java.base/share/classes/java/util/regex/Pattern.java b/src/java.base/share/classes/java/util/regex/Pattern.java index 1e03f56e38e..49233dda6e4 100644 --- a/src/java.base/share/classes/java/util/regex/Pattern.java +++ b/src/java.base/share/classes/java/util/regex/Pattern.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2023, 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 @@ -1090,6 +1090,10 @@ public static Pattern compile(String regex) { * * @throws PatternSyntaxException * If the expression's syntax is invalid + * + * @implNote If {@link #CANON_EQ} is specified and the number of combining + * marks for any character is too large, an {@link java.lang.OutOfMemoryError} + * is thrown. */ public static Pattern compile(String regex, int flags) { return new Pattern(regex, flags); @@ -1123,6 +1127,13 @@ public String toString() { * The character sequence to be matched * * @return A new matcher for this pattern + * + * @implNote When a {@link Pattern} is deserialized, compilation is deferred + * until a direct or indirect invocation of this method. Thus, if a + * deserialized pattern has {@link #CANON_EQ} among its flags and the number + * of combining marks for any character is too large, an + * {@link java.lang.OutOfMemoryError} is thrown, + * as in {@link #compile(String, int)}. */ public Matcher matcher(CharSequence input) { if (!compiled) { @@ -1596,14 +1607,30 @@ private static String[] producePermutations(String input) { return result; } - int length = 1; + /* + * Since + * 12! = 479_001_600 < Integer.MAX_VALUE + * 13! = 6_227_020_800 > Integer.MAX_VALUE + * the computation of n! using int arithmetic will overflow iff + * n < 0 or n > 12 + * + * Here, nCodePoints! is computed in the next for-loop below. + * As nCodePoints >= 0, the computation overflows iff nCodePoints > 12. + * In that case, throw OOME to simulate length > Integer.MAX_VALUE. + */ int nCodePoints = countCodePoints(input); - for(int x=1; x 12) { + throw new OutOfMemoryError("Pattern too complex"); + } + /* Compute length = nCodePoints! */ + int length = 1; + for (int x = 2; x <= nCodePoints; ++x) { + length *= x; + } String[] temp = new String[length]; - int combClass[] = new int[nCodePoints]; + int[] combClass = new int[nCodePoints]; for(int x=0, i=0; xGetDoubleArrayRegion(env, matrix, 0, 4, dmat); diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/Exchange.java b/src/java.net.http/share/classes/jdk/internal/net/http/Exchange.java index b1a1925a0a9..7832909246d 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/Exchange.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/Exchange.java @@ -464,8 +464,8 @@ private CompletableFuture expectContinue(ExchangeImpl ex) { "Unable to handle 101 while waiting for 100"); return MinimalFuture.failedFuture(failed); } - return exchImpl.readBodyAsync(this::ignoreBody, false, parentExecutor) - .thenApply(v -> r1); + exchImpl.expectContinueFailed(rcode); + return MinimalFuture.completedFuture(r1); } }); } diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/ExchangeImpl.java b/src/java.net.http/share/classes/jdk/internal/net/http/ExchangeImpl.java index 17d49c1aa61..852742b6606 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/ExchangeImpl.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/ExchangeImpl.java @@ -260,4 +260,8 @@ HttpBodySubscriberWrapper createResponseSubscriber(HttpResponse.BodyHandler { private final Http1AsyncReceiver asyncReceiver; private volatile EOFException eof; private volatile BodyParser bodyParser; + private volatile boolean closeWhenFinished; // max number of bytes of (fixed length) body to ignore on redirect private static final int MAX_IGNORE = 1024; @@ -404,7 +405,11 @@ public CompletableFuture readBody(HttpResponse.BodySubscriber p, private void onFinished() { asyncReceiver.clear(); - if (return2Cache) { + if (closeWhenFinished) { + if (debug.on()) + debug.log("Closing Connection when finished"); + connection.close(); + } else if (return2Cache) { Log.logTrace("Attempting to return connection to the pool: {0}", connection); // TODO: need to do something here? // connection.setAsyncCallbacks(null, null, null); @@ -416,6 +421,10 @@ private void onFinished() { } } + void closeWhenFinished() { + closeWhenFinished = true; + } + HttpHeaders responseHeaders() { return headers; } diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/HttpClientImpl.java b/src/java.net.http/share/classes/jdk/internal/net/http/HttpClientImpl.java index 96bdda7eb9c..c7cff5cf393 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/HttpClientImpl.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/HttpClientImpl.java @@ -279,23 +279,25 @@ public String toString() { } } - static void registerPending(PendingRequest pending) { + static CompletableFuture registerPending(PendingRequest pending, CompletableFuture res) { // shortcut if cf is already completed: no need to go through the trouble of // registering it - if (pending.cf.isDone()) return; + if (pending.cf.isDone()) return res; var client = pending.client; var cf = pending.cf; var id = pending.id; boolean added = client.pendingRequests.add(pending); // this may immediately remove `pending` from the set is the cf is already completed - pending.ref = cf.whenComplete((r,t) -> client.pendingRequests.remove(pending)); + var ref = res.whenComplete((r,t) -> client.pendingRequests.remove(pending)); + pending.ref = ref; assert added : "request %d was already added".formatted(id); // should not happen, unless the selector manager has already // exited abnormally if (client.selmgr.isClosed()) { pending.abort(client.selmgr.selectorClosedException()); } + return ref; } static void abortPendingRequests(HttpClientImpl client, Throwable reason) { @@ -558,8 +560,9 @@ public boolean registerSubscriber(HttpBodySubscriberWrapper subscriber) { if (debug.on()) { debug.log("body subscriber registered: " + count); } + return true; } - return true; + return false; } } } @@ -875,8 +878,9 @@ private void debugCompleted(String tag, long startNanos, HttpRequest req) { cf = sendAsync(req, responseHandler, null, null); return cf.get(); } catch (InterruptedException ie) { - if (cf != null ) + if (cf != null) { cf.cancel(true); + } throw ie; } catch (ExecutionException e) { final Throwable throwable = e.getCause(); @@ -991,19 +995,23 @@ private void debugCompleted(String tag, long startNanos, HttpRequest req) { (b,t) -> debugCompleted("ClientImpl (async)", start, userRequest)); } - // makes sure that any dependent actions happen in the CF default - // executor. This is only needed for sendAsync(...), when - // exchangeExecutor is non-null. - if (exchangeExecutor != null) { - res = res.whenCompleteAsync((r, t) -> { /* do nothing */}, ASYNC_POOL); - } - // The mexCf is the Cf we need to abort if the SelectorManager thread // is aborted. PendingRequest pending = new PendingRequest(id, requestImpl, mexCf, mex, this); - registerPending(pending); - return res; - } catch(Throwable t) { + res = registerPending(pending, res); + + if (exchangeExecutor != null) { + // makes sure that any dependent actions happen in the CF default + // executor. This is only needed for sendAsync(...), when + // exchangeExecutor is non-null. + return res.isDone() ? res + : res.whenCompleteAsync((r, t) -> { /* do nothing */}, ASYNC_POOL); + } else { + // make a defensive copy that can be safely canceled + // by the caller + return res.isDone() ? res : res.copy(); + } + } catch (Throwable t) { requestUnreference(); debugCompleted("ClientImpl (async)", start, userRequest); throw t; diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/MultiExchange.java b/src/java.net.http/share/classes/jdk/internal/net/http/MultiExchange.java index 92cd223a541..93f4b62672c 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/MultiExchange.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/MultiExchange.java @@ -91,7 +91,7 @@ class MultiExchange implements Cancelable { Exchange previous; volatile Throwable retryCause; volatile boolean expiredOnce; - volatile HttpResponse response = null; + volatile HttpResponse response; // Maximum number of times a request will be retried/redirected // for any reason @@ -274,11 +274,19 @@ public void cancel(IOException cause) { @Override public boolean cancel(boolean mayInterruptIfRunning) { boolean cancelled = this.cancelled; + boolean firstCancel = false; if (!cancelled && mayInterruptIfRunning) { if (interrupted.get() == null) { - interrupted.compareAndSet(null, + firstCancel = interrupted.compareAndSet(null, new CancellationException("Request cancelled")); } + if (debug.on()) { + if (firstCancel) { + debug.log("multi exchange recording: " + interrupted.get()); + } else { + debug.log("multi exchange recorded: " + interrupted.get()); + } + } this.cancelled = true; var exchange = getExchange(); if (exchange != null) { @@ -360,17 +368,30 @@ private CompletableFuture> handleNoBody(Response r, Exchange }).exceptionallyCompose(this::whenCancelled); } + // returns a CancellationExcpetion that wraps the given cause + // if cancel(boolean) was called, the given cause otherwise + private Throwable wrapIfCancelled(Throwable cause) { + CancellationException interrupt = interrupted.get(); + if (interrupt == null) return cause; + + var cancel = new CancellationException(interrupt.getMessage()); + // preserve the stack trace of the original exception to + // show where the call to cancel(boolean) came from + cancel.setStackTrace(interrupt.getStackTrace()); + cancel.initCause(Utils.getCancelCause(cause)); + return cancel; + } + + // if the request failed because the multi exchange was cancelled, + // make sure the reported exception is wrapped in CancellationException private CompletableFuture> whenCancelled(Throwable t) { - CancellationException x = interrupted.get(); - if (x != null) { - // make sure to fail with CancellationException if cancel(true) - // was called. - t = x.initCause(Utils.getCancelCause(t)); + var x = wrapIfCancelled(t); + if (x instanceof CancellationException) { if (debug.on()) { - debug.log("MultiExchange interrupted with: " + t.getCause()); + debug.log("MultiExchange interrupted with: " + x.getCause()); } } - return MinimalFuture.failedFuture(t); + return MinimalFuture.failedFuture(x); } static class NullSubscription implements Flow.Subscription { diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/Stream.java b/src/java.net.http/share/classes/jdk/internal/net/http/Stream.java index b145c1c3fc0..97fc9bd4bfc 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/Stream.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/Stream.java @@ -188,7 +188,7 @@ private void schedule() { if (debug.on()) debug.log("subscribing user subscriber"); subscriber.onSubscribe(userSubscription); } - while (!inputQ.isEmpty()) { + while (!inputQ.isEmpty() && errorRef.get() == null) { Http2Frame frame = inputQ.peek(); if (frame instanceof ResetFrame) { inputQ.remove(); @@ -310,6 +310,13 @@ private boolean consumed(DataFrame df) { return endStream; } + @Override + void expectContinueFailed(int rcode) { + // Have to mark request as sent, due to no request body being sent in the + // event of a 417 Expectation Failed or some other non 100 response code + requestSent(); + } + // This method is called by Http2Connection::decrementStreamCount in order // to make sure that the stream count is decremented only once for // a given stream. @@ -409,6 +416,10 @@ private void sendDataFrame(DataFrame frame) { // pushes entire response body into response subscriber // blocking when required by local or remote flow control CompletableFuture receiveData(BodySubscriber bodySubscriber, Executor executor) { + // ensure that the body subscriber will be subscribed and onError() is + // invoked + pendingResponseSubscriber = bodySubscriber; + // We want to allow the subscriber's getBody() method to block so it // can work with InputStreams. So, we offload execution. responseBodyCF = ResponseSubscribers.getBodyAsync(executor, bodySubscriber, @@ -419,9 +430,6 @@ CompletableFuture receiveData(BodySubscriber bodySubscriber, Executor exec responseBodyCF.completeExceptionally(t); } - // ensure that the body subscriber will be subscribed and onError() is - // invoked - pendingResponseSubscriber = bodySubscriber; sched.runOrSchedule(); // in case data waiting already to be processed, or error return responseBodyCF; diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 613adf1ddb0..f1ffbfe2520 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -80,10 +80,17 @@ gc/g1/logging/TestG1LoggingFailure.java 8169634 generic-all gc/g1/humongousObjects/TestHeapCounters.java 8178918 generic-all gc/shenandoah/TestAllocIntArrays.java#aggressive 8309622 generic-all gc/shenandoah/TestAllocIntArrays.java#iu-aggressive 8309622 generic-all +gc/shenandoah/TestAllocObjectArrays.java#aggressive 8309622 generic-all +gc/shenandoah/TestAllocObjectArrays.java#iu-aggressive 8309622 generic-all +gc/shenandoah/TestJcmdHeapDump.java#aggressive 8309622 generic-all +gc/shenandoah/TestJcmdHeapDump.java#iu-aggressive 8309622 generic-all +gc/shenandoah/TestSieveObjects.java#aggressive 8309622 generic-all +gc/shenandoah/TestSieveObjects.java#iu-aggressive 8309622 generic-all gc/stress/CriticalNativeStress.java#id1 8312028 generic-all gc/stress/gclocker/TestExcessGCLockerCollections.java 8229120 generic-all gc/stress/gclocker/TestGCLockerWithParallel.java 8180622 generic-all gc/stress/gclocker/TestGCLockerWithG1.java 8180622 generic-all +gc/stress/gcold/TestGCOldWithShenandoah.java#aggressive 8309622 generic-all gc/stress/gcold/TestGCOldWithShenandoah.java#iu-aggressive 8309622 generic-all gc/stress/TestJNIBlockFullGC/TestJNIBlockFullGC.java 8192647 generic-all diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index e2937eec0d0..b9d364ad07c 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -447,7 +447,6 @@ java/awt/font/TextLayout/LigatureCaretTest.java 8266312 generic-all java/awt/image/VolatileImage/CustomCompositeTest.java 8199002 windows-all,linux-all java/awt/image/VolatileImage/GradientPaints.java 8199003 linux-all java/awt/JAWT/JAWT.sh 8197798 windows-all,linux-all -java/awt/Debug/DumpOnKey/DumpOnKey.java 8202667 windows-all java/awt/Focus/WindowUpdateFocusabilityTest/WindowUpdateFocusabilityTest.java 8339929 linux-all java/awt/datatransfer/ConstructFlavoredObjectTest/ConstructFlavoredObjectTest.java 8202860 linux-all java/awt/FileDialog/FilenameFilterTest/FilenameFilterTest.java 8202882 linux-all diff --git a/test/jdk/java/awt/Debug/DumpOnKey/DumpOnKey.java b/test/jdk/java/awt/Debug/DumpOnKey/DumpOnKey.java index 5dd99d9e6ea..67b88b0d955 100644 --- a/test/jdk/java/awt/Debug/DumpOnKey/DumpOnKey.java +++ b/test/jdk/java/awt/Debug/DumpOnKey/DumpOnKey.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2021, 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 @@ */ import java.awt.AWTException; +import java.awt.EventQueue; import java.awt.Frame; import java.awt.Robot; import java.awt.Window; @@ -58,9 +59,11 @@ public void list(final PrintStream out, final int indent) { w.setSize(200, 200); w.setLocationRelativeTo(null); w.setVisible(true); + w.toFront(); + w.requestFocus(); final Robot robot = new Robot(); - robot.setAutoDelay(50); + robot.setAutoDelay(100); robot.setAutoWaitForIdle(true); robot.mouseMove(w.getX() + w.getWidth() / 2, w.getY() + w.getHeight() / 2); @@ -74,7 +77,14 @@ public void list(final PrintStream out, final int indent) { robot.keyRelease(KeyEvent.VK_SHIFT); robot.keyRelease(KeyEvent.VK_CONTROL); - w.dispose(); + try { + EventQueue.invokeAndWait(() -> { + w.dispose(); + }); + } catch (Exception e) {} + + robot.delay(2000); + if (dumped != dump) { throw new RuntimeException("Exp:" + dump + ", actual:" + dumped); } diff --git a/test/jdk/java/awt/FileDialog/MultipleMode.java b/test/jdk/java/awt/FileDialog/MultipleMode.java new file mode 100644 index 00000000000..da26f4a20d9 --- /dev/null +++ b/test/jdk/java/awt/FileDialog/MultipleMode.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2010, 2024, 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.BorderLayout; +import java.awt.Button; +import java.awt.Checkbox; +import java.awt.FileDialog; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.Panel; +import java.awt.TextArea; +import java.io.File; + +/* + * @test + * @bug 6467204 + * @summary Need to implement "extended" native FileDialog for JFileChooser + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual MultipleMode +*/ + +public class MultipleMode { + + private static final String INSTRUCTIONS = + """ + 1. Verify that the 'multiple' checkbox is off and press the 'open' button + 2. Verify that the file dialog doesn't allow the multiple file selection + 3. Select any file and close the file dialog + 4. The results will be displayed, verify the results + 5. Turn the 'multiple' checkbox on and press the 'open' button + 6. Verify that the file dialog allows the multiple file selection + 7. Select several files and close the file dialog + 8. The results will be displayed, verify the results. + """; + + public static void main(String[] args) throws Exception { + PassFailJFrame + .builder() + .title("MultipleMode test instructions") + .instructions(INSTRUCTIONS) + .rows(15) + .columns(40) + .position(PassFailJFrame.Position.TOP_LEFT_CORNER) + .testUI(MultipleMode::init) + .build() + .awaitAndCheck(); + } + + private static Frame init() { + Frame frame = new Frame("MultipleMode"); + TextArea sysout = new TextArea("", 20, 70); + sysout.setEditable(false); + + final Checkbox mode = new Checkbox("multiple", false); + + Button open = new Button("open"); + open.addActionListener(e -> { + FileDialog d = new FileDialog(frame); + d.setMultipleMode(mode.getState()); + d.setVisible(true); + + // print the results + sysout.append("DIR:\n"); + sysout.append(" %s\n".formatted(d.getDirectory())); + sysout.append("FILE:\n"); + sysout.append(" %s\n".formatted(d.getFile())); + sysout.append("FILES:\n"); + for (File f : d.getFiles()) { + sysout.append(" %s\n".formatted(f)); + } + }); + + Panel panel = new Panel(new FlowLayout()); + panel.add(mode); + panel.add(open); + + frame.setLayout(new BorderLayout()); + frame.add(panel, BorderLayout.NORTH); + frame.add(sysout, BorderLayout.CENTER); + + frame.pack(); + + return frame; + } +} diff --git a/test/jdk/java/awt/FileDialog/MultipleMode/MultipleMode.html b/test/jdk/java/awt/FileDialog/MultipleMode/MultipleMode.html deleted file mode 100644 index 16118014e23..00000000000 --- a/test/jdk/java/awt/FileDialog/MultipleMode/MultipleMode.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - MultipleMode - - - -

MultipleMode
Bug ID: 6467204

- -

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

- - - - diff --git a/test/jdk/java/awt/FileDialog/MultipleMode/MultipleMode.java b/test/jdk/java/awt/FileDialog/MultipleMode/MultipleMode.java deleted file mode 100644 index e0e7bdf83c7..00000000000 --- a/test/jdk/java/awt/FileDialog/MultipleMode/MultipleMode.java +++ /dev/null @@ -1,289 +0,0 @@ -/* - * Copyright (c) 2010, 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 6467204 - @summary Need to implement "extended" native FileDialog for JFileChooser - @author dmitry.cherepanov@sun.com area=awt.filedialog - @run applet/manual=yesno MultipleMode.html -*/ - -// Note there is no @ in front of test above. This is so that the -// harness will not mistake this file as a test file. It should -// only see the html file as a test file. (the harness runs all -// valid test files, so it would run this test twice if this file -// were valid as well as the html file.) -// Also, note the area= after Your Name in the author tag. Here, you -// should put which functional area the test falls in. See the -// AWT-core home page -> test areas and/or -> AWT team for a list of -// areas. -// There are several places where ManualYesNoTest appear. It is -// recommended that these be changed by a global search and replace, -// such as ESC-% in xemacs. - - - -/** - * MultipleMode.java - * - * summary: - */ - -import java.applet.Applet; -import java.awt.*; -import java.awt.event.*; -import java.io.File; - - -//Manual tests should run as applet tests if possible because they -// get their environments cleaned up, including AWT threads, any -// test created threads, and any system resources used by the test -// such as file descriptors. (This is normally not a problem as -// main tests usually run in a separate VM, however on some platforms -// such as the Mac, separate VMs are not possible and non-applet -// tests will cause problems). Also, you don't have to worry about -// synchronisation stuff in Applet tests the way you do in main -// tests... - - -public class MultipleMode extends Applet -{ - //Declare things used in the test, like buttons and labels here - - public void init() - { - //Create instructions for the user here, as well as set up - // the environment -- set the layout manager, add buttons, - // etc. - this.setLayout (new BorderLayout ()); - - String[] instructions = - { - " 1. Turn the 'multiple' checkbox off and press the 'open' button ", - " 2. Verify that the file dialog doesn't allow the multiple file selection ", - " 3. Select any file and close the file dialog ", - " 4. The results will be displayed, verify the results ", - " 5. Turn the 'multiple' checkbox on and press the 'open' button ", - " 6. Verify that the file dialog allows the multiple file selection ", - " 7. Select several files and close the file dialog ", - " 8. The results will be displayed, verify the results " - }; - Sysout.createDialogWithInstructions( instructions ); - - }//End init() - - public void start () - { - final Checkbox mode = new Checkbox("multiple", true); - Button open = new Button("open"); - open.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - FileDialog d = new FileDialog((Frame)null); - d.setMultipleMode(mode.getState()); - d.setVisible(true); - - // print the results - Sysout.println("DIR:"); - Sysout.println(d.getDirectory()); - Sysout.println("FILE:"); - Sysout.println(d.getFile()); - Sysout.println("FILES:"); - File files[] = d.getFiles(); - for (File f : files) { - Sysout.println(String.valueOf(f)); - } - } - }); - - setLayout(new FlowLayout()); - add(mode); - add(open); - - //Get things going. Request focus, set size, et cetera - setSize (200,200); - setVisible(true); - validate(); - - }// start() - - //The rest of this class is the actions which perform the test... - - //Use Sysout.println to communicate with the user NOT System.out!! - //Sysout.println ("Something Happened!"); - -}// class ManualYesNoTest - -/* Place other classes related to the test after this line */ - - - - - -/**************************************************** - Standard Test Machinery - DO NOT modify anything below -- it's a standard - chunk of code whose purpose is to make user - interaction uniform, and thereby make it simpler - to read and understand someone else's test. - ****************************************************/ - -/** - 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 boolean numbering = false; - private static int messageNumber = 0; - - public static void createDialogWithInstructions( String[] instructions ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - dialog.printInstructions( instructions ); - dialog.setVisible(true); - println( "Any messages for the tester will display here." ); - } - - public static void createDialog( ) - { - dialog = new TestDialog( new Frame(), "Instructions" ); - String[] defInstr = { "Instructions will appear here. ", "" } ; - dialog.printInstructions( defInstr ); - dialog.setVisible(true); - println( "Any messages for the tester will display here." ); - } - - /* Enables message counting for the tester. */ - public static void enableNumbering(boolean enable){ - numbering = enable; - } - - public static void printInstructions( String[] instructions ) - { - dialog.printInstructions( instructions ); - } - - - public static void println( String messageIn ) - { - if (numbering) { - messageIn = "" + messageNumber + " " + messageIn; - messageNumber++; - } - dialog.displayMessage( messageIn ); - } - -}// Sysout class - -/** - 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 Dialog -{ - - TextArea instructionsText; - TextArea messageText; - int maxStringLength = 80; - - //DO NOT call this directly, go through Sysout - public TestDialog( Frame frame, String name ) - { - super( frame, name ); - int scrollBoth = TextArea.SCROLLBARS_BOTH; - instructionsText = new TextArea( "", 15, maxStringLength, scrollBoth ); - add( "North", instructionsText ); - - messageText = new TextArea( "", 5, maxStringLength, scrollBoth ); - add("Center", messageText); - - pack(); - - setVisible(true); - }// TestDialog() - - //DO NOT call this directly, go through Sysout - public void printInstructions( String[] instructions ) - { - //Clear out any current instructions - instructionsText.setText( "" ); - - //Go down array of instruction strings - - String printStr, remainingStr; - for( int i=0; i < instructions.length; i++ ) - { - //chop up each into pieces maxSringLength long - remainingStr = instructions[ i ]; - while( remainingStr.length() > 0 ) - { - //if longer than max then chop off first max chars to print - if( remainingStr.length() >= maxStringLength ) - { - //Try to chop on a word boundary - int posOfSpace = remainingStr. - lastIndexOf( ' ', maxStringLength - 1 ); - - if( posOfSpace <= 0 ) posOfSpace = maxStringLength - 1; - - printStr = remainingStr.substring( 0, posOfSpace + 1 ); - remainingStr = remainingStr.substring( posOfSpace + 1 ); - } - //else just print - else - { - printStr = remainingStr; - remainingStr = ""; - } - - instructionsText.append( printStr + "\n" ); - - }// while - - }// for - - }//printInstructions() - - //DO NOT call this directly, go through Sysout - public void displayMessage( String messageIn ) - { - messageText.append( messageIn + "\n" ); - System.out.println(messageIn); - } - -}// TestDialog class diff --git a/test/jdk/java/awt/FileDialog/SaveFileNameOverrideTest.java b/test/jdk/java/awt/FileDialog/SaveFileNameOverrideTest.java new file mode 100644 index 00000000000..fc88ffa879e --- /dev/null +++ b/test/jdk/java/awt/FileDialog/SaveFileNameOverrideTest.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2011, 2024, 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.JButton; +import java.awt.FileDialog; +import java.awt.Frame; +import java.io.File; + +/* + * @test + * @bug 6998877 8022531 + * @summary After double-click on the folder names, FileNameOverrideTest FAILED + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual SaveFileNameOverrideTest + */ + +public class SaveFileNameOverrideTest { + + private final static String clickDirName = "Directory for double click"; + private static final String INSTRUCTIONS = + """ + 1) Click on the 'Show File Dialog' button. A file dialog will appear. + 2) Double-click on '%s' + 3) Click on a confirmation button. + (It can be 'OK', 'Save' or any other name depending on the platform). + 3) The test will automatically pass or fail. + """ + .formatted(clickDirName); + private final static String dirPath = "."; + + public static void main(String[] args) throws Exception { + System.out.println(System.getProperties()); + System.out.println(new File(dirPath).getAbsolutePath()); + File tmpDir = new File(dirPath + File.separator + clickDirName); + if (!tmpDir.mkdir()) { + throw new RuntimeException("Cannot create directory."); + } + tmpDir.deleteOnExit(); + + PassFailJFrame + .builder() + .title("SaveFileNameOverrideTest Instructions") + .instructions(INSTRUCTIONS) + .splitUIRight(SaveFileNameOverrideTest::getButton) + .rows(8) + .columns(40) + .build() + .awaitAndCheck(); + } + + public static JButton getButton() { + JButton showBtn = new JButton("Show File Dialog"); + showBtn.addActionListener(e -> { + FileDialog fd = + new FileDialog((Frame) null, "Save", FileDialog.SAVE); + + fd.setFile("input"); + fd.setDirectory(new File(dirPath).getAbsolutePath()); + fd.setVisible(true); + + String output = fd.getFile(); + if ("input".equals(output)) { + PassFailJFrame.forcePass(); + } else { + PassFailJFrame.forceFail("TEST FAILED (output file - " + output + ")"); + } + }); + return showBtn; + } +} diff --git a/test/jdk/java/awt/FileDialog/SaveFileNameOverrideTest/SaveFileNameOverrideTest.html b/test/jdk/java/awt/FileDialog/SaveFileNameOverrideTest/SaveFileNameOverrideTest.html deleted file mode 100644 index 0a0d48e48c6..00000000000 --- a/test/jdk/java/awt/FileDialog/SaveFileNameOverrideTest/SaveFileNameOverrideTest.html +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - SaveFileNameOverrideTest - - - -

SaveFileNameOverrideTest
Bug ID: 6260659

- -

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

- - - - diff --git a/test/jdk/java/awt/FileDialog/SaveFileNameOverrideTest/SaveFileNameOverrideTest.java b/test/jdk/java/awt/FileDialog/SaveFileNameOverrideTest/SaveFileNameOverrideTest.java deleted file mode 100644 index 79b5a3a6de0..00000000000 --- a/test/jdk/java/awt/FileDialog/SaveFileNameOverrideTest/SaveFileNameOverrideTest.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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 - @bug 6998877 8022531 - @summary After double-click on the folder names, FileNameOverrideTest FAILED - @author Sergey.Bylokhov@oracle.com area=awt.filedialog - @library ../../regtesthelpers - @build Sysout - @run applet/manual=yesno SaveFileNameOverrideTest.html -*/ - -import test.java.awt.regtesthelpers.Sysout; - -import java.applet.Applet; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.File; - -public class SaveFileNameOverrideTest extends Applet implements ActionListener { - private final static String clickDirName = "Directory for double click"; - private final static String dirPath = "."; - private Button showBtn; - private FileDialog fd; - - public void init() { - this.setLayout(new GridLayout(1, 1)); - - fd = new FileDialog(new Frame(), "Save", FileDialog.SAVE); - - showBtn = new Button("Show File Dialog"); - showBtn.addActionListener(this); - add(showBtn); - - File tmpDir = new File(dirPath + File.separator + clickDirName); - tmpDir.mkdir(); - - String[] instructions = { - "1) Click on 'Show File Dialog' button. A file dialog will come up.", - "2) Double-click on '" + clickDirName + "' and click a confirmation", - " button, it can be 'OK', 'Save' or any other platform-dependent name.", - "3) See result of the test below" - }; - - Sysout.createDialogWithInstructions(instructions); - - }//End init() - - public void start() { - setSize(200, 200); - show(); - }// start() - - public void actionPerformed(ActionEvent e) { - if (e.getSource() == showBtn) { - fd.setFile("input"); - fd.setDirectory(dirPath); - fd.setVisible(true); - String output = fd.getFile(); - if ("input".equals(output)) { - Sysout.println("TEST PASSED"); - } else { - Sysout.println("TEST FAILED (output file - " + output + ")"); - } - } - } -}// class ManualYesNoTest diff --git a/test/jdk/java/awt/FontMetrics/ExtremeFontSizeTest.java b/test/jdk/java/awt/FontMetrics/ExtremeFontSizeTest.java index caa365a3f21..f596f710831 100644 --- a/test/jdk/java/awt/FontMetrics/ExtremeFontSizeTest.java +++ b/test/jdk/java/awt/FontMetrics/ExtremeFontSizeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 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 @@ -24,16 +24,19 @@ import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; +import java.awt.GraphicsEnvironment; import java.awt.Rectangle; import java.awt.font.FontRenderContext; import java.awt.font.GlyphVector; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; +import java.util.HashMap; +import java.util.Map; /* * @test - * @bug 8328896 + * @bug 8328896 8357672 * @summary test that using very large font sizes used don't break later uses */ @@ -49,34 +52,106 @@ public class ExtremeFontSizeTest { static double[] scales = { 1.0, 900.0}; static boolean[] fms = { false, true }; + static class Key { + int fontSize; + double scale; + boolean fm; + String str; + + + Key(int fs, double sc, boolean f, String s) { + fontSize = fs; + scale = sc; + fm = f; + str = s; + } + + public boolean equals(Object o) { + return + (o instanceof Key k) && + this.fontSize == k.fontSize && + this.scale == k.scale && + this.fm == k.fm && + this.str.equals(k.str); + } + + public int hashCode() { + return fontSize + (int)scale + (fm ? 1 : 0) + str.hashCode(); + } + } + + static class Value { + int height; + double strBounds; + Rectangle pixelBounds; + Rectangle2D visualBounds; + + Value(int h, double sb, Rectangle pb, Rectangle2D vb) { + height = h; + strBounds = sb; + pixelBounds = pb; + visualBounds = vb; + } + + public boolean equals(Object o) { + return + (o instanceof Value v) && + this.height == v.height && + this.strBounds == v.strBounds && + this.pixelBounds.equals(v.pixelBounds) && + this.visualBounds.equals(v.visualBounds); + } + + public int hashCode() { + return height + (int)strBounds + pixelBounds.hashCode() + visualBounds.hashCode(); + } + } + + static Map metricsMap = new HashMap(); + public static void main(String[] args) { + Font[] fonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts(); + for (Font f : fonts) { + font = f.deriveFont(Font.PLAIN, 12); + System.out.println("Test font : " + font); + if (font.canDisplayUpTo(testString) != -1) { + System.out.println("Skipping since cannot display test string"); + continue; + } + metricsMap = new HashMap(); + testFont(); + } + } + + static void testFont() { /* run tests validating bounds etc are non-zero * then run with extreme scales for which zero is allowed - but not required * then run the first tests again to be sure they are still reasonable. */ - runTests(); - test(5_000_000, 10_000, false, testString, false); - test(5_000_000, 10_000, true, testString, false); - test(0, 0.00000001, false, testString, false); - runTests(); + runTests(true, false); + test(5_000_000, 10_000, false, testString, false, false, false); + test(5_000_000, 10_000, true, testString, false, false, false); + test(0, 0.00000001, false, testString, false, false, false); + runTests(false, true); if (failed) { throw new RuntimeException("Test failed. Check stdout log."); } } - static void runTests() { + static void runTests(boolean add, boolean check) { for (int fontSize : fontSizes) { for (double scale : scales) { for (boolean fm : fms) { - test(fontSize, scale, fm, testString, true); + test(fontSize, scale, fm, testString, true, add, check); } } } } - static void test(int size, double scale, boolean fm, String str, boolean checkAll) { + static void test(int size, double scale, boolean fm, String str, + boolean checkAll, boolean add, boolean check) { AffineTransform at = AffineTransform.getScaleInstance(scale, scale); FontRenderContext frc = new FontRenderContext(at, false, fm); @@ -114,5 +189,22 @@ static void test(int size, double scale, boolean fm, String str, boolean checkAl System.out.println(" *** RESULTS NOT AS EXPECTED *** "); } System.out.println(); + + Key k = null; + Value v = null; + if (add || check) { + k = new Key(size, scale, fm, str); + v = new Value(height, width, pixelBounds, visualBounds); + } + if (add) { + metricsMap.put(k, v); + } + if (check) { + Value vmap = metricsMap.get(k); + if (!v.equals(vmap)) { + failed = true; + System.out.println("Values differ"); + } + } } } diff --git a/test/jdk/java/net/httpclient/AbstractThrowingSubscribers.java b/test/jdk/java/net/httpclient/AbstractThrowingSubscribers.java index 077ae346462..dc7ca3fe9d5 100644 --- a/test/jdk/java/net/httpclient/AbstractThrowingSubscribers.java +++ b/test/jdk/java/net/httpclient/AbstractThrowingSubscribers.java @@ -474,6 +474,7 @@ private void testThrowing(String uri, boolean sameClient, if (response != null) { finisher.finish(where, response, thrower); } + var tracker = TRACKER.getTracker(client); if (!sameClient) { // Wait for the client to be garbage collected. // we use the ReferenceTracker API rather than HttpClient::close here, @@ -482,7 +483,6 @@ private void testThrowing(String uri, boolean sameClient, // By using the ReferenceTracker, we will get some diagnosis about what // is keeping the client alive if it doesn't get GC'ed within the // expected time frame. - var tracker = TRACKER.getTracker(client); client = null; System.gc(); System.out.println(now() + "waiting for client to shutdown: " + tracker.getName()); @@ -491,6 +491,14 @@ private void testThrowing(String uri, boolean sameClient, if (error != null) throw error; System.out.println(now() + "client shutdown normally: " + tracker.getName()); System.err.println(now() + "client shutdown normally: " + tracker.getName()); + } else { + System.out.println(now() + "waiting for operation to finish: " + tracker.getName()); + System.err.println(now() + "waiting for operation to finish: " + tracker.getName()); + var error = TRACKER.checkFinished(tracker, 10000); + if (error != null) throw error; + System.out.println(now() + "operation finished normally: " + tracker.getName()); + System.err.println(now() + "operation finished normally: " + tracker.getName()); + } } } @@ -800,7 +808,7 @@ public void teardown() throws Exception { sharedClient == null ? null : sharedClient.toString(); sharedClient = null; Thread.sleep(100); - AssertionError fail = TRACKER.check(500); + AssertionError fail = TRACKER.check(5000); try { httpTestServer.stop(); httpsTestServer.stop(); diff --git a/test/jdk/java/net/httpclient/ExpectContinueTest.java b/test/jdk/java/net/httpclient/ExpectContinueTest.java new file mode 100644 index 00000000000..97d1676fdde --- /dev/null +++ b/test/jdk/java/net/httpclient/ExpectContinueTest.java @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2022, 2023, 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. + */ + +/* + * @test + * @summary Tests that when the httpclient sends a 100 Expect Continue header and receives + * a response code of 417 Expectation Failed, that the client does not hang + * indefinitely and closes the connection. + * @bug 8286171 + * @library /test/lib /test/jdk/java/net/httpclient/lib + * @build jdk.httpclient.test.lib.common.HttpServerAdapters + * @run testng/othervm ExpectContinueTest + */ + + +import com.sun.net.httpserver.HttpServer; + +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import javax.net.ServerSocketFactory; +import java.io.BufferedReader; +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.Writer; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.StringTokenizer; +import java.util.concurrent.CompletableFuture; +import jdk.httpclient.test.lib.common.HttpServerAdapters; +import jdk.httpclient.test.lib.http2.Http2TestServer; + +import static java.net.http.HttpClient.Version.HTTP_1_1; +import static java.net.http.HttpClient.Version.HTTP_2; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.testng.Assert.assertEquals; + +public class ExpectContinueTest implements HttpServerAdapters { + + HttpTestServer http1TestServer; // HTTP/1.1 + Http1HangServer http1HangServer; + HttpTestServer http2TestServer; // HTTP/2 + + URI getUri; + URI postUri; + URI hangUri; + URI h2getUri; + URI h2postUri; + URI h2hangUri; + + static final String EXPECTATION_FAILED_417 = "417 Expectation Failed"; + + @BeforeTest + public void setup() throws Exception { + InetSocketAddress saHang = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); + + http1TestServer = HttpTestServer.create(HTTP_1_1); + http1TestServer.addHandler(new GetHandler(), "/http1/get"); + http1TestServer.addHandler(new PostHandler(), "/http1/post"); + getUri = URI.create("http://" + http1TestServer.serverAuthority() + "/http1/get"); + postUri = URI.create("http://" + http1TestServer.serverAuthority() + "/http1/post"); + + // Due to limitations of the above Http1 Server, a manual approach is taken to test the hanging with the + // httpclient using Http1 so that the correct response header can be returned for the test case + http1HangServer = new Http1HangServer(saHang); + hangUri = URI.create("http://" + http1HangServer.ia.getCanonicalHostName() + ":" + http1HangServer.port + "/http1/hang"); + + + http2TestServer = HttpTestServer.create(HTTP_2); + http2TestServer.addHandler(new GetHandler(), "/http2/get"); + http2TestServer.addHandler(new PostHandler(), "/http2/post"); + http2TestServer.addHandler(new PostHandlerCantContinue(), "/http2/hang"); + h2getUri = URI.create("http://" + http2TestServer.serverAuthority() + "/http2/get"); + h2postUri = URI.create("http://" + http2TestServer.serverAuthority() + "/http2/post"); + h2hangUri = URI.create("http://" + http2TestServer.serverAuthority() + "/http2/hang"); + + http1TestServer.start(); + http1HangServer.start(); + http2TestServer.start(); + } + + @AfterTest + public void teardown() throws IOException { + http1TestServer.stop(); + http1HangServer.close(); + http2TestServer.stop(); + } + + static class GetHandler implements HttpTestHandler { + + @Override + public void handle(HttpTestExchange exchange) throws IOException { + try (InputStream is = exchange.getRequestBody(); + OutputStream os = exchange.getResponseBody()) { + is.readAllBytes(); + byte[] bytes = "RESPONSE_BODY".getBytes(UTF_8); + exchange.sendResponseHeaders(200, bytes.length); + os.write(bytes); + } + } + } + + static class PostHandler implements HttpTestHandler { + + @Override + public void handle(HttpTestExchange exchange) throws IOException { + // Http1 server has already sent 100 response at this point but not Http2 server + if (exchange.getExchangeVersion().equals(HttpClient.Version.HTTP_2)) { + // Send 100 Headers, tell client that we're ready for body + exchange.sendResponseHeaders(100, 0); + } + + // Read body from client and acknowledge with 200 + try (InputStream is = exchange.getRequestBody(); + OutputStream os = exchange.getResponseBody()) { + is.readAllBytes(); + exchange.sendResponseHeaders(200, 0); + } + } + } + + static class PostHandlerCantContinue implements HttpTestHandler { + + @Override + public void handle(HttpTestExchange exchange) throws IOException { + //Send 417 Headers, tell client to not send body + try (InputStream is = exchange.getRequestBody(); + OutputStream os = exchange.getResponseBody()) { + byte[] bytes = EXPECTATION_FAILED_417.getBytes(); + exchange.sendResponseHeaders(417, bytes.length); + os.write(bytes); + } + } + } + + static class Http1HangServer extends Thread implements Closeable { + + final ServerSocket ss; + final InetAddress ia; + final int port; + volatile boolean closed = false; + volatile Socket client; + + Http1HangServer(InetSocketAddress sa) throws IOException { + ss = ServerSocketFactory.getDefault() + .createServerSocket(sa.getPort(), -1, sa.getAddress()); + ia = ss.getInetAddress(); + port = ss.getLocalPort(); + } + + @Override + public void run() { + byte[] bytes = EXPECTATION_FAILED_417.getBytes(); + + while (!closed) { + try { + // Not using try with resources here as we expect the client to close resources when + // 417 is received + client = ss.accept(); + InputStream is = client.getInputStream(); + OutputStream os = client.getOutputStream(); + + BufferedReader reader = new BufferedReader(new InputStreamReader(is)); + Writer w = new OutputStreamWriter(os, UTF_8); + PrintWriter pw = new PrintWriter(w); + + StringBuilder response = new StringBuilder(); + String line = null; + StringBuilder reqBuilder = new StringBuilder(); + while (!(line = reader.readLine()).isEmpty()) { + reqBuilder.append(line + "\r\n"); + } + String req = reqBuilder.toString(); + System.err.println("Http1HangServer received: " + req); + StringTokenizer tokenizer = new StringTokenizer(req); + String method = tokenizer.nextToken(); + String path = tokenizer.nextToken(); + String version = tokenizer.nextToken(); + + boolean validRequest = method.equals("POST") && path.equals("/http1/hang") + && version.equals("HTTP/1.1"); + // If correct request, send 417 reply. Otherwise, wait for correct one + if (validRequest) { + closed = true; + response.append("HTTP/1.1 417 Expectation Failed\r\n") + .append("Content-Length: ") + .append(0) + .append("\r\n\r\n"); + pw.print(response); + pw.flush(); + + os.write(bytes); + os.flush(); + } else { + client.close(); + } + } catch (IOException e) { + closed = true; + e.printStackTrace(); + } + } + } + + @Override + public void close() throws IOException { + if (client != null) client.close(); + if (ss != null) ss.close(); + } + } + + @DataProvider(name = "uris") + public Object[][] urisData() { + return new Object[][]{ + { getUri, postUri, hangUri, HTTP_1_1 }, + { h2getUri, h2postUri, h2hangUri, HttpClient.Version.HTTP_2 } + }; + } + + @Test(dataProvider = "uris") + public void test(URI getUri, URI postUri, URI hangUri, HttpClient.Version version) throws IOException, InterruptedException { + HttpClient client = HttpClient.newBuilder() + .version(version) + .build(); + + HttpRequest getRequest = HttpRequest.newBuilder(getUri) + .GET() + .build(); + + HttpRequest postRequest = HttpRequest.newBuilder(postUri) + .POST(HttpRequest.BodyPublishers.ofString("Sample Post")) + .expectContinue(true) + .build(); + + HttpRequest hangRequest = HttpRequest.newBuilder(hangUri) + .POST(HttpRequest.BodyPublishers.ofString("Sample Post")) + .expectContinue(true) + .build(); + + CompletableFuture> cf = client.sendAsync(getRequest, HttpResponse.BodyHandlers.ofString()); + HttpResponse resp = cf.join(); + System.err.println("Response Headers: " + resp.headers()); + System.err.println("Response Status Code: " + resp.statusCode()); + assertEquals(resp.statusCode(), 200); + + cf = client.sendAsync(postRequest, HttpResponse.BodyHandlers.ofString()); + resp = cf.join(); + System.err.println("Response Headers: " + resp.headers()); + System.err.println("Response Status Code: " + resp.statusCode()); + assertEquals(resp.statusCode(), 200); + + cf = client.sendAsync(hangRequest, HttpResponse.BodyHandlers.ofString()); + resp = cf.join(); + System.err.println("Response Headers: " + resp.headers()); + System.err.println("Response Status Code: " + resp.statusCode()); + assertEquals(resp.statusCode(), 417); + } + +} diff --git a/test/jdk/java/net/httpclient/HttpGetInCancelledFuture.java b/test/jdk/java/net/httpclient/HttpGetInCancelledFuture.java new file mode 100644 index 00000000000..4b38adebeb1 --- /dev/null +++ b/test/jdk/java/net/httpclient/HttpGetInCancelledFuture.java @@ -0,0 +1,424 @@ +/* + * Copyright (c) 2023, 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.IOException; +import java.io.UncheckedIOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpClient.Version; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.time.Duration; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; + +import jdk.internal.net.http.common.OperationTrackers.Tracker; +import jdk.test.lib.net.SimpleSSLContext; +import jdk.test.lib.net.URIBuilder; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/* + * @test + * @bug 8316580 + * @library /test/lib + * @run junit/othervm -Djdk.tracePinnedThreads=full + * -DuseReferenceTracker=true + * HttpGetInCancelledFuture + * @summary This test verifies that cancelling a future that + * does an HTTP request using the HttpClient doesn't cause + * HttpClient::close to block forever. + */ +public class HttpGetInCancelledFuture { + + static final boolean useTracker = Boolean.getBoolean("useReferenceTracker"); + + static final class TestException extends RuntimeException { + public TestException(String message, Throwable cause) { + super(message, cause); + } + } + + static ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; + + HttpClient makeClient(URI uri, Version version, Executor executor) { + var builder = HttpClient.newBuilder(); + if (uri.getScheme().equalsIgnoreCase("https")) { + try { + builder.sslContext(new SimpleSSLContext().get()); + } catch (IOException io) { + throw new UncheckedIOException(io); + } + } + return builder.connectTimeout(Duration.ofSeconds(1)) + .executor(executor) + .version(version) + .build(); + } + + record TestCase(String url, int reqCount, Version version) {} + // A server that doesn't accept + static volatile ServerSocket NOT_ACCEPTING; + + static List parameters() { + ServerSocket ss = NOT_ACCEPTING; + if (ss == null) { + synchronized (HttpGetInCancelledFuture.class) { + if ((ss = NOT_ACCEPTING) == null) { + try { + ss = new ServerSocket(); + var loopback = InetAddress.getLoopbackAddress(); + ss.bind(new InetSocketAddress(loopback, 0), 10); + NOT_ACCEPTING = ss; + } catch (IOException io) { + throw new UncheckedIOException(io); + } + } + } + } + URI http = URIBuilder.newBuilder() + .loopback() + .scheme("http") + .port(ss.getLocalPort()) + .path("/not-accepting/") + .buildUnchecked(); + URI https = URIBuilder.newBuilder() + .loopback() + .scheme("https") + .port(ss.getLocalPort()) + .path("/not-accepting/") + .buildUnchecked(); + // use all HTTP versions, without and with TLS + return List.of( + new TestCase(http.toString(), 200, Version.HTTP_2), + new TestCase(http.toString(), 200, Version.HTTP_1_1), + new TestCase(https.toString(), 200, Version.HTTP_2), + new TestCase(https.toString(), 200, Version.HTTP_1_1) + ); + } + + @ParameterizedTest + @MethodSource("parameters") + void runTest(TestCase test) { + System.out.println("Testing with: " + test); + runTest(test.url, test.reqCount, test.version); + } + + static class TestTaskScope implements AutoCloseable { + final ExecutorService pool = new ForkJoinPool(); + final Map, Future> tasks = new ConcurrentHashMap<>(); + final AtomicReference failed = new AtomicReference<>(); + + class Task implements Callable { + final Callable task; + final CompletableFuture cf = new CompletableFuture<>(); + Task(Callable task) { + this.task = task; + } + @Override + public T call() throws Exception { + try { + var res = task.call(); + cf.complete(res); + return res; + } catch (Throwable t) { + cf.completeExceptionally(t); + throw t; + } + } + CompletableFuture cf() { + return cf; + } + } + + + static class ShutdownOnFailure extends TestTaskScope { + public ShutdownOnFailure() {} + + @Override + protected void completed(Task task, T result, Throwable throwable) { + super.completed(task, result, throwable); + if (throwable != null) { + if (failed.get() == null) { + ExecutionException ex = throwable instanceof ExecutionException x + ? x : new ExecutionException(throwable); + failed.compareAndSet(null, ex); + } + tasks.entrySet().forEach(this::cancel); + } + } + + void cancel(Map.Entry, Future> entry) { + entry.getValue().cancel(true); + entry.getKey().cf().cancel(true); + tasks.remove(entry.getKey(), entry.getValue()); + } + + @Override + public CompletableFuture fork(Callable callable) { + var ex = failed.get(); + if (ex == null) { + return super.fork(callable); + } // otherwise do nothing + return CompletableFuture.failedFuture(new RejectedExecutionException()); + } + } + + public CompletableFuture fork(Callable callable) { + var task = new Task<>(callable); + var res = pool.submit(task); + tasks.put(task, res); + task.cf.whenComplete((r,t) -> completed(task, r, t)); + return task.cf; + } + + protected void completed(Task task, T result, Throwable throwable) { + tasks.remove(task); + } + + public void join() throws InterruptedException { + try { + var cfs = tasks.keySet().stream() + .map(Task::cf).toArray(CompletableFuture[]::new); + CompletableFuture.allOf(cfs).get(); + } catch (InterruptedException it) { + throw it; + } catch (ExecutionException ex) { + failed.compareAndSet(null, ex); + } + } + + public void throwIfFailed() throws ExecutionException { + ExecutionException x = failed.get(); + if (x != null) throw x; + } + + public void close() { + // ForkJoinPool does not implement AutoClosable in 17. + //pool.close(); + // Taking the full implementation of close() from 21 + // does not work. Some methods are not public. + pool.shutdownNow(); + try { + pool.awaitTermination(1, TimeUnit.DAYS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + + ExecutorService testExecutor() { + return Executors.newCachedThreadPool(); + } + + void runTest(String url, int reqCount, Version version) { + final var dest = URI.create(url); + ExecutorService executor = null; + try { + executor = testExecutor(); + HttpClient httpClient = makeClient(dest, version, executor); + TRACKER.track(httpClient); + Tracker tracker = TRACKER.getTracker(httpClient); + Throwable failed = null; + try { + try (final var scope = new TestTaskScope.ShutdownOnFailure()) { + launchAndProcessRequests(scope, httpClient, reqCount, dest); + } finally { + System.out.printf("StructuredTaskScope closed: STARTED=%s, SUCCESS=%s, INTERRUPT=%s, FAILED=%s%n", + STARTED.get(), SUCCESS.get(), INTERRUPT.get(), FAILED.get()); + } + System.out.println("ERROR: Expected TestException not thrown"); + throw new AssertionError("Expected TestException not thrown"); + } catch (TestException x) { + System.out.println("Got expected exception: " + x); + } catch (Throwable t) { + System.out.println("ERROR: Unexpected exception: " + t); + failed = t; + throw t; + } finally { + // we can either use the tracker or call HttpClient::close + if (useTracker) { + // using the tracker depends on GC but will give us some diagnostic + // if some operations are not properly cancelled and prevent the client + // from terminating + httpClient = null; + System.gc(); + System.out.println(TRACKER.diagnose(tracker)); + var error = TRACKER.check(tracker, 10000); + if (error != null) { + if (failed != null) error.addSuppressed(failed); + EXCEPTIONS.forEach(x -> { + System.out.println("FAILED: " + x); + }); + EXCEPTIONS.forEach(x -> { + x.printStackTrace(System.out); + }); + throw error; + } + } else { + // if not all operations terminate, close() will block + // forever and the test will fail in jtreg timeout. + // there will be no diagnostic. + //httpClient.close(); + // HttpClient does not implement AutoClosable in 17. + // Omitting this test variant. + } + System.out.println("HttpClient closed"); + } + } finally { + // ExecutorService does not implement AutoClosable in 17. + // Taken from ExecutorService close() implementation in 21. + if (executor != null) { + boolean terminated = executor.isTerminated(); + if (!terminated) { + executor.shutdown(); + boolean interrupted = false; + while (!terminated) { + try { + terminated = executor.awaitTermination(1L, TimeUnit.DAYS); + } catch (InterruptedException e) { + if (!interrupted) { + executor.shutdownNow(); + interrupted = true; + } + } + } + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } + System.out.println("ThreadExecutor closed"); + } + // not all tasks may have been started before the scope was cancelled + // due to the first connect/timeout exception, but all tasks that started + // must have either succeeded, be interrupted, or failed + assertTrue(STARTED.get() > 0); + assertEquals(STARTED.get(), SUCCESS.get() + INTERRUPT.get() + FAILED.get()); + if (SUCCESS.get() > 0) { + // we don't expect any server to be listening and responding + System.out.println("WARNING: got some unexpected successful responses from " + + "\"" + NOT_ACCEPTING.getLocalSocketAddress() + "\": " + SUCCESS.get()); + } + } + + private void launchAndProcessRequests( + TestTaskScope.ShutdownOnFailure scope, + HttpClient httpClient, + int reqCount, + URI dest) { + for (int counter = 0; counter < reqCount; counter++) { + scope.fork(() -> + getAndCheck(httpClient, dest) + ); + } + try { + scope.join(); + } catch (InterruptedException e) { + throw new AssertionError("scope.join() was interrupted", e); + } + try { + scope.throwIfFailed(); + } catch (ExecutionException e) { + throw new TestException("something threw an exception in StructuredTaskScope", e); + } + } + + final static AtomicLong ID = new AtomicLong(); + final AtomicLong SUCCESS = new AtomicLong(); + final AtomicLong INTERRUPT = new AtomicLong(); + final AtomicLong FAILED = new AtomicLong(); + final AtomicLong STARTED = new AtomicLong(); + final CopyOnWriteArrayList EXCEPTIONS = new CopyOnWriteArrayList<>(); + private String getAndCheck(HttpClient httpClient, URI url) { + STARTED.incrementAndGet(); + final var response = sendRequest(httpClient, url); + String res = response.body(); + int statusCode = response.statusCode(); + assertEquals(200, statusCode); + return res; + } + + private HttpResponse sendRequest(HttpClient httpClient, URI url) { + var id = ID.incrementAndGet(); + try { + var request = HttpRequest.newBuilder(url).GET().build(); + var response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + // System.out.println("Got response for " + id + ": " + response); + SUCCESS.incrementAndGet(); + return response; + } catch (InterruptedException e) { + INTERRUPT.incrementAndGet(); + // System.out.println("Got interrupted for " + id + ": " + e); + throw new RuntimeException(e); + } catch (Exception e) { + FAILED.incrementAndGet(); + EXCEPTIONS.add(e); + //System.out.println("Got exception for " + id + ": " + e); + throw new RuntimeException(e); + } + } + + @AfterAll + static void tearDown() { + try { + System.gc(); + var error = TRACKER.check(5000); + if (error != null) throw error; + } finally { + ServerSocket ss; + synchronized (HttpGetInCancelledFuture.class) { + ss = NOT_ACCEPTING; + NOT_ACCEPTING = null; + } + if (ss != null) { + try { + ss.close(); + } catch (IOException io) { + throw new UncheckedIOException(io); + } + } + } + } +} + diff --git a/test/jdk/java/net/httpclient/ReferenceTracker.java b/test/jdk/java/net/httpclient/ReferenceTracker.java index ae55f969c71..d7e16d01201 100644 --- a/test/jdk/java/net/httpclient/ReferenceTracker.java +++ b/test/jdk/java/net/httpclient/ReferenceTracker.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2023, 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,14 @@ public StringBuilder diagnose(StringBuilder warnings) { return diagnose(warnings, (t) -> t.getOutstandingHttpOperations() > 0); } + public StringBuilder diagnose(Tracker tracker) { + return diagnose(tracker, new StringBuilder(), (t) -> t.getOutstandingHttpOperations() > 0); + } + + public StringBuilder diagnose(HttpClient client) { + return diagnose(getTracker(client)); + } + public StringBuilder diagnose(Tracker tracker, StringBuilder warnings, Predicate hasOutstanding) { checkOutstandingOperations(warnings, tracker, hasOutstanding); return warnings; @@ -107,6 +115,14 @@ public AssertionError check(Tracker tracker, long graceDelayMs) { "outstanding operations or unreleased resources", true); } + public AssertionError checkFinished(Tracker tracker, long graceDelayMs) { + Predicate hasOperations = (t) -> t.getOutstandingOperations() > 0; + Predicate hasSubscribers = (t) -> t.getOutstandingSubscribers() > 0; + return check(tracker, graceDelayMs, + hasOperations.or(hasSubscribers), + "outstanding operations or unreleased resources", false); + } + public AssertionError check(long graceDelayMs) { Predicate hasOperations = (t) -> t.getOutstandingOperations() > 0; Predicate hasSubscribers = (t) -> t.getOutstandingSubscribers() > 0; @@ -238,6 +254,11 @@ public AssertionError check(Tracker tracker, } long duration = Duration.ofNanos(System.nanoTime() - waitStart).toMillis(); if (hasOutstanding.test(tracker)) { + if (i == 0 && waited == 0) { + // we found nothing and didn't wait expecting success, but then found + // something. Respin to make sure we wait. + return check(tracker, graceDelayMs, hasOutstanding, description, printThreads); + } StringBuilder warnings = diagnose(tracker, new StringBuilder(), hasOutstanding); if (hasOutstanding.test(tracker)) { fail = new AssertionError(warnings.toString()); @@ -294,6 +315,11 @@ public AssertionError check(long graceDelayMs, } long duration = Duration.ofNanos(System.nanoTime() - waitStart).toMillis(); if (TRACKERS.stream().anyMatch(hasOutstanding)) { + if (i == 0 && waited == 0) { + // we found nothing and didn't wait expecting success, but then found + // something. Respin to make sure we wait. + return check(graceDelayMs, hasOutstanding, description, printThreads); + } StringBuilder warnings = diagnose(new StringBuilder(), hasOutstanding); addSummary(warnings); if (TRACKERS.stream().anyMatch(hasOutstanding)) { diff --git a/test/jdk/javax/rmi/ssl/SSLSocketParametersTest.java b/test/jdk/javax/rmi/ssl/SSLSocketParametersTest.java index 63d9332f353..6da32894587 100644 --- a/test/jdk/javax/rmi/ssl/SSLSocketParametersTest.java +++ b/test/jdk/javax/rmi/ssl/SSLSocketParametersTest.java @@ -24,6 +24,7 @@ /* * @test * @bug 5016500 + * @library /test/lib/ * @summary Test SslRmi[Client|Server]SocketFactory SSL socket parameters. * @run main/othervm SSLSocketParametersTest 1 * @run main/othervm SSLSocketParametersTest 2 @@ -33,14 +34,15 @@ * @run main/othervm SSLSocketParametersTest 6 * @run main/othervm SSLSocketParametersTest 7 */ +import jdk.test.lib.Asserts; + import java.io.IOException; import java.io.File; import java.io.Serializable; -import java.net.ServerSocket; -import java.net.Socket; +import java.lang.ref.Reference; +import java.rmi.ConnectIOException; import java.rmi.Remote; import java.rmi.RemoteException; -import java.rmi.server.RMIClientSocketFactory; import java.rmi.server.RMIServerSocketFactory; import java.rmi.server.UnicastRemoteObject; import javax.net.ssl.SSLContext; @@ -50,121 +52,30 @@ public class SSLSocketParametersTest implements Serializable { public interface Hello extends Remote { - public String sayHello() throws RemoteException; + String sayHello() throws RemoteException; } - public class HelloImpl extends UnicastRemoteObject implements Hello { - - public HelloImpl(int port, - RMIClientSocketFactory csf, - RMIServerSocketFactory ssf) - throws RemoteException { - super(port, csf, ssf); - } - + public class HelloImpl implements Hello { public String sayHello() { return "Hello World!"; } - - public Remote runServer() throws IOException { - System.out.println("Inside HelloImpl::runServer"); - // Get a remote stub for this RMI object - // - Remote stub = toStub(this); - System.out.println("Stub = " + stub); - return stub; - } - } - - public class HelloClient { - - public void runClient(Remote stub) throws IOException { - System.out.println("Inside HelloClient::runClient"); - // "obj" is the identifier that we'll use to refer - // to the remote object that implements the "Hello" - // interface - Hello obj = (Hello) stub; - String message = obj.sayHello(); - System.out.println(message); - } - } - - public static class ClientFactory extends SslRMIClientSocketFactory { - - public ClientFactory() { - super(); - } - - public Socket createSocket(String host, int port) throws IOException { - System.out.println("ClientFactory::Calling createSocket(" + - host + "," + port + ")"); - return super.createSocket(host, port); - } - } - - public static class ServerFactory extends SslRMIServerSocketFactory { - - public ServerFactory() { - super(); - } - - public ServerFactory(String[] ciphers, - String[] protocols, - boolean need) { - super(ciphers, protocols, need); - } - - public ServerFactory(SSLContext context, - String[] ciphers, - String[] protocols, - boolean need) { - super(context, ciphers, protocols, need); - } - - public ServerSocket createServerSocket(int port) throws IOException { - System.out.println("ServerFactory::Calling createServerSocket(" + - port + ")"); - return super.createServerSocket(port); - } } - public void testRmiCommunication(RMIServerSocketFactory serverFactory, boolean expectException) { - - HelloImpl server = null; + public void testRmiCommunication(RMIServerSocketFactory serverSocketFactory) throws Exception { + HelloImpl server = new HelloImpl(); + Hello stub = (Hello)UnicastRemoteObject.exportObject(server, + 0, new SslRMIClientSocketFactory(), serverSocketFactory); try { - server = new HelloImpl(0, - new ClientFactory(), - serverFactory); - Remote stub = server.runServer(); - HelloClient client = new HelloClient(); - client.runClient(stub); - if (expectException) { - throw new RuntimeException("Test completed without throwing an expected exception."); - } - - } catch (IOException exc) { - if (!expectException) { - throw new RuntimeException("An error occurred during test execution", exc); - } else { - System.out.println("Caught expected exception: " + exc); - } - + String msg = stub.sayHello(); + Asserts.assertEquals("Hello World!", msg); + } finally { + Reference.reachabilityFence(server); } } - private static void testServerFactory(String[] cipherSuites, String[] protocol, String expectedMessage) throws Exception { - try { - new ServerFactory(SSLContext.getDefault(), + private static void testSslServerSocketFactory(String[] cipherSuites, String[] protocol) throws Exception { + new SslRMIServerSocketFactory(SSLContext.getDefault(), cipherSuites, protocol, false); - throw new RuntimeException( - "The expected exception for "+ expectedMessage + " was not thrown."); - } catch (IllegalArgumentException exc) { - // expecting an exception with a specific message - // anything else is an error - if (!exc.getMessage().toLowerCase().contains(expectedMessage)) { - throw exc; - } - } } public void runTest(int testNumber) throws Exception { @@ -172,34 +83,49 @@ public void runTest(int testNumber) throws Exception { switch (testNumber) { /* default constructor - default config */ - case 1 -> testRmiCommunication(new ServerFactory(), false); + case 1 -> + testRmiCommunication(new SslRMIServerSocketFactory()); /* non-default constructor - default config */ - case 2 -> testRmiCommunication(new ServerFactory(null, null, false), false); + case 2 -> + testRmiCommunication(new SslRMIServerSocketFactory(null, null, false)); /* needClientAuth=true */ - case 3 -> testRmiCommunication(new ServerFactory(null, null, null, true), false); + case 3 -> + testRmiCommunication(new SslRMIServerSocketFactory(null, null, null, true)); /* server side dummy_ciphersuite */ - case 4 -> - testServerFactory(new String[]{"dummy_ciphersuite"}, null, "unsupported ciphersuite"); + case 4 -> { + Exception exc = Asserts.assertThrows(IllegalArgumentException.class, + () -> testSslServerSocketFactory(new String[]{"dummy_ciphersuite"}, null)); + if (!exc.getMessage().toLowerCase().contains("unsupported ciphersuite")) { + throw exc; + } + } /* server side dummy_protocol */ - case 5 -> - testServerFactory(null, new String[]{"dummy_protocol"}, "unsupported protocol"); + case 5 -> { + Exception thrown = Asserts.assertThrows(IllegalArgumentException.class, + () -> testSslServerSocketFactory(null, new String[]{"dummy_protocol"})); + if (!thrown.getMessage().toLowerCase().contains("unsupported protocol")) { + throw thrown; + } + } /* client side dummy_ciphersuite */ case 6 -> { System.setProperty("javax.rmi.ssl.client.enabledCipherSuites", "dummy_ciphersuite"); - testRmiCommunication(new ServerFactory(), true); + Asserts.assertThrows(ConnectIOException.class, + () -> testRmiCommunication(new SslRMIServerSocketFactory())); } /* client side dummy_protocol */ case 7 -> { System.setProperty("javax.rmi.ssl.client.enabledProtocols", "dummy_protocol"); - testRmiCommunication(new ServerFactory(), true); + Asserts.assertThrows(ConnectIOException.class, + () -> testRmiCommunication(new SslRMIServerSocketFactory())); } default -> diff --git a/test/jdk/javax/swing/JColorChooser/Test6977726.java b/test/jdk/javax/swing/JColorChooser/Test6977726.java index a79931c93eb..1ec72036f14 100644 --- a/test/jdk/javax/swing/JColorChooser/Test6977726.java +++ b/test/jdk/javax/swing/JColorChooser/Test6977726.java @@ -41,7 +41,14 @@ public static void main(String[] args) throws Exception { String instructions = """ Check that there is a panel with "Text Preview Panel" text and with title "Preview" in the JColorChooser. - Test passes if the panel is as described, test fails otherwise."""; + Test passes if the panel is as described, test fails otherwise. + + Note: "Preview" title is not applicable for GTK Look and Feel."""; + + // In case this test is run with GTK L&F, the preview panel title + // is missing due to the "ColorChooser.showPreviewPanelText" property + // which is set to "Boolean.FALSE" for GTK L&F. Test instructions are + // modified to reflect that "Preview" title is not applicable for GTK L&F. PassFailJFrame.builder() .title("Test6977726") diff --git a/test/jdk/javax/swing/JSpinner/4515999/JSpinnerMouseAndKeyPressTest.java b/test/jdk/javax/swing/JSpinner/4515999/JSpinnerMouseAndKeyPressTest.java index 6e5fb3ad97f..49a237a1e27 100644 --- a/test/jdk/javax/swing/JSpinner/4515999/JSpinnerMouseAndKeyPressTest.java +++ b/test/jdk/javax/swing/JSpinner/4515999/JSpinnerMouseAndKeyPressTest.java @@ -83,6 +83,7 @@ private static void createUI() { panel.add(spinner); frame.add(panel); frame.setUndecorated(true); + frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); frame.pack(); frame.setAlwaysOnTop(true); frame.setLocationRelativeTo(null); @@ -103,6 +104,7 @@ public static void runTest() throws Exception { setLookAndFeel(laf); createUI(); }); + robot.waitForIdle(); SwingUtilities.invokeAndWait(() -> { Point loc = spinner.getLocationOnScreen(); diff --git a/test/jdk/javax/swing/border/Test4243289.html b/test/jdk/javax/swing/border/Test4243289.html deleted file mode 100644 index 5616bd835ac..00000000000 --- a/test/jdk/javax/swing/border/Test4243289.html +++ /dev/null @@ -1,32 +0,0 @@ - - - - -When applet starts, you'll see a panel with a TitledBorder with title "Panel Title". -If this title is overstriken with the border line, test fails, otherwise it passes. - - - - - diff --git a/test/jdk/javax/swing/border/Test4243289.java b/test/jdk/javax/swing/border/Test4243289.java index ae535a1e517..4b89239bc19 100644 --- a/test/jdk/javax/swing/border/Test4243289.java +++ b/test/jdk/javax/swing/border/Test4243289.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2024, 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,32 +21,55 @@ * questions. */ +import java.awt.Dimension; +import java.awt.Font; +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.border.TitledBorder; + /* * @test * @bug 4243289 * @summary Tests that TitledBorder do not draw line through its caption - * @author Peter Zhelezniakov - * @run applet/manual=yesno Test4243289.html + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual Test4243289 */ -import java.awt.Font; -import javax.swing.BorderFactory; -import javax.swing.JApplet; -import javax.swing.JPanel; -import javax.swing.border.TitledBorder; +public class Test4243289 { + public static void main(String[] args) throws Exception { + String testInstructions = """ + If TitledBorder with title "Panel Title" is overstruck with + the border line, test fails, otherwise it passes. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(testInstructions) + .rows(3) + .columns(35) + .splitUI(Test4243289::init) + .build() + .awaitAndCheck(); + } -public class Test4243289 extends JApplet { - public void init() { - Font font = new Font("Dialog", Font.PLAIN, 12); // NON-NLS: the font name + public static JComponent init() { + Font font = new Font(Font.DIALOG, Font.PLAIN, 12); TitledBorder border = BorderFactory.createTitledBorder( BorderFactory.createEtchedBorder(), - "Panel Title", // NON-NLS: the title of the border + "Panel Title", TitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION, font); JPanel panel = new JPanel(); panel.setBorder(border); - getContentPane().add(panel); + panel.setPreferredSize(new Dimension(100, 100)); + Box main = Box.createVerticalBox(); + main.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); + main.add(panel); + return main; } } diff --git a/test/jdk/javax/swing/text/GlyphView/4984669/bug4984669.html b/test/jdk/javax/swing/text/GlyphView/4984669/bug4984669.html deleted file mode 100644 index f9991a231c1..00000000000 --- a/test/jdk/javax/swing/text/GlyphView/4984669/bug4984669.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - -The four lines printed above in a bold typeface should all be underlined. -It is a bug if any of these lines is underlined only partially. -The very first line should not be underlined at all. - - diff --git a/test/jdk/javax/swing/text/GlyphView/4984669/bug4984669.java b/test/jdk/javax/swing/text/GlyphView/htmlUnderliningTest.java similarity index 54% rename from test/jdk/javax/swing/text/GlyphView/4984669/bug4984669.java rename to test/jdk/javax/swing/text/GlyphView/htmlUnderliningTest.java index ba590f9472a..b12a129dfa2 100644 --- a/test/jdk/javax/swing/text/GlyphView/4984669/bug4984669.java +++ b/test/jdk/javax/swing/text/GlyphView/htmlUnderliningTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2024, 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,24 +21,48 @@ * questions. */ +import javax.swing.JEditorPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.text.MutableAttributeSet; +import javax.swing.text.SimpleAttributeSet; +import javax.swing.text.StyleConstants; +import javax.swing.text.StyledEditorKit; + /* @test - @bug 4984669 8002148 - @summary Tests HTML underlining - @author Peter Zhelezniakov - @run applet/manual=yesno bug4984669.html -*/ -import javax.swing.*; -import javax.swing.text.*; + * @bug 4984669 8002148 + * @summary Tests HTML underlining + * @library /java/awt/regtesthelpers + * @build PassFailJFrame + * @run main/manual htmlUnderliningTest + */ + +public class htmlUnderliningTest { + public static void main(String[] args) throws Exception { + String testInstructions = """ + The four lines printed in a bold typeface should all be underlined. + It is a bug if any of these lines is underlined only partially. + The very first line should not be underlined at all. + """; + + PassFailJFrame.builder() + .title("Test Instructions") + .instructions(testInstructions) + .rows(4) + .columns(35) + .splitUI(htmlUnderliningTest::initializeTest) + .build() + .awaitAndCheck(); + } -public class bug4984669 extends JApplet -{ - public void init() { + public static JPanel initializeTest() { + JPanel panel = new JPanel(); JEditorPane pane = new JEditorPane(); - this.getContentPane().add(new JScrollPane(pane)); + panel.add(new JScrollPane(pane)); pane.setEditorKit(new StyledEditorKit()); try { - pane.getDocument().insertString(0,"12 \n",null); + pane.getDocument().insertString(0, "12 \n", null); MutableAttributeSet attrs = new SimpleAttributeSet(); StyleConstants.setFontSize(attrs, 36); @@ -51,5 +75,6 @@ public void init() { } catch (Exception e) { throw new Error("Failed: Unexpected Exception", e); } + return panel; } } diff --git a/test/jdk/sun/security/ssl/SSLSocketImpl/NonAutoClose.java b/test/jdk/sun/security/ssl/SSLSocketImpl/NonAutoClose.java index 2209bd3a648..82f3b7d1537 100644 --- a/test/jdk/sun/security/ssl/SSLSocketImpl/NonAutoClose.java +++ b/test/jdk/sun/security/ssl/SSLSocketImpl/NonAutoClose.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2025, 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 @@ -29,229 +29,144 @@ /* * @test * @bug 4404399 - * @ignore this test does not work any more as the TLS spec changes the - * behaviors of close_notify. + * @comment this test does not work in TLSv1.3 as the spec changes the + * behaviors of close_notify. * @summary When a layered SSL socket is closed, it should wait for close_notify - * @run main/othervm NonAutoClose + * @library /test/lib /javax/net/ssl/templates + * @run main/othervm NonAutoClose TLSv1 + * @run main/othervm NonAutoClose TLSv1.1 + * @run main/othervm NonAutoClose TLSv1.2 * @author Brad Wetmore */ import java.io.*; +import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; +import java.util.concurrent.CountDownLatch; import javax.net.ssl.*; -import java.security.cert.X509Certificate; -import java.security.cert.CertificateException; +import jdk.test.lib.security.SecurityUtils; -public class NonAutoClose { - /* - * ============================================================= - * Set the various variables needed for the tests, then - * specify what tests to run on each side. - */ +import static jdk.test.lib.Asserts.assertEquals; - /* - * 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. - */ - private static boolean separateServerThread = true; - - /* - * Where do we find the keystores? - */ - private final static String pathToStores = "../../../../javax/net/ssl/etc"; - private final static String keyStoreFile = "keystore"; - private final static String trustStoreFile = "truststore"; - private final static String passwd = "passphrase"; - private final static char[] cpasswd = "passphrase".toCharArray(); +public class NonAutoClose extends SSLContextTemplate { /* * Is the server ready to serve? */ - volatile static boolean serverReady = false; + private static final CountDownLatch SERVER_READY = new CountDownLatch(1); /* * Turn on SSL debugging? */ - private final static boolean DEBUG = false; - private final static boolean VERBOSE = true; - private final static int NUM_ITERATIONS = 10; + private final static boolean DEBUG = Boolean.getBoolean("test.debug"); + private final static int NUM_ITERATIONS = 10; private final static int PLAIN_SERVER_VAL = 1; private final static int PLAIN_CLIENT_VAL = 2; private final static int TLS_SERVER_VAL = 3; private final static int TLS_CLIENT_VAL = 4; - /* - * If the client or server is doing some kind of object creation - * that the other side depends on, and that thread prematurely - * exits, you may experience a hang. The test harness will - * terminate all hung threads after its timeout has expired, - * currently 3 minutes by default, but you might try to be - * smart about it.... - */ - - void expectValue(int got, int expected, String msg) throws IOException { - if (VERBOSE) { - System.err.println(msg + ": read (" + got + ")"); - } - if (got != expected) { - throw new IOException(msg + ": read (" + got - + ") but expecting(" + expected + ")"); - } - } - - /* * Define the server side of the test. - * - * If the server prematurely exits, serverReady will be set to true - * to avoid infinite hangs. */ + private void doServerSide() throws Exception { + System.out.println("Starting server"); + SSLSocketFactory sslsf = createServerSSLContext().getSocketFactory(); - void doServerSide() throws Exception { - if (VERBOSE) { - System.err.println("Starting server"); - } + try (ServerSocket serverSocket = new ServerSocket(SERVER_PORT)) { + SERVER_PORT = serverSocket.getLocalPort(); - /* - * Setup the SSL stuff - */ - SSLSocketFactory sslsf = - (SSLSocketFactory) SSLSocketFactory.getDefault(); + /* + * Signal Client, we're ready for his connect. + */ + System.out.println("Signal server ready"); + SERVER_READY.countDown(); - ServerSocket serverSocket = new ServerSocket(SERVER_PORT); + try (Socket plainSocket = serverSocket.accept(); + InputStream is = plainSocket.getInputStream(); + OutputStream os = plainSocket.getOutputStream()) { - SERVER_PORT = serverSocket.getLocalPort(); + assertEquals(PLAIN_CLIENT_VAL, is.read()); - /* - * Signal Client, we're ready for his connect. - */ - serverReady = true; + os.write(PLAIN_SERVER_VAL); + os.flush(); - Socket plainSocket = serverSocket.accept(); - InputStream is = plainSocket.getInputStream(); - OutputStream os = plainSocket.getOutputStream(); - - expectValue(is.read(), PLAIN_CLIENT_VAL, "Server"); - - os.write(PLAIN_SERVER_VAL); - os.flush(); - - for (int i = 1; i <= NUM_ITERATIONS; i++) { - if (VERBOSE) { - System.err.println("================================="); - System.err.println("Server Iteration #" + i); - } + for (int i = 1; i <= NUM_ITERATIONS; i++) { + if (DEBUG) { + System.out.println("================================="); + System.out.println("Server Iteration #" + i); + } - SSLSocket ssls = (SSLSocket) sslsf.createSocket(plainSocket, - SERVER_NAME, plainSocket.getPort(), false); + try (SSLSocket ssls = (SSLSocket) sslsf.createSocket(plainSocket, + plainSocket.getInetAddress().getHostName(), + plainSocket.getPort(), false)) { - ssls.setUseClientMode(false); - InputStream sslis = ssls.getInputStream(); - OutputStream sslos = ssls.getOutputStream(); + ssls.setEnabledProtocols(new String[]{protocol}); + ssls.setUseClientMode(false); + try (InputStream sslis = ssls.getInputStream(); + OutputStream sslos = ssls.getOutputStream()) { - expectValue(sslis.read(), TLS_CLIENT_VAL, "Server"); + assertEquals(TLS_CLIENT_VAL, sslis.read()); - sslos.write(TLS_SERVER_VAL); - sslos.flush(); + sslos.write(TLS_SERVER_VAL); + sslos.flush(); + } + } + } - sslis.close(); - sslos.close(); - ssls.close(); + assertEquals(PLAIN_CLIENT_VAL, is.read()); - if (VERBOSE) { - System.err.println("TLS socket is closed"); + os.write(PLAIN_SERVER_VAL); + os.flush(); } } - - expectValue(is.read(), PLAIN_CLIENT_VAL, "Server"); - - os.write(PLAIN_SERVER_VAL); - os.flush(); - - is.close(); - os.close(); - plainSocket.close(); - - if (VERBOSE) { - System.err.println("Server plain socket is closed"); - } } /* * Define the client side of the test. - * - * If the server prematurely exits, serverReady will be set to true - * to avoid infinite hangs. */ private void doClientSide() throws Exception { /* * Wait for server to get started. */ - while (!serverReady) { - Thread.sleep(50); - } - - if (VERBOSE) { - System.err.println("Starting client"); - } - - /* - * Setup the SSL stuff - */ - SSLSocketFactory sslsf = - (SSLSocketFactory) SSLSocketFactory.getDefault(); - - Socket plainSocket = new Socket(SERVER_NAME, SERVER_PORT); - InputStream is = plainSocket.getInputStream(); - OutputStream os = plainSocket.getOutputStream(); - - os.write(PLAIN_CLIENT_VAL); - os.flush(); + System.out.println("Waiting for server ready"); + SERVER_READY.await(); - expectValue(is.read(), PLAIN_SERVER_VAL, "Client"); + SSLSocketFactory sslsf = createClientSSLContext().getSocketFactory(); - for (int i = 1; i <= NUM_ITERATIONS; i++) { - if (VERBOSE) { - System.err.println("==================================="); - System.err.println("Client Iteration #" + i); - } + try (Socket plainSocket = new Socket(InetAddress.getLocalHost(), SERVER_PORT); + InputStream is = plainSocket.getInputStream(); + OutputStream os = plainSocket.getOutputStream()) { - SSLSocket ssls = (SSLSocket) sslsf.createSocket(plainSocket, - SERVER_NAME, plainSocket.getPort(), false); + os.write(PLAIN_CLIENT_VAL); + os.flush(); - ssls.setUseClientMode(true); + assertEquals(PLAIN_SERVER_VAL, is.read()); - InputStream sslis = ssls.getInputStream(); - OutputStream sslos = ssls.getOutputStream(); - - sslos.write(TLS_CLIENT_VAL); - sslos.flush(); + for (int i = 1; i <= NUM_ITERATIONS; i++) { + if (DEBUG) { + System.out.println("==================================="); + System.out.println("Client Iteration #" + i); + } + try (SSLSocket ssls = (SSLSocket) sslsf.createSocket(plainSocket, + plainSocket.getInetAddress().getHostName(), + plainSocket.getPort(), false); + InputStream sslis = ssls.getInputStream(); + OutputStream sslos = ssls.getOutputStream()) { - expectValue(sslis.read(), TLS_SERVER_VAL, "Client"); + ssls.setUseClientMode(true); - sslis.close(); - sslos.close(); - ssls.close(); + sslos.write(TLS_CLIENT_VAL); + sslos.flush(); - if (VERBOSE) { - System.err.println("Client TLS socket is closed"); + assertEquals(TLS_SERVER_VAL, sslis.read()); + } } - } - os.write(PLAIN_CLIENT_VAL); - os.flush(); - - expectValue(is.read(), PLAIN_SERVER_VAL, "Client"); - - is.close(); - os.close(); - plainSocket.close(); - - if (VERBOSE) { - System.err.println("Client plain socket is closed"); + os.write(PLAIN_CLIENT_VAL); + os.flush(); + assertEquals(PLAIN_SERVER_VAL, is.read()); } } @@ -261,25 +176,13 @@ private void doClientSide() throws Exception { */ private volatile int SERVER_PORT = 0; - private final static String SERVER_NAME = "localhost"; - - private volatile Exception serverException = null; private volatile Exception clientException = null; - private final static String keyFilename = - System.getProperty("test.src", ".") + "/" + pathToStores + - "/" + keyStoreFile; - private final static String trustFilename = - System.getProperty("test.src", ".") + "/" + pathToStores + - "/" + trustStoreFile; - - - // Used for running test standalone public static void main(String[] args) throws Exception { - System.setProperty("javax.net.ssl.keyStore", keyFilename); - System.setProperty("javax.net.ssl.keyStorePassword", passwd); - System.setProperty("javax.net.ssl.trustStore", trustFilename); - System.setProperty("javax.net.ssl.trustStorePassword", passwd); + String protocol = args[0]; + if ("TLSv1".equals(protocol) || "TLSv1.1".equals(protocol)) { + SecurityUtils.removeFromDisabledTlsAlgs(protocol); + } if (DEBUG) System.setProperty("javax.net.debug", "all"); @@ -287,94 +190,45 @@ public static void main(String[] args) throws Exception { /* * Start the tests. */ - new NonAutoClose(); + new NonAutoClose(protocol); } private Thread clientThread = null; - private Thread serverThread = null; + private final String protocol; /* * Primary constructor, used to drive remainder of the test. * * Fork off the other side, then do your work. */ - NonAutoClose() throws Exception { - if (separateServerThread) { - startServer(true); - startClient(false); - } else { - startClient(true); - startServer(false); - } + NonAutoClose(String protocol) throws Exception { + this.protocol = protocol; + startClient(); + doServerSide(); /* * Wait for other side to close down. */ - if (separateServerThread) { - serverThread.join(); - } else { - clientThread.join(); - } + clientThread.join(); - /* - * When we get here, the test is pretty much over. - * - * If the main thread excepted, that propagates back - * immediately. If the other thread threw an exception, we - * should report back. - */ - if (serverException != null) { - System.err.print("Server Exception:"); - throw serverException; - } if (clientException != null) { System.err.print("Client Exception:"); throw clientException; } } - private 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..."); - serverReady = true; - serverException = e; - } - } - }; - serverThread.start(); - } else { - doServerSide(); - } - } - - private 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..."); - clientException = e; - } - } - }; - clientThread.start(); - } else { - doClientSide(); - } + private void startClient() { + clientThread = new Thread(() -> { + try { + doClientSide(); + } catch (Exception e) { + /* + * Our client thread just died. + */ + System.err.println("Client died..."); + clientException = e; + } + }); + clientThread.start(); } } diff --git a/test/jdk/sun/security/ssl/SSLSocketImpl/SetClientMode.java b/test/jdk/sun/security/ssl/SSLSocketImpl/SetClientMode.java index 8eed2344bbc..4b55ac6ea90 100644 --- a/test/jdk/sun/security/ssl/SSLSocketImpl/SetClientMode.java +++ b/test/jdk/sun/security/ssl/SSLSocketImpl/SetClientMode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2025, 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,11 +27,13 @@ /* * @test * @bug 6223624 - * @ignore this test does not grant to work. The handshake may have completed - * when getSession() return. Please update or remove this test case. + * @library /test/lib /javax/net/ssl/templates * @summary SSLSocket.setUseClientMode() fails to throw expected * IllegalArgumentException - * @run main/othervm SetClientMode + * @run main/othervm SetClientMode TLSv1 + * @run main/othervm SetClientMode TLSv1.1 + * @run main/othervm SetClientMode TLSv1.2 + * @run main/othervm SetClientMode TLSv1.3 */ /* @@ -47,143 +49,82 @@ * occasionally on the very first iteration. */ -import java.io.*; import java.lang.*; import java.net.*; +import java.util.concurrent.CountDownLatch; import javax.net.ssl.*; -import java.security.*; -import java.security.cert.*; +import jdk.test.lib.security.SecurityUtils; -public class SetClientMode { - private static String[] algorithms = {"TLS", "SSL", "SSLv3", "TLS"}; - volatile int serverPort = 0; +import static jdk.test.lib.Asserts.assertThrows; - /* - * Where do we find the keystores? - */ - static String pathToStores = "../../../../javax/net/ssl/etc"; - static String keyStoreFile = "keystore"; - static String trustStoreFile = "truststore"; - static String passwd = "passphrase"; - - - public SetClientMode() { - // trivial constructor - } +public class SetClientMode extends SSLContextTemplate { + private volatile int serverPort = 0; + private static final CountDownLatch HANDSHAKE_COMPLETE = new CountDownLatch(1); public static void main(String[] args) throws Exception { - String keyFilename = - System.getProperty("test.src", "./") + "/" + pathToStores + - "/" + keyStoreFile; - String trustFilename = - System.getProperty("test.src", "./") + "/" + pathToStores + - "/" + trustStoreFile; - - System.setProperty("javax.net.ssl.keyStore", keyFilename); - System.setProperty("javax.net.ssl.keyStorePassword", passwd); - System.setProperty("javax.net.ssl.trustStore", trustFilename); - System.setProperty("javax.net.ssl.trustStorePassword", passwd); - - new SetClientMode().run(); - } + String protocol = args[0]; - public void run() throws Exception { - for (int i = 0; i < algorithms.length; i++) { - testCombo( algorithms[i] ); + if ("TLSv1".equals(protocol) || "TLSv1.1".equals(protocol)) { + SecurityUtils.removeFromDisabledTlsAlgs(protocol); } - } - public void testCombo(String algorithm) throws Exception { - Exception modeException = null ; + new SetClientMode().run(protocol); + } + public void run(String protocol) throws Exception { // Create a server socket - SSLServerSocketFactory ssf = - (SSLServerSocketFactory)SSLServerSocketFactory.getDefault(); - SSLServerSocket serverSocket = - (SSLServerSocket)ssf.createServerSocket(serverPort); - serverPort = serverSocket.getLocalPort(); - - // Create a client socket - SSLSocketFactory sf = (SSLSocketFactory)SSLSocketFactory.getDefault(); - SSLSocket clientSocket = (SSLSocket)sf.createSocket( - InetAddress.getLocalHost(), - serverPort ); - - // Create a client which will use the SSLSocket to talk to the server - SocketClient client = new SocketClient(clientSocket); - - // Start the client and then accept any connection - client.start(); - - SSLSocket connectedSocket = (SSLSocket)serverSocket.accept(); - - // force handshaking to complete - connectedSocket.getSession(); - - try { - // Now try invoking setClientMode() on one - // or the other of our two sockets. We expect - // to see an IllegalArgumentException because - // handshaking has begun. - clientSocket.setUseClientMode(false); - - modeException = new Exception("no IllegalArgumentException"); - } catch (IllegalArgumentException iae) { - System.out.println("succeeded, we can't set the client mode"); - } catch (Exception e) { - modeException = e; - } finally { - // Shut down. - connectedSocket.close(); - serverSocket.close(); - - if (modeException != null) { - throw modeException; + SSLServerSocketFactory ssf = createServerSSLContext().getServerSocketFactory(); + + try (SSLServerSocket serverSocket = + (SSLServerSocket) ssf.createServerSocket(serverPort)) { + serverSocket.setEnabledProtocols(new String[]{ protocol }); + serverPort = serverSocket.getLocalPort(); + + // Create a client socket + SSLSocketFactory sf = createClientSSLContext().getSocketFactory(); + + try (SSLSocket clientSocket = (SSLSocket) sf.createSocket( + InetAddress.getLocalHost(), + serverPort)) { + + // Create a client which will use the SSLSocket to talk to the server + Client client = new Client(clientSocket); + + // Start the client and then accept any connection + client.start(); + + SSLSocket connectedSocket = (SSLSocket) serverSocket.accept(); + + // force handshaking to complete + connectedSocket.getSession(); + + HANDSHAKE_COMPLETE.await(); + + // Now try invoking setClientMode() on the client socket. + // We expect to see an IllegalArgumentException because + // handshaking has begun. + assertThrows(IllegalArgumentException.class, + () -> clientSocket.setUseClientMode(false)); } } - - return; } // A thread-based client which does nothing except // start handshaking on the socket it's given. - class SocketClient extends Thread { - SSLSocket clientsideSocket; - Exception clientException = null; - boolean done = false; + static class Client extends Thread { + private final SSLSocket socket; - public SocketClient( SSLSocket s ) { - clientsideSocket = s; + public Client(SSLSocket s) { + socket = s; } public void run() { try { - clientsideSocket.startHandshake(); - - // If we were to invoke setUseClientMode() - // here, the expected exception will happen. - //clientsideSocket.getSession(); - //clientsideSocket.setUseClientMode( false ); - } catch ( Exception e ) { + socket.startHandshake(); + HANDSHAKE_COMPLETE.countDown(); + } catch (Exception e) { e.printStackTrace(); - clientException = e; - } finally { - done = true; - try { - clientsideSocket.close(); - } catch ( IOException e ) { - // eat it - } } - return; - } - - boolean isDone() { - return done; - } - - Exception getException() { - return clientException; } } } diff --git a/test/langtools/tools/javac/lambda/LambdaExpr02.java b/test/langtools/tools/javac/lambda/LambdaExpr02.java index d2fb358dbe3..d3526b54373 100644 --- a/test/langtools/tools/javac/lambda/LambdaExpr02.java +++ b/test/langtools/tools/javac/lambda/LambdaExpr02.java @@ -28,7 +28,7 @@ * basic test for simple lambda expressions in multiple scopes * @author Brian Goetz * @author Maurizio Cimadamore - * @run main LambdaExpr01 + * @run main LambdaExpr02 */ public class LambdaExpr02 { diff --git a/test/lib/jdk/test/lib/Asserts.java b/test/lib/jdk/test/lib/Asserts.java index 1fd76007506..0b9e0be3f06 100644 --- a/test/lib/jdk/test/lib/Asserts.java +++ b/test/lib/jdk/test/lib/Asserts.java @@ -553,6 +553,46 @@ public static void assertStringsEqual(String str1, String str2, } } + /** + * A functional interface for executing tests in assertThrownException + */ + @FunctionalInterface + public interface TestMethod { + void execute() throws Throwable; + } + + + public static T assertThrows(Class expected, TestMethod testMethod) { + return assertThrows(expected, testMethod, "An unexpected exception was thrown."); + } + + /** + * Asserts that the given exception (or a subclass of it) is thrown when + * executing the test method. + * + * If the test method throws the correct exception, the exception is returned + * to the caller for additional validation e.g., comparing the exception + * message. + * + * @param expected The expected exception + * @param testMethod The code to execute that should throw the exception + * @param msg A description of the assumption + * @return The thrown exception. + */ + public static T assertThrows(Class expected, TestMethod testMethod, String msg) { + try { + testMethod.execute(); + } catch (Throwable exc) { + if (expected.isInstance(exc)) { + return (T) exc; + } else { + fail(Objects.toString(msg, "An unexpected exception was thrown.") + + " Expected " + expected.getName(), exc); + } + } + throw new RuntimeException("No exception was thrown. Expected: " + expected.getName()); + } + /** * Returns a string formatted with a message and expected and actual values. * @param lhs the actual value