From 6f97ab01547a5eedb5436973fbea44485529672b Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Thu, 12 Dec 2024 23:27:36 +0100 Subject: [PATCH 1/2] [MNG-8430] Resolver 2.0.5 Prepare for Resolver 2.0.5 --- https://issues.apache.org/jira/browse/MNG-8430 --- .../java/org/apache/maven/api/Constants.java | 2 +- .../maven/internal/aether/MavenInstaller.java | 58 ------------------- ...venDeployer.java => MavenTransformer.java} | 27 ++++----- .../standalone/RepositorySystemSupplier.java | 4 ++ pom.xml | 2 +- src/site/markdown/configuration.properties | 4 +- src/site/markdown/configuration.yaml | 4 +- src/site/markdown/maven-configuration.md | 2 +- 8 files changed, 23 insertions(+), 80 deletions(-) delete mode 100644 impl/maven-core/src/main/java/org/apache/maven/internal/aether/MavenInstaller.java rename impl/maven-core/src/main/java/org/apache/maven/internal/aether/{MavenDeployer.java => MavenTransformer.java} (64%) diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Constants.java b/api/maven-api-core/src/main/java/org/apache/maven/api/Constants.java index a80fbd68f626..dc2ab449abd6 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/Constants.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Constants.java @@ -440,7 +440,7 @@ public final class Constants { /** * User property used to store the build timestamp. * - * @since 4.1.0 + * @since 4.0.0 */ @Config(type = "java.time.Instant") public static final String MAVEN_START_INSTANT = "maven.startInstant"; diff --git a/impl/maven-core/src/main/java/org/apache/maven/internal/aether/MavenInstaller.java b/impl/maven-core/src/main/java/org/apache/maven/internal/aether/MavenInstaller.java deleted file mode 100644 index d7f2f95f1402..000000000000 --- a/impl/maven-core/src/main/java/org/apache/maven/internal/aether/MavenInstaller.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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.apache.maven.internal.aether; - -import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; - -import org.apache.maven.internal.transformation.ConsumerPomArtifactTransformer; -import org.eclipse.aether.RepositorySystemSession; -import org.eclipse.aether.impl.Installer; -import org.eclipse.aether.installation.InstallRequest; -import org.eclipse.aether.installation.InstallResult; -import org.eclipse.aether.installation.InstallationException; -import org.eclipse.aether.internal.impl.DefaultInstaller; -import org.eclipse.sisu.Priority; - -import static java.util.Objects.requireNonNull; - -/** - * Maven specific installer. - */ -@Singleton -@Named -@Priority(100) -final class MavenInstaller implements Installer { - - private final DefaultInstaller installer; - - private final ConsumerPomArtifactTransformer consumerPomArtifactTransformer; - - @Inject - MavenInstaller(DefaultInstaller installer, ConsumerPomArtifactTransformer consumerPomArtifactTransformer) { - this.installer = requireNonNull(installer); - this.consumerPomArtifactTransformer = requireNonNull(consumerPomArtifactTransformer); - } - - @Override - public InstallResult install(RepositorySystemSession session, InstallRequest request) throws InstallationException { - return installer.install(session, consumerPomArtifactTransformer.remapInstallArtifacts(session, request)); - } -} diff --git a/impl/maven-core/src/main/java/org/apache/maven/internal/aether/MavenDeployer.java b/impl/maven-core/src/main/java/org/apache/maven/internal/aether/MavenTransformer.java similarity index 64% rename from impl/maven-core/src/main/java/org/apache/maven/internal/aether/MavenDeployer.java rename to impl/maven-core/src/main/java/org/apache/maven/internal/aether/MavenTransformer.java index 93aa84418ee8..01e6ebbdffe0 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/internal/aether/MavenDeployer.java +++ b/impl/maven-core/src/main/java/org/apache/maven/internal/aether/MavenTransformer.java @@ -25,34 +25,31 @@ import org.apache.maven.internal.transformation.ConsumerPomArtifactTransformer; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.deployment.DeployRequest; -import org.eclipse.aether.deployment.DeployResult; -import org.eclipse.aether.deployment.DeploymentException; -import org.eclipse.aether.impl.Deployer; -import org.eclipse.aether.internal.impl.DefaultDeployer; -import org.eclipse.sisu.Priority; +import org.eclipse.aether.installation.InstallRequest; +import org.eclipse.aether.spi.artifact.transformer.ArtifactTransformer; import static java.util.Objects.requireNonNull; /** - * Maven specific deployer. + * Maven specific transformer. */ @Singleton @Named -@Priority(100) -final class MavenDeployer implements Deployer { - - private final DefaultDeployer deployer; - +final class MavenTransformer implements ArtifactTransformer { private final ConsumerPomArtifactTransformer consumerPomArtifactTransformer; @Inject - MavenDeployer(DefaultDeployer deployer, ConsumerPomArtifactTransformer consumerPomArtifactTransformer) { - this.deployer = requireNonNull(deployer); + MavenTransformer(ConsumerPomArtifactTransformer consumerPomArtifactTransformer) { this.consumerPomArtifactTransformer = requireNonNull(consumerPomArtifactTransformer); } @Override - public DeployResult deploy(RepositorySystemSession session, DeployRequest request) throws DeploymentException { - return deployer.deploy(session, consumerPomArtifactTransformer.remapDeployArtifacts(session, request)); + public InstallRequest transformInstallArtifacts(RepositorySystemSession session, InstallRequest request) { + return consumerPomArtifactTransformer.remapInstallArtifacts(session, request); + } + + @Override + public DeployRequest transformDeployArtifacts(RepositorySystemSession session, DeployRequest request) { + return consumerPomArtifactTransformer.remapDeployArtifacts(session, request); } } diff --git a/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/standalone/RepositorySystemSupplier.java b/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/standalone/RepositorySystemSupplier.java index 0d1423a7a30b..a341c4a2efef 100644 --- a/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/standalone/RepositorySystemSupplier.java +++ b/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/standalone/RepositorySystemSupplier.java @@ -99,6 +99,8 @@ /** * DI Bridge for Maven Resolver + * + * TODO: reuse mvn4 Supplier here */ @SuppressWarnings("unused") public class RepositorySystemSupplier { @@ -397,6 +399,7 @@ static Installer newInstaller( repositoryEventDispatcher, artifactFactories != null ? artifactFactories : Map.of(), metadataFactories, + Map.of(), syncContextFactory); } @@ -419,6 +422,7 @@ static Deployer newDeployer( updateCheckManager, artifactFactories != null ? artifactFactories : Map.of(), metadataFactories, + Map.of(), syncContextFactory, offlineController); } diff --git a/pom.xml b/pom.xml index 1e628a340f27..52ab93c0f27a 100644 --- a/pom.xml +++ b/pom.xml @@ -162,7 +162,7 @@ under the License. 1.27 1.4.0 4.0.4 - 2.0.4 + 2.0.5-SNAPSHOT 4.0.3 0.9.0.M3 2.0.16 diff --git a/src/site/markdown/configuration.properties b/src/site/markdown/configuration.properties index 44b45571ff2a..f275125be562 100644 --- a/src/site/markdown/configuration.properties +++ b/src/site/markdown/configuration.properties @@ -172,10 +172,10 @@ props.26.description = props.26.defaultValue = ${maven.user.conf}/settings-security4.xml props.26.configurationSource = User properties props.27.key = maven.startInstant -props.27.configurationType = String +props.27.configurationType = java.time.Instant props.27.description = User property used to store the build timestamp. props.27.defaultValue = -props.27.since = 4.1.0 +props.27.since = 4.0.0 props.27.configurationSource = User properties props.28.key = maven.style.color props.28.configurationType = String diff --git a/src/site/markdown/configuration.yaml b/src/site/markdown/configuration.yaml index f225960935a4..2b3d4276618c 100644 --- a/src/site/markdown/configuration.yaml +++ b/src/site/markdown/configuration.yaml @@ -172,10 +172,10 @@ props: defaultValue: ${maven.user.conf}/settings-security4.xml configurationSource: User properties - key: maven.startInstant - configurationType: String + configurationType: java.time.Instant description: "User property used to store the build timestamp." defaultValue: - since: 4.1.0 + since: 4.0.0 configurationSource: User properties - key: maven.style.color configurationType: String diff --git a/src/site/markdown/maven-configuration.md b/src/site/markdown/maven-configuration.md index aa4eada9edda..459c51cec8b7 100644 --- a/src/site/markdown/maven-configuration.md +++ b/src/site/markdown/maven-configuration.md @@ -51,7 +51,7 @@ under the License. | 24. | `maven.resolver.transport` | `String` | Resolver transport to use. Can be default, wagon, apache, jdk or auto. | `default` | 4.0.0 | User properties | | 25. | `maven.session.versionFilter` | `String` | User property for version filter expression used in session, applied to resolving ranges: a semicolon separated list of filters to apply. By default, no version filter is applied (like in Maven 3).
Supported filters: Example filter expression: "h(5);s;e(org.foo:bar:1) will cause: ranges are filtered for "top 5" (instead full range), snapshots are banned if root project is not a snapshot, and if range for org.foo:bar is being processed, version 1 is omitted. Value in this property builds org.eclipse.aether.collection.VersionFilter instance. | - | 4.0.0 | User properties | | 26. | `maven.settings.security` | `String` | | `${maven.user.conf}/settings-security4.xml` | | User properties | -| 27. | `maven.startInstant` | `String` | User property used to store the build timestamp. | - | 4.1.0 | User properties | +| 27. | `maven.startInstant` | `java.time.Instant` | User property used to store the build timestamp. | - | 4.0.0 | User properties | | 28. | `maven.style.color` | `String` | Maven output color mode. Allowed values are auto, always, never. | `auto` | 4.0.0 | User properties | | 29. | `maven.style.debug` | `String` | Color style for debug messages. | `bold,f:cyan` | 4.0.0 | User properties | | 30. | `maven.style.error` | `String` | Color style for error messages. | `bold,f:red` | 4.0.0 | User properties | From b76a150f9c4ef5b78b744182fc540fe7f3ca627c Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Thu, 12 Dec 2024 11:51:34 +0100 Subject: [PATCH 2/2] [MNG-5729] Fix transfer rate computation --- impl/maven-cli/pom.xml | 5 + .../AbstractMavenTransferListener.java | 9 +- .../maven/cling/transfer/FileSizeFormat.java | 16 + .../ConsoleMavenTransferListenerTest.java | 139 +++++++ .../cling/transfer/FileSizeFormatTest.java | 356 ++++++++++++++++++ .../transfer/SimplexTransferListenerTest.java | 112 ++++++ .../maven/internal/impl/AbstractSession.java | 6 + 7 files changed, 637 insertions(+), 6 deletions(-) create mode 100644 impl/maven-cli/src/test/java/org/apache/maven/cling/transfer/ConsoleMavenTransferListenerTest.java create mode 100644 impl/maven-cli/src/test/java/org/apache/maven/cling/transfer/FileSizeFormatTest.java create mode 100644 impl/maven-cli/src/test/java/org/apache/maven/cling/transfer/SimplexTransferListenerTest.java diff --git a/impl/maven-cli/pom.xml b/impl/maven-cli/pom.xml index bd8c0b61def2..97f35179fe3f 100644 --- a/impl/maven-cli/pom.xml +++ b/impl/maven-cli/pom.xml @@ -89,6 +89,11 @@ under the License. junit-jupiter-api test + + org.mockito + mockito-core + test + com.google.jimfs jimfs diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/transfer/AbstractMavenTransferListener.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/transfer/AbstractMavenTransferListener.java index 4cba621f55a6..fa4341b8f1b5 100644 --- a/impl/maven-cli/src/main/java/org/apache/maven/cling/transfer/AbstractMavenTransferListener.java +++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/transfer/AbstractMavenTransferListener.java @@ -20,7 +20,6 @@ import java.io.PrintWriter; import java.time.Duration; -import java.time.Instant; import org.apache.maven.api.MonotonicClock; import org.apache.maven.api.services.MessageBuilder; @@ -83,13 +82,11 @@ public void transferSucceeded(TransferEvent event) { message.resetStyle().append(resource.getResourceName()); message.style(STYLE).append(" (").append(format.format(contentLength)); - Duration duration = - Duration.between(Instant.ofEpochMilli(resource.getTransferStartTime()), MonotonicClock.now()); + Duration duration = Duration.between(resource.getStartTime(), MonotonicClock.now()); if ((duration.getSeconds() | duration.getNano()) > 0) { // duration.isPositive() - long bytesPerSecond = Math.round(contentLength / (double) duration.toSeconds()); + double bytesPerSecond = contentLength / (double) duration.toSeconds(); message.append(" at "); - format.format(message, bytesPerSecond); - message.append("/s"); + format.formatRate(message, bytesPerSecond); } message.append(')').resetStyle(); diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/transfer/FileSizeFormat.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/transfer/FileSizeFormat.java index a46e0b035b71..d29eb01540a2 100644 --- a/impl/maven-cli/src/main/java/org/apache/maven/cling/transfer/FileSizeFormat.java +++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/transfer/FileSizeFormat.java @@ -152,6 +152,22 @@ public void format(MessageBuilder builder, long size, ScaleUnit unit) { format(builder, size, unit, false); } + public void formatRate(MessageBuilder builder, double rate) { + ScaleUnit unit = ScaleUnit.getScaleUnit(Math.round(rate)); + double scaledRate = rate / unit.bytes(); + if (unit == ScaleUnit.BYTE || scaledRate < 0.05d || scaledRate >= 10.0d) { + builder.append(Long.toString(Math.round(scaledRate))); + } else { + builder.append(Double.toString(Math.round(scaledRate * 10d) / 10d)); + } + if (unit == ScaleUnit.BYTE) { + builder.append(" B"); + } else { + builder.append(" ").append(unit.symbol()); + } + builder.append("/s"); + } + private void format(MessageBuilder builder, long size, ScaleUnit unit, boolean omitSymbol) { if (size < 0L) { throw new IllegalArgumentException("file size cannot be negative: " + size); diff --git a/impl/maven-cli/src/test/java/org/apache/maven/cling/transfer/ConsoleMavenTransferListenerTest.java b/impl/maven-cli/src/test/java/org/apache/maven/cling/transfer/ConsoleMavenTransferListenerTest.java new file mode 100644 index 000000000000..d06c4d18e364 --- /dev/null +++ b/impl/maven-cli/src/test/java/org/apache/maven/cling/transfer/ConsoleMavenTransferListenerTest.java @@ -0,0 +1,139 @@ +/* + * 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.apache.maven.cling.transfer; + +import java.io.File; +import java.io.PrintWriter; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import org.apache.maven.jline.JLineMessageBuilderFactory; +import org.eclipse.aether.DefaultRepositorySystemSession; +import org.eclipse.aether.transfer.TransferCancelledException; +import org.eclipse.aether.transfer.TransferEvent; +import org.eclipse.aether.transfer.TransferListener; +import org.eclipse.aether.transfer.TransferResource; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +class ConsoleMavenTransferListenerTest { + + private CountDownLatch startLatch; + private CountDownLatch endLatch; + + @Test + void testTransferProgressedWithPrintResourceNames() throws Exception { + int size = 1000; + ExecutorService service = Executors.newFixedThreadPool(size * 2); + startLatch = new CountDownLatch(size); + endLatch = new CountDownLatch(size); + Map output = new ConcurrentHashMap(); + + TransferListener listener = new SimplexTransferListener(new ConsoleMavenTransferListener( + new JLineMessageBuilderFactory(), + new PrintWriter(System.out) { + + @Override + public void print(Object o) { + + String string = o.toString(); + int i = string.length() - 1; + while (i >= 0) { + char c = string.charAt(i); + if (c == '\n' || c == '\r' || c == ' ') i--; + else break; + } + + string = string.substring(0, i + 1).trim(); + output.put(string, string); + System.out.print(o); + } + }, + true)); + TransferResource resource = + new TransferResource(null, null, "http://maven.org/test/test-resource", new File(""), null); + resource.setContentLength(size - 1); + + DefaultRepositorySystemSession session = new DefaultRepositorySystemSession(h -> false); // no close handle + + // warm up + test(listener, session, resource, 0); + + for (int i = 1; i < size; i++) { + final int bytes = i; + + service.execute(() -> { + test(listener, session, resource, bytes); + }); + } + + // start all threads at once + try { + startLatch.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + // wait for all thread to end + try { + endLatch.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + // despite all are back, we need to make sure all the events are processed (are async) + // this one should block until all processed + listener.transferSucceeded(new TransferEvent.Builder(session, resource) + .setType(TransferEvent.EventType.SUCCEEDED) + .build()); + + StringBuilder message = new StringBuilder("Messages ["); + boolean test = true; + for (int i = 0; i < 999; i++) { + boolean ok = output.containsKey("Progress (1): test-resource (" + i + "/999 B)"); + if (!ok) { + System.out.println("false : " + i); + message.append(i + ","); + } + test = test & ok; + } + assertTrue(test, message + "] are missing in " + output); + } + + private void test( + TransferListener listener, + DefaultRepositorySystemSession session, + TransferResource resource, + final int bytes) { + TransferEvent event = new TransferEvent.Builder(session, resource) + .setType(TransferEvent.EventType.PROGRESSED) + .setTransferredBytes(bytes) + .build(); + startLatch.countDown(); + try { + listener.transferProgressed(event); + } catch (TransferCancelledException e) { + } + endLatch.countDown(); + } +} diff --git a/impl/maven-cli/src/test/java/org/apache/maven/cling/transfer/FileSizeFormatTest.java b/impl/maven-cli/src/test/java/org/apache/maven/cling/transfer/FileSizeFormatTest.java new file mode 100644 index 000000000000..9a969f0359d6 --- /dev/null +++ b/impl/maven-cli/src/test/java/org/apache/maven/cling/transfer/FileSizeFormatTest.java @@ -0,0 +1,356 @@ +/* + * 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.apache.maven.cling.transfer; + +import org.apache.maven.api.services.MessageBuilder; +import org.apache.maven.cling.transfer.FileSizeFormat.ScaleUnit; +import org.apache.maven.internal.impl.DefaultMessageBuilder; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class FileSizeFormatTest { + + @Test + void testNegativeSize() { + FileSizeFormat format = new FileSizeFormat(); + + long negativeSize = -100L; + assertThrows(IllegalArgumentException.class, () -> format.format(negativeSize)); + } + + @Test + void testSize() { + FileSizeFormat format = new FileSizeFormat(); + + long _0_bytes = 0L; + assertEquals("0 B", format.format(_0_bytes)); + + long _5_bytes = 5L; + assertEquals("5 B", format.format(_5_bytes)); + + long _10_bytes = 10L; + assertEquals("10 B", format.format(_10_bytes)); + + long _15_bytes = 15L; + assertEquals("15 B", format.format(_15_bytes)); + + long _999_bytes = 999L; + assertEquals("999 B", format.format(_999_bytes)); + + long _1000_bytes = 1000L; + assertEquals("1.0 kB", format.format(_1000_bytes)); + + long _5500_bytes = 5500L; + assertEquals("5.5 kB", format.format(_5500_bytes)); + + long _10_kilobytes = 10L * 1000L; + assertEquals("10 kB", format.format(_10_kilobytes)); + + long _15_kilobytes = 15L * 1000L; + assertEquals("15 kB", format.format(_15_kilobytes)); + + long _999_kilobytes = 999L * 1000L; + assertEquals("999 kB", format.format(_999_kilobytes)); + + long _1000_kilobytes = 1000L * 1000L; + assertEquals("1.0 MB", format.format(_1000_kilobytes)); + + long _5500_kilobytes = 5500L * 1000L; + assertEquals("5.5 MB", format.format(_5500_kilobytes)); + + long _10_megabytes = 10L * 1000L * 1000L; + assertEquals("10 MB", format.format(_10_megabytes)); + + long _15_megabytes = 15L * 1000L * 1000L; + assertEquals("15 MB", format.format(_15_megabytes)); + + long _999_megabytes = 999L * 1000L * 1000L; + assertEquals("999 MB", format.format(_999_megabytes)); + + long _1000_megabytes = 1000L * 1000L * 1000L; + assertEquals("1.0 GB", format.format(_1000_megabytes)); + + long _5500_megabytes = 5500L * 1000L * 1000L; + assertEquals("5.5 GB", format.format(_5500_megabytes)); + + long _10_gigabytes = 10L * 1000L * 1000L * 1000L; + assertEquals("10 GB", format.format(_10_gigabytes)); + + long _15_gigabytes = 15L * 1000L * 1000L * 1000L; + assertEquals("15 GB", format.format(_15_gigabytes)); + + long _1000_gigabytes = 1000L * 1000L * 1000L * 1000L; + assertEquals("1000 GB", format.format(_1000_gigabytes)); + } + + @Test + void testSizeWithSelectedScaleUnit() { + FileSizeFormat format = new FileSizeFormat(); + + long _0_bytes = 0L; + assertEquals("0 B", format.format(_0_bytes)); + assertEquals("0 B", format.format(_0_bytes, ScaleUnit.BYTE)); + assertEquals("0 kB", format.format(_0_bytes, ScaleUnit.KILOBYTE)); + assertEquals("0 MB", format.format(_0_bytes, ScaleUnit.MEGABYTE)); + assertEquals("0 GB", format.format(_0_bytes, ScaleUnit.GIGABYTE)); + + long _5_bytes = 5L; + assertEquals("5 B", format.format(_5_bytes)); + assertEquals("5 B", format.format(_5_bytes, ScaleUnit.BYTE)); + assertEquals("0 kB", format.format(_5_bytes, ScaleUnit.KILOBYTE)); + assertEquals("0 MB", format.format(_5_bytes, ScaleUnit.MEGABYTE)); + assertEquals("0 GB", format.format(_5_bytes, ScaleUnit.GIGABYTE)); + + long _49_bytes = 49L; + assertEquals("49 B", format.format(_49_bytes)); + assertEquals("49 B", format.format(_49_bytes, ScaleUnit.BYTE)); + assertEquals("0 kB", format.format(_49_bytes, ScaleUnit.KILOBYTE)); + assertEquals("0 MB", format.format(_49_bytes, ScaleUnit.MEGABYTE)); + assertEquals("0 GB", format.format(_49_bytes, ScaleUnit.GIGABYTE)); + + long _50_bytes = 50L; + assertEquals("50 B", format.format(_50_bytes)); + assertEquals("50 B", format.format(_50_bytes, ScaleUnit.BYTE)); + assertEquals("0.1 kB", format.format(_50_bytes, ScaleUnit.KILOBYTE)); + assertEquals("0 MB", format.format(_50_bytes, ScaleUnit.MEGABYTE)); + assertEquals("0 GB", format.format(_50_bytes, ScaleUnit.GIGABYTE)); + + long _999_bytes = 999L; + assertEquals("999 B", format.format(_999_bytes)); + assertEquals("999 B", format.format(_999_bytes, ScaleUnit.BYTE)); + assertEquals("1.0 kB", format.format(_999_bytes, ScaleUnit.KILOBYTE)); + assertEquals("0 MB", format.format(_999_bytes, ScaleUnit.MEGABYTE)); + assertEquals("0 GB", format.format(_999_bytes, ScaleUnit.GIGABYTE)); + + long _1000_bytes = 1000L; + assertEquals("1.0 kB", format.format(_1000_bytes)); + assertEquals("1000 B", format.format(_1000_bytes, ScaleUnit.BYTE)); + assertEquals("1.0 kB", format.format(_1000_bytes, ScaleUnit.KILOBYTE)); + assertEquals("0 MB", format.format(_1000_bytes, ScaleUnit.MEGABYTE)); + assertEquals("0 GB", format.format(_1000_bytes, ScaleUnit.GIGABYTE)); + + long _49_kilobytes = 49L * 1000L; + assertEquals("49 kB", format.format(_49_kilobytes)); + assertEquals("49000 B", format.format(_49_kilobytes, ScaleUnit.BYTE)); + assertEquals("49 kB", format.format(_49_kilobytes, ScaleUnit.KILOBYTE)); + assertEquals("0 MB", format.format(_49_kilobytes, ScaleUnit.MEGABYTE)); + assertEquals("0 GB", format.format(_49_kilobytes, ScaleUnit.GIGABYTE)); + + long _50_kilobytes = 50L * 1000L; + assertEquals("50 kB", format.format(_50_kilobytes)); + assertEquals("50000 B", format.format(_50_kilobytes, ScaleUnit.BYTE)); + assertEquals("50 kB", format.format(_50_kilobytes, ScaleUnit.KILOBYTE)); + assertEquals("0.1 MB", format.format(_50_kilobytes, ScaleUnit.MEGABYTE)); + assertEquals("0 GB", format.format(_50_kilobytes, ScaleUnit.GIGABYTE)); + + long _999_kilobytes = 999L * 1000L; + assertEquals("999 kB", format.format(_999_kilobytes)); + assertEquals("999000 B", format.format(_999_kilobytes, ScaleUnit.BYTE)); + assertEquals("999 kB", format.format(_999_kilobytes, ScaleUnit.KILOBYTE)); + assertEquals("1.0 MB", format.format(_999_kilobytes, ScaleUnit.MEGABYTE)); + assertEquals("0 GB", format.format(_999_kilobytes, ScaleUnit.GIGABYTE)); + + long _1000_kilobytes = 1000L * 1000L; + assertEquals("1.0 MB", format.format(_1000_kilobytes)); + assertEquals("1000000 B", format.format(_1000_kilobytes, ScaleUnit.BYTE)); + assertEquals("1000 kB", format.format(_1000_kilobytes, ScaleUnit.KILOBYTE)); + assertEquals("1.0 MB", format.format(_1000_kilobytes, ScaleUnit.MEGABYTE)); + assertEquals("0 GB", format.format(_1000_kilobytes, ScaleUnit.GIGABYTE)); + + long _49_megabytes = 49L * 1000L * 1000L; + assertEquals("49 MB", format.format(_49_megabytes)); + assertEquals("49000000 B", format.format(_49_megabytes, ScaleUnit.BYTE)); + assertEquals("49000 kB", format.format(_49_megabytes, ScaleUnit.KILOBYTE)); + assertEquals("49 MB", format.format(_49_megabytes, ScaleUnit.MEGABYTE)); + assertEquals("0 GB", format.format(_49_megabytes, ScaleUnit.GIGABYTE)); + + long _50_megabytes = 50L * 1000L * 1000L; + assertEquals("50 MB", format.format(_50_megabytes)); + assertEquals("50000000 B", format.format(_50_megabytes, ScaleUnit.BYTE)); + assertEquals("50000 kB", format.format(_50_megabytes, ScaleUnit.KILOBYTE)); + assertEquals("50 MB", format.format(_50_megabytes, ScaleUnit.MEGABYTE)); + assertEquals("0.1 GB", format.format(_50_megabytes, ScaleUnit.GIGABYTE)); + + long _999_megabytes = 999L * 1000L * 1000L; + assertEquals("999 MB", format.format(_999_megabytes)); + assertEquals("999000000 B", format.format(_999_megabytes, ScaleUnit.BYTE)); + assertEquals("999000 kB", format.format(_999_megabytes, ScaleUnit.KILOBYTE)); + assertEquals("999 MB", format.format(_999_megabytes, ScaleUnit.MEGABYTE)); + assertEquals("1.0 GB", format.format(_999_megabytes, ScaleUnit.GIGABYTE)); + + long _1000_megabytes = 1000L * 1000L * 1000L; + assertEquals("1.0 GB", format.format(_1000_megabytes)); + assertEquals("1000000000 B", format.format(_1000_megabytes, ScaleUnit.BYTE)); + assertEquals("1000000 kB", format.format(_1000_megabytes, ScaleUnit.KILOBYTE)); + assertEquals("1000 MB", format.format(_1000_megabytes, ScaleUnit.MEGABYTE)); + assertEquals("1.0 GB", format.format(_1000_megabytes, ScaleUnit.GIGABYTE)); + } + + @Test + void testNegativeProgressedSize() { + FileSizeFormat format = new FileSizeFormat(); + + long negativeProgressedSize = -100L; + assertThrows(IllegalArgumentException.class, () -> format.formatProgress(negativeProgressedSize, 10L)); + } + + @Test + void testNegativeProgressedSizeBiggerThanSize() { + FileSizeFormat format = new FileSizeFormat(); + + assertThrows(IllegalArgumentException.class, () -> format.formatProgress(100L, 10L)); + } + + @Test + void testProgressedSizeWithoutSize() { + FileSizeFormat format = new FileSizeFormat(); + + long _0_bytes = 0L; + assertEquals("0 B", format.formatProgress(_0_bytes, -1L)); + + long _1000_bytes = 1000L; + assertEquals("1.0 kB", format.formatProgress(_1000_bytes, -1L)); + + long _1000_kilobytes = 1000L * 1000L; + assertEquals("1.0 MB", format.formatProgress(_1000_kilobytes, -1L)); + + long _1000_megabytes = 1000L * 1000L * 1000L; + assertEquals("1.0 GB", format.formatProgress(_1000_megabytes, -1L)); + } + + @Test + void testProgressedBothZero() { + FileSizeFormat format = new FileSizeFormat(); + + long _0_bytes = 0L; + assertEquals("0 B", format.formatProgress(_0_bytes, _0_bytes)); + } + + @Test + void testProgressedSizeWithSize() { + FileSizeFormat format = new FileSizeFormat(); + + long _0_bytes = 0L; + long _400_bytes = 400L; + long _800_bytes = 2L * _400_bytes; + assertEquals("0/800 B", format.formatProgress(_0_bytes, _800_bytes)); + assertEquals("400/800 B", format.formatProgress(_400_bytes, _800_bytes)); + assertEquals("800 B", format.formatProgress(_800_bytes, _800_bytes)); + + long _4000_bytes = 4000L; + long _8000_bytes = 2L * _4000_bytes; + long _50_kilobytes = 50000L; + assertEquals("0/8.0 kB", format.formatProgress(_0_bytes, _8000_bytes)); + assertEquals("0.4/8.0 kB", format.formatProgress(_400_bytes, _8000_bytes)); + assertEquals("4.0/8.0 kB", format.formatProgress(_4000_bytes, _8000_bytes)); + assertEquals("8.0 kB", format.formatProgress(_8000_bytes, _8000_bytes)); + assertEquals("8.0/50 kB", format.formatProgress(_8000_bytes, _50_kilobytes)); + assertEquals("16/50 kB", format.formatProgress(2L * _8000_bytes, _50_kilobytes)); + assertEquals("50 kB", format.formatProgress(_50_kilobytes, _50_kilobytes)); + + long _500_kilobytes = 500000L; + long _1000_kilobytes = 2L * _500_kilobytes; + ; + long _5000_kilobytes = 5L * _1000_kilobytes; + long _15_megabytes = 3L * _5000_kilobytes; + assertEquals("0/5.0 MB", format.formatProgress(_0_bytes, _5000_kilobytes)); + assertEquals("0.5/5.0 MB", format.formatProgress(_500_kilobytes, _5000_kilobytes)); + assertEquals("1.0/5.0 MB", format.formatProgress(_1000_kilobytes, _5000_kilobytes)); + assertEquals("5.0 MB", format.formatProgress(_5000_kilobytes, _5000_kilobytes)); + assertEquals("5.0/15 MB", format.formatProgress(_5000_kilobytes, _15_megabytes)); + assertEquals("15 MB", format.formatProgress(_15_megabytes, _15_megabytes)); + + long _500_megabytes = 500000000L; + long _1000_megabytes = 2L * _500_megabytes; + long _5000_megabytes = 5L * _1000_megabytes; + long _15_gigabytes = 3L * _5000_megabytes; + assertEquals("0/500 MB", format.formatProgress(_0_bytes, _500_megabytes)); + assertEquals("1.0/5.0 GB", format.formatProgress(_1000_megabytes, _5000_megabytes)); + assertEquals("5.0 GB", format.formatProgress(_5000_megabytes, _5000_megabytes)); + assertEquals("5.0/15 GB", format.formatProgress(_5000_megabytes, _15_gigabytes)); + assertEquals("15 GB", format.formatProgress(_15_gigabytes, _15_gigabytes)); + } + + @Test + void testFormatRate() { + FileSizeFormat format = new FileSizeFormat(); + + // Test bytes per second + MessageBuilder builder = new DefaultMessageBuilder(); + format.formatRate(builder, 5.0); + assertEquals("5 B/s", builder.build()); + + // Test kilobytes per second + builder = new DefaultMessageBuilder(); + format.formatRate(builder, 5500.0); + assertEquals("5.5 kB/s", builder.build()); + + // Test megabytes per second + builder = new DefaultMessageBuilder(); + format.formatRate(builder, 5500000.0); + assertEquals("5.5 MB/s", builder.build()); + + // Test gigabytes per second + builder = new DefaultMessageBuilder(); + format.formatRate(builder, 5500000000.0); + assertEquals("5.5 GB/s", builder.build()); + } + + @Test + void testFormatRateThresholds() { + FileSizeFormat format = new FileSizeFormat(); + + // Test value less than 0.05 + MessageBuilder builder = new DefaultMessageBuilder(); + format.formatRate(builder, 45.0); // 45 B/s + assertEquals("45 B/s", builder.build()); + + // Test value greater than or equal to 10 + builder = new DefaultMessageBuilder(); + format.formatRate(builder, 15000.0); // 15 kB/s + assertEquals("15 kB/s", builder.build()); + + // Test value between 0.05 and 10 + builder = new DefaultMessageBuilder(); + format.formatRate(builder, 5500.0); // 5.5 kB/s + assertEquals("5.5 kB/s", builder.build()); + } + + @Test + void testFormatRateEdgeCases() { + FileSizeFormat format = new FileSizeFormat(); + + // Test zero rate + MessageBuilder builder = new DefaultMessageBuilder(); + format.formatRate(builder, 0.0); + assertEquals("0 B/s", builder.build()); + + // Test rate at exactly 1000 (1 kB/s) + builder = new DefaultMessageBuilder(); + format.formatRate(builder, 1000.0); + assertEquals("1.0 kB/s", builder.build()); + + // Test rate at exactly 1000000 (1 MB/s) + builder = new DefaultMessageBuilder(); + format.formatRate(builder, 1000000.0); + assertEquals("1.0 MB/s", builder.build()); + } +} diff --git a/impl/maven-cli/src/test/java/org/apache/maven/cling/transfer/SimplexTransferListenerTest.java b/impl/maven-cli/src/test/java/org/apache/maven/cling/transfer/SimplexTransferListenerTest.java new file mode 100644 index 000000000000..36abafaf5b9a --- /dev/null +++ b/impl/maven-cli/src/test/java/org/apache/maven/cling/transfer/SimplexTransferListenerTest.java @@ -0,0 +1,112 @@ +/* + * 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.apache.maven.cling.transfer; + +import java.io.File; + +import org.eclipse.aether.DefaultRepositorySystemSession; +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.transfer.TransferCancelledException; +import org.eclipse.aether.transfer.TransferEvent; +import org.eclipse.aether.transfer.TransferListener; +import org.eclipse.aether.transfer.TransferResource; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +class SimplexTransferListenerTest { + @Test + void cancellation() throws InterruptedException { + TransferListener delegate = new TransferListener() { + @Override + public void transferInitiated(TransferEvent event) throws TransferCancelledException { + throw new TransferCancelledException(); + } + + @Override + public void transferStarted(TransferEvent event) throws TransferCancelledException { + throw new TransferCancelledException(); + } + + @Override + public void transferProgressed(TransferEvent event) throws TransferCancelledException { + throw new TransferCancelledException(); + } + + @Override + public void transferCorrupted(TransferEvent event) throws TransferCancelledException { + throw new TransferCancelledException(); + } + + @Override + public void transferSucceeded(TransferEvent event) {} + + @Override + public void transferFailed(TransferEvent event) {} + }; + + SimplexTransferListener listener = new SimplexTransferListener(delegate); + + TransferResource resource = + new TransferResource(null, null, "http://maven.org/test/test-resource", new File("file"), null); + DefaultRepositorySystemSession session = new DefaultRepositorySystemSession(h -> false); // no close handle + + // for technical reasons we cannot throw here, even if delegate does cancel transfer + listener.transferInitiated(event(session, resource, TransferEvent.EventType.INITIATED)); + + Thread.sleep(500); // to make sure queue is processed, cancellation applied + + // subsequent call will cancel + assertThrows( + TransferCancelledException.class, + () -> listener.transferStarted(event(session, resource, TransferEvent.EventType.STARTED))); + } + + @Test + void handlesAbsentTransferSource() throws InterruptedException, TransferCancelledException { + TransferResource resource = new TransferResource(null, null, "http://maven.org/test/test-resource", null, null); + + RepositorySystemSession session = Mockito.mock(RepositorySystemSession.class); + TransferListener delegate = Mockito.mock(TransferListener.class); + SimplexTransferListener listener = new SimplexTransferListener(delegate); + + TransferEvent transferInitiatedEvent = event(session, resource, TransferEvent.EventType.INITIATED); + TransferEvent transferStartedEvent = event(session, resource, TransferEvent.EventType.STARTED); + TransferEvent transferProgressedEvent = event(session, resource, TransferEvent.EventType.PROGRESSED); + TransferEvent transferSucceededEvent = event(session, resource, TransferEvent.EventType.SUCCEEDED); + + listener.transferInitiated(transferInitiatedEvent); + listener.transferStarted(transferStartedEvent); + listener.transferProgressed(transferProgressedEvent); + listener.transferSucceeded(transferSucceededEvent); + + Thread.sleep(500); // to make sure queue is processed, cancellation applied + + Mockito.verify(delegate).transferInitiated(transferInitiatedEvent); + Mockito.verify(delegate).transferStarted(transferStartedEvent); + Mockito.verify(delegate).transferProgressed(transferProgressedEvent); + Mockito.verify(delegate).transferSucceeded(transferSucceededEvent); + } + + private static TransferEvent event( + RepositorySystemSession session, TransferResource resource, TransferEvent.EventType type) { + return new TransferEvent.Builder(session, resource).setType(type).build(); + } +} diff --git a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/AbstractSession.java b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/AbstractSession.java index e0a59e07ea6f..a2245631312a 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/AbstractSession.java +++ b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/AbstractSession.java @@ -44,6 +44,7 @@ import org.apache.maven.api.Language; import org.apache.maven.api.Listener; import org.apache.maven.api.LocalRepository; +import org.apache.maven.api.MonotonicClock; import org.apache.maven.api.Node; import org.apache.maven.api.Packaging; import org.apache.maven.api.PathScope; @@ -92,6 +93,7 @@ import org.eclipse.aether.RepositorySystem; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.ArtifactType; +import org.eclipse.aether.transfer.TransferResource; import static org.apache.maven.internal.impl.Utils.map; import static org.apache.maven.internal.impl.Utils.nonNull; @@ -113,6 +115,10 @@ public abstract class AbstractSession implements InternalSession { private final Map allDependencies = Collections.synchronizedMap(new WeakHashMap<>()); + static { + TransferResource.setClock(MonotonicClock.get()); + } + public AbstractSession( RepositorySystemSession session, RepositorySystem repositorySystem,