From f11e82d394307174758d9474c2d47df2fb40c63a Mon Sep 17 00:00:00 2001 From: Aayush Tiwari Date: Thu, 2 Jul 2026 19:54:29 +0530 Subject: [PATCH] Use try-with-resources in AbstractTransporter Refactor stream handling in utilGet and utilPut while preserving the existing close and flush contracts. Add tests for the close=false and close=true paths. Closes apache/maven-resolver#1521 Generated-by: Codex --- .../transport/AbstractTransporter.java | 34 +++-- .../transport/AbstractTransporterTest.java | 143 ++++++++++++++++++ 2 files changed, 164 insertions(+), 13 deletions(-) create mode 100644 maven-resolver-spi/src/test/java/org/eclipse/aether/spi/connector/transport/AbstractTransporterTest.java diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/transport/AbstractTransporter.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/transport/AbstractTransporter.java index 7c4f680d6f..a532cd4d00 100644 --- a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/transport/AbstractTransporter.java +++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/transport/AbstractTransporter.java @@ -90,12 +90,16 @@ public void get(GetTask task) throws Exception { */ protected void utilGet(GetTask task, InputStream is, boolean close, long length, boolean resume) throws IOException, TransferCancelledException { - try (OutputStream os = task.newOutputStream(resume)) { - task.getListener().transportStarted(resume ? task.getResumeOffset() : 0L, length); - copy(os, is, task.getListener()); - } finally { - if (close) { - is.close(); + if (close) { + try (InputStream input = is; + OutputStream os = task.newOutputStream(resume)) { + task.getListener().transportStarted(resume ? task.getResumeOffset() : 0L, length); + copy(os, input, task.getListener()); + } + } else { + try (OutputStream os = task.newOutputStream(resume)) { + task.getListener().transportStarted(resume ? task.getResumeOffset() : 0L, length); + copy(os, is, task.getListener()); } } } @@ -129,13 +133,17 @@ public void put(PutTask task) throws Exception { */ protected void utilPut(PutTask task, OutputStream os, boolean close) throws IOException, TransferCancelledException { - try (InputStream is = task.newInputStream()) { - task.getListener().transportStarted(0, task.getDataLength()); - copy(os, is, task.getListener()); - } finally { - if (close) { - os.close(); - } else { + if (close) { + try (OutputStream output = os; + InputStream is = task.newInputStream()) { + task.getListener().transportStarted(0, task.getDataLength()); + copy(output, is, task.getListener()); + } + } else { + try (InputStream is = task.newInputStream()) { + task.getListener().transportStarted(0, task.getDataLength()); + copy(os, is, task.getListener()); + } finally { os.flush(); } } diff --git a/maven-resolver-spi/src/test/java/org/eclipse/aether/spi/connector/transport/AbstractTransporterTest.java b/maven-resolver-spi/src/test/java/org/eclipse/aether/spi/connector/transport/AbstractTransporterTest.java new file mode 100644 index 0000000000..9eaedc35d6 --- /dev/null +++ b/maven-resolver-spi/src/test/java/org/eclipse/aether/spi/connector/transport/AbstractTransporterTest.java @@ -0,0 +1,143 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.eclipse.aether.spi.connector.transport; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.net.URI; +import java.nio.charset.StandardCharsets; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class AbstractTransporterTest { + + private static final byte[] DATA = "data".getBytes(StandardCharsets.UTF_8); + private static final TestTransporter TRANSPORTER = new TestTransporter(); + + @Test + void testUtilGetClosesInputStreamWhenRequested() throws Exception { + CloseAwareInputStream input = new CloseAwareInputStream(DATA); + GetTask task = new GetTask(URI.create("file.txt")); + + TRANSPORTER.utilGet(task, input, true, DATA.length, false); + + assertArrayEquals(DATA, task.getDataBytes()); + assertTrue(input.isClosed()); + } + + @Test + void testUtilGetLeavesInputStreamOpenWhenRequested() throws Exception { + CloseAwareInputStream input = new CloseAwareInputStream(DATA); + GetTask task = new GetTask(URI.create("file.txt")); + + TRANSPORTER.utilGet(task, input, false, DATA.length, false); + + assertArrayEquals(DATA, task.getDataBytes()); + assertFalse(input.isClosed()); + } + + @Test + void testUtilPutClosesOutputStreamWhenRequested() throws Exception { + CloseAwareOutputStream output = new CloseAwareOutputStream(); + PutTask task = new PutTask(URI.create("file.txt")).setDataBytes(DATA); + + TRANSPORTER.utilPut(task, output, true); + + assertArrayEquals(DATA, output.toByteArray()); + assertTrue(output.isClosed()); + assertFalse(output.isFlushed()); + } + + @Test + void testUtilPutFlushesOutputStreamWhenLeftOpen() throws Exception { + CloseAwareOutputStream output = new CloseAwareOutputStream(); + PutTask task = new PutTask(URI.create("file.txt")).setDataBytes(DATA); + + TRANSPORTER.utilPut(task, output, false); + + assertArrayEquals(DATA, output.toByteArray()); + assertFalse(output.isClosed()); + assertTrue(output.isFlushed()); + } + + private static final class TestTransporter extends AbstractTransporter { + @Override + public int classify(Throwable error) { + return ERROR_OTHER; + } + + @Override + protected void implPeek(PeekTask task) {} + + @Override + protected void implGet(GetTask task) {} + + @Override + protected void implPut(PutTask task) {} + + @Override + protected void implClose() {} + } + + private static final class CloseAwareInputStream extends ByteArrayInputStream { + private boolean closed; + + CloseAwareInputStream(byte[] buf) { + super(buf); + } + + @Override + public void close() throws IOException { + closed = true; + super.close(); + } + + boolean isClosed() { + return closed; + } + } + + private static final class CloseAwareOutputStream extends ByteArrayOutputStream { + private boolean closed; + private boolean flushed; + + @Override + public void close() throws IOException { + closed = true; + super.close(); + } + + @Override + public void flush() throws IOException { + flushed = true; + super.flush(); + } + + boolean isClosed() { + return closed; + } + + boolean isFlushed() { + return flushed; + } + } +}