[MRESOLVER-46] Add support InputStream/OutputStream transformers
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/AbstractForwardingRepositorySystemSession.java b/maven-resolver-api/src/main/java/org/eclipse/aether/AbstractForwardingRepositorySystemSession.java
index 20df431..35759cb 100644
--- a/maven-resolver-api/src/main/java/org/eclipse/aether/AbstractForwardingRepositorySystemSession.java
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/AbstractForwardingRepositorySystemSession.java
@@ -36,6 +36,7 @@
import org.eclipse.aether.resolution.ArtifactDescriptorPolicy;
import org.eclipse.aether.resolution.ResolutionErrorPolicy;
import org.eclipse.aether.transfer.TransferListener;
+import org.eclipse.aether.transform.FileTransformerManager;
/**
* A special repository system session to enable decorating or proxying another session. To do so, clients have to
@@ -61,129 +62,160 @@
*/
protected abstract RepositorySystemSession getSession();
+ @Override
public boolean isOffline()
{
return getSession().isOffline();
}
+ @Override
public boolean isIgnoreArtifactDescriptorRepositories()
{
return getSession().isIgnoreArtifactDescriptorRepositories();
}
+ @Override
public ResolutionErrorPolicy getResolutionErrorPolicy()
{
return getSession().getResolutionErrorPolicy();
}
+ @Override
public ArtifactDescriptorPolicy getArtifactDescriptorPolicy()
{
return getSession().getArtifactDescriptorPolicy();
}
+ @Override
public String getChecksumPolicy()
{
return getSession().getChecksumPolicy();
}
+ @Override
public String getUpdatePolicy()
{
return getSession().getUpdatePolicy();
}
+ @Override
public LocalRepository getLocalRepository()
{
return getSession().getLocalRepository();
}
+ @Override
public LocalRepositoryManager getLocalRepositoryManager()
{
return getSession().getLocalRepositoryManager();
}
+ @Override
public WorkspaceReader getWorkspaceReader()
{
return getSession().getWorkspaceReader();
}
+ @Override
public RepositoryListener getRepositoryListener()
{
return getSession().getRepositoryListener();
}
+ @Override
public TransferListener getTransferListener()
{
return getSession().getTransferListener();
}
+ @Override
public Map<String, String> getSystemProperties()
{
return getSession().getSystemProperties();
}
+ @Override
public Map<String, String> getUserProperties()
{
return getSession().getUserProperties();
}
+ @Override
public Map<String, Object> getConfigProperties()
{
return getSession().getConfigProperties();
}
+ @Override
public MirrorSelector getMirrorSelector()
{
return getSession().getMirrorSelector();
}
+ @Override
public ProxySelector getProxySelector()
{
return getSession().getProxySelector();
}
+ @Override
public AuthenticationSelector getAuthenticationSelector()
{
return getSession().getAuthenticationSelector();
}
+ @Override
public ArtifactTypeRegistry getArtifactTypeRegistry()
{
return getSession().getArtifactTypeRegistry();
}
+ @Override
public DependencyTraverser getDependencyTraverser()
{
return getSession().getDependencyTraverser();
}
+ @Override
public DependencyManager getDependencyManager()
{
return getSession().getDependencyManager();
}
+ @Override
public DependencySelector getDependencySelector()
{
return getSession().getDependencySelector();
}
+ @Override
public VersionFilter getVersionFilter()
{
return getSession().getVersionFilter();
}
+ @Override
public DependencyGraphTransformer getDependencyGraphTransformer()
{
return getSession().getDependencyGraphTransformer();
}
+ @Override
public SessionData getData()
{
return getSession().getData();
}
+ @Override
public RepositoryCache getCache()
{
return getSession().getCache();
}
+
+ @Override
+ public FileTransformerManager geFileTransformerManager()
+ {
+ return getSession().geFileTransformerManager();
+ }
}
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/DefaultRepositorySystemSession.java b/maven-resolver-api/src/main/java/org/eclipse/aether/DefaultRepositorySystemSession.java
index 13773df..8ba4e15 100644
--- a/maven-resolver-api/src/main/java/org/eclipse/aether/DefaultRepositorySystemSession.java
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/DefaultRepositorySystemSession.java
@@ -24,6 +24,9 @@
import java.util.Map;
import static java.util.Objects.requireNonNull;
+import java.util.Collection;
+
+import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.ArtifactType;
import org.eclipse.aether.artifact.ArtifactTypeRegistry;
import org.eclipse.aether.collection.DependencyGraphTransformer;
@@ -44,6 +47,8 @@
import org.eclipse.aether.resolution.ArtifactDescriptorPolicy;
import org.eclipse.aether.resolution.ResolutionErrorPolicy;
import org.eclipse.aether.transfer.TransferListener;
+import org.eclipse.aether.transform.FileTransformer;
+import org.eclipse.aether.transform.FileTransformerManager;
/**
* A simple repository system session.
@@ -73,6 +78,8 @@
private LocalRepositoryManager localRepositoryManager;
+ private FileTransformerManager fileTransformerManager;
+
private WorkspaceReader workspaceReader;
private RepositoryListener repositoryListener;
@@ -130,6 +137,7 @@
proxySelector = NullProxySelector.INSTANCE;
authenticationSelector = NullAuthenticationSelector.INSTANCE;
artifactTypeRegistry = NullArtifactTypeRegistry.INSTANCE;
+ fileTransformerManager = NullFileTransformerManager.INSTANCE;
data = new DefaultSessionData();
}
@@ -317,6 +325,23 @@
return this;
}
+ @Override
+ public FileTransformerManager geFileTransformerManager()
+ {
+ return fileTransformerManager;
+ }
+
+ public DefaultRepositorySystemSession setFileTransformerManager( FileTransformerManager fileTransformerManager )
+ {
+ failIfReadOnly();
+ this.fileTransformerManager = fileTransformerManager;
+ if ( this.fileTransformerManager == null )
+ {
+ this.fileTransformerManager = NullFileTransformerManager.INSTANCE;
+ }
+ return this;
+ }
+
public WorkspaceReader getWorkspaceReader()
{
return workspaceReader;
@@ -829,4 +854,15 @@
}
+ static final class NullFileTransformerManager implements FileTransformerManager
+ {
+ public static final FileTransformerManager INSTANCE = new NullFileTransformerManager();
+
+ @Override
+ public Collection<FileTransformer> getTransformersForArtifact( Artifact artifact )
+ {
+ return Collections.emptyList();
+ }
+ }
+
}
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/RepositorySystemSession.java b/maven-resolver-api/src/main/java/org/eclipse/aether/RepositorySystemSession.java
index 888f29c..025ec80 100644
--- a/maven-resolver-api/src/main/java/org/eclipse/aether/RepositorySystemSession.java
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/RepositorySystemSession.java
@@ -37,6 +37,7 @@
import org.eclipse.aether.resolution.ArtifactDescriptorPolicy;
import org.eclipse.aether.resolution.ResolutionErrorPolicy;
import org.eclipse.aether.transfer.TransferListener;
+import org.eclipse.aether.transform.FileTransformerManager;
/**
* Defines settings and components that control the repository system. Once initialized, the session object itself is
@@ -260,4 +261,11 @@
*/
RepositoryCache getCache();
+ /**
+ * Get the file transformer manager
+ *
+ * @return the manager, never {@code null}
+ */
+ FileTransformerManager geFileTransformerManager();
+
}
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/transform/FileTransformer.java b/maven-resolver-api/src/main/java/org/eclipse/aether/transform/FileTransformer.java
new file mode 100644
index 0000000..17c997c
--- /dev/null
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/transform/FileTransformer.java
@@ -0,0 +1,51 @@
+package org.eclipse.aether.transform;
+
+/*
+ * 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.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.eclipse.aether.artifact.Artifact;
+
+/**
+ * Can transform a file while installing/deploying
+ *
+ * @author Robert Scholte
+ * @since 1.2.0
+ */
+public interface FileTransformer
+{
+ /**
+ * Transform the target location
+ *
+ * @param artifact the original artifact
+ * @return the transformed artifact
+ */
+ Artifact transformArtifact( Artifact artifact );
+
+ /**
+ * Transform the data
+ *
+ * @param file the file with the original data
+ * @return the transformed data
+ */
+ InputStream transformData( File file ) throws IOException;
+}
diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/transform/FileTransformerManager.java b/maven-resolver-api/src/main/java/org/eclipse/aether/transform/FileTransformerManager.java
new file mode 100644
index 0000000..9eaec01
--- /dev/null
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/transform/FileTransformerManager.java
@@ -0,0 +1,49 @@
+package org.eclipse.aether.transform;
+
+/*
+ * 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.
+ */
+
+import java.util.Collection;
+
+import org.eclipse.aether.artifact.Artifact;
+
+/**
+ * Manager the FileTransformers
+ *
+ * @author Robert Scholte
+ * @since 1.2.0
+ */
+public interface FileTransformerManager
+{
+ /**
+ * <p>
+ * All transformers for this specific artifact. Be aware that if you want to create additional files, but also want
+ * to the original to be deployed, you must add an explicit transformer for that file too (one that doesn't
+ * transform the artifact and data).
+ * </p>
+ *
+ * <p><strong>IMPORTANT</strong> When using a fileTransformer, the content of the file is stored in memory to ensure
+ * that file content and checksums stay in sync!
+ * </p>
+ *
+ * @param artifact the artifact
+ * @return a collection of FileTransformers to apply on the artifact, never {@code null}
+ */
+ Collection<FileTransformer> getTransformersForArtifact( Artifact artifact );
+}
diff --git a/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnector.java b/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnector.java
index 87c8191..d15f80c 100644
--- a/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnector.java
+++ b/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnector.java
@@ -19,15 +19,18 @@
* under the License.
*/
+import static java.util.Objects.requireNonNull;
+
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
import java.net.URI;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import static java.util.Objects.requireNonNull;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
@@ -60,6 +63,7 @@
import org.eclipse.aether.transfer.NoTransporterException;
import org.eclipse.aether.transfer.TransferEvent;
import org.eclipse.aether.transfer.TransferResource;
+import org.eclipse.aether.transform.FileTransformer;
import org.eclipse.aether.util.ChecksumUtils;
import org.eclipse.aether.util.ConfigUtils;
import org.eclipse.aether.util.concurrency.RunnableErrorForwarder;
@@ -279,7 +283,7 @@
List<RepositoryLayout.Checksum> checksums = layout.getChecksums( transfer.getArtifact(), true, location );
- Runnable task = new PutTaskRunner( location, transfer.getFile(), checksums, listener );
+ Runnable task = new PutTaskRunner( location, transfer.getFile(), transfer.getFileTransformer(), checksums, listener );
task.run();
}
@@ -495,24 +499,70 @@
private final File file;
+ private final FileTransformer fileTransformer;
+
private final Collection<RepositoryLayout.Checksum> checksums;
PutTaskRunner( URI path, File file, List<RepositoryLayout.Checksum> checksums,
+ TransferTransportListener<?> listener )
+ {
+ this( path, file, null, checksums, listener );
+ }
+
+ /**
+ * <strong>IMPORTANT</strong> When using a fileTransformer, the content of the file is stored in memory to
+ * ensure that file content and checksums stay in sync!
+ *
+ * @param path
+ * @param file
+ * @param fileTransformer
+ * @param checksums
+ * @param listener
+ */
+ PutTaskRunner( URI path, File file, FileTransformer fileTransformer, List<RepositoryLayout.Checksum> checksums,
TransferTransportListener<?> listener )
{
super( path, listener );
this.file = requireNonNull( file, "source file cannot be null" );
+ this.fileTransformer = fileTransformer;
this.checksums = safe( checksums );
}
protected void runTask()
throws Exception
{
- transporter.put( new PutTask( path ).setDataFile( file ).setListener( listener ) );
- uploadChecksums( file, path );
+ if ( fileTransformer != null )
+ {
+ // transform data once to byte array, ensure constant data for checksum
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ byte[] buffer = new byte[1024];
+
+ try ( InputStream transformData = fileTransformer.transformData( file ) )
+ {
+ for ( int read; ( read = transformData.read( buffer, 0, buffer.length ) ) != -1; )
+ {
+ baos.write( buffer, 0, read );
+ }
+ }
+
+ byte[] bytes = baos.toByteArray();
+ transporter.put( new PutTask( path ).setDataBytes( bytes ).setListener( listener ) );
+ uploadChecksums( file, bytes, path );
+ }
+ else
+ {
+ transporter.put( new PutTask( path ).setDataFile( file ).setListener( listener ) );
+ uploadChecksums( file, null , path );
+ }
}
- private void uploadChecksums( File file, URI location )
+ /**
+ *
+ * @param file source
+ * @param bytes transformed data from file or {@code null}
+ * @param location target
+ */
+ private void uploadChecksums( File file, byte[] bytes, URI location )
{
if ( checksums.isEmpty() )
{
@@ -520,12 +570,22 @@
}
try
{
- Set<String> algos = new HashSet<String>();
+ Set<String> algos = new HashSet<>();
for ( RepositoryLayout.Checksum checksum : checksums )
{
algos.add( checksum.getAlgorithm() );
}
- Map<String, Object> sumsByAlgo = ChecksumUtils.calc( file, algos );
+
+ Map<String, Object> sumsByAlgo;
+ if ( bytes != null )
+ {
+ sumsByAlgo = ChecksumUtils.calc( bytes, algos );
+ }
+ else
+ {
+ sumsByAlgo = ChecksumUtils.calc( file, algos );
+ }
+
for ( RepositoryLayout.Checksum checksum : checksums )
{
uploadChecksum( checksum.getLocation(), sumsByAlgo.get( checksum.getAlgorithm() ) );
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultDeployer.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultDeployer.java
index 194ce30..4d6f5be 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultDeployer.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultDeployer.java
@@ -71,6 +71,8 @@
import org.eclipse.aether.transfer.RepositoryOfflineException;
import org.eclipse.aether.transfer.TransferCancelledException;
import org.eclipse.aether.transfer.TransferEvent;
+import org.eclipse.aether.transform.FileTransformer;
+import org.eclipse.aether.transform.FileTransformerManager;
/**
*/
@@ -237,6 +239,8 @@
{
List<? extends MetadataGenerator> generators = getMetadataGenerators( session, request );
+ FileTransformerManager fileTransformerManager = session.geFileTransformerManager();
+
List<ArtifactUpload> artifactUploads = new ArrayList<ArtifactUpload>();
List<MetadataUpload> metadataUploads = new ArrayList<MetadataUpload>();
IdentityHashMap<Metadata, Object> processedMetadata = new IdentityHashMap<Metadata, Object>();
@@ -266,10 +270,26 @@
artifacts.set( i, artifact );
- ArtifactUpload upload = new ArtifactUpload( artifact, artifact.getFile() );
- upload.setTrace( trace );
- upload.setListener( new ArtifactUploadListener( catapult, upload ) );
- artifactUploads.add( upload );
+ Collection<FileTransformer> fileTransformers = fileTransformerManager.getTransformersForArtifact( artifact );
+ if ( !fileTransformers.isEmpty() )
+ {
+ for ( FileTransformer fileTransformer : fileTransformers )
+ {
+ Artifact targetArtifact = fileTransformer.transformArtifact( artifact );
+
+ ArtifactUpload upload = new ArtifactUpload( targetArtifact, artifact.getFile(), fileTransformer );
+ upload.setTrace( trace );
+ upload.setListener( new ArtifactUploadListener( catapult, upload ) );
+ artifactUploads.add( upload );
+ }
+ }
+ else
+ {
+ ArtifactUpload upload = new ArtifactUpload( artifact, artifact.getFile() );
+ upload.setTrace( trace );
+ upload.setListener( new ArtifactUploadListener( catapult, upload ) );
+ artifactUploads.add( upload );
+ }
}
connector.put( artifactUploads, null );
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultInstaller.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultInstaller.java
index 7cd14d0..e4c998a 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultInstaller.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultInstaller.java
@@ -19,12 +19,14 @@
* under the License.
*/
+import static java.util.Objects.requireNonNull;
+
import java.io.File;
+import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.List;
-import static java.util.Objects.requireNonNull;
import java.util.Set;
import javax.inject.Inject;
@@ -52,6 +54,7 @@
import org.eclipse.aether.spi.io.FileProcessor;
import org.eclipse.aether.spi.locator.Service;
import org.eclipse.aether.spi.locator.ServiceLocator;
+import org.eclipse.aether.transform.FileTransformer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -236,9 +239,37 @@
File srcFile = artifact.getFile();
- File dstFile = new File( lrm.getRepository().getBasedir(), lrm.getPathForLocalArtifact( artifact ) );
+ Collection<FileTransformer> fileTransformers = session.geFileTransformerManager().getTransformersForArtifact( artifact );
+ if ( fileTransformers.isEmpty() )
+ {
+ install( session, trace, artifact, lrm, srcFile, null );
+ }
+ else
+ {
+ for ( FileTransformer fileTransformer : fileTransformers )
+ {
+ install( session, trace, artifact, lrm, srcFile, fileTransformer );
+ }
+ }
+ }
- artifactInstalling( session, trace, artifact, dstFile );
+ private void install( RepositorySystemSession session, RequestTrace trace, Artifact artifact,
+ LocalRepositoryManager lrm, File srcFile, FileTransformer fileTransformer )
+ throws InstallationException
+ {
+ final Artifact targetArtifact;
+ if ( fileTransformer != null )
+ {
+ targetArtifact = fileTransformer.transformArtifact( artifact );
+ }
+ else
+ {
+ targetArtifact = artifact;
+ }
+
+ File dstFile = new File( lrm.getRepository().getBasedir(), lrm.getPathForLocalArtifact( targetArtifact ) );
+
+ artifactInstalling( session, trace, targetArtifact, dstFile );
Exception exception = null;
try
@@ -249,29 +280,37 @@
}
boolean copy =
- "pom".equals( artifact.getExtension() ) || srcFile.lastModified() != dstFile.lastModified()
+ "pom".equals( targetArtifact.getExtension() ) || srcFile.lastModified() != dstFile.lastModified()
|| srcFile.length() != dstFile.length() || !srcFile.exists();
- if ( copy )
+ if ( !copy )
+ {
+ LOGGER.debug( "Skipped re-installing {} to {}, seems unchanged", srcFile, dstFile );
+ }
+ else if ( fileTransformer != null )
+ {
+ try ( InputStream is = fileTransformer.transformData( srcFile ) )
+ {
+ fileProcessor.write( dstFile, is );
+ dstFile.setLastModified( srcFile.lastModified() );
+ }
+ }
+ else
{
fileProcessor.copy( srcFile, dstFile );
dstFile.setLastModified( srcFile.lastModified() );
}
- else
- {
- LOGGER.debug( "Skipped re-installing {} to {}, seems unchanged", srcFile, dstFile );
- }
- lrm.add( session, new LocalArtifactRegistration( artifact ) );
+ lrm.add( session, new LocalArtifactRegistration( targetArtifact ) );
}
catch ( Exception e )
{
exception = e;
- throw new InstallationException( "Failed to install artifact " + artifact + ": " + e.getMessage(), e );
+ throw new InstallationException( "Failed to install artifact " + targetArtifact + ": " + e.getMessage(), e );
}
finally
{
- artifactInstalled( session, trace, artifact, dstFile, exception );
+ artifactInstalled( session, trace, targetArtifact, dstFile, exception );
}
}
diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultDeployerTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultDeployerTest.java
index 9465e87..fa7dc42 100644
--- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultDeployerTest.java
+++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultDeployerTest.java
@@ -19,10 +19,16 @@
* under the License.
*/
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -37,7 +43,6 @@
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.deployment.DeployRequest;
import org.eclipse.aether.deployment.DeploymentException;
-import org.eclipse.aether.internal.impl.DefaultDeployer;
import org.eclipse.aether.internal.test.util.TestFileProcessor;
import org.eclipse.aether.internal.test.util.TestFileUtils;
import org.eclipse.aether.internal.test.util.TestUtils;
@@ -52,6 +57,8 @@
import org.eclipse.aether.spi.connector.MetadataUpload;
import org.eclipse.aether.spi.connector.RepositoryConnector;
import org.eclipse.aether.transfer.MetadataNotFoundException;
+import org.eclipse.aether.transform.FileTransformer;
+import org.eclipse.aether.util.artifact.SubArtifact;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -382,4 +389,35 @@
assertNull( props.toString(), props.get( "old" ) );
}
+ @Test
+ public void testFileTransformer() throws Exception
+ {
+ final Artifact transformedArtifact = new SubArtifact( artifact, null, "raj" );
+ FileTransformer transformer = new FileTransformer()
+ {
+ @Override
+ public InputStream transformData( File file )
+ {
+ return new ByteArrayInputStream( "transformed data".getBytes( StandardCharsets.UTF_8 ) );
+ }
+
+ @Override
+ public Artifact transformArtifact( Artifact artifact )
+ {
+ return transformedArtifact;
+ }
+ };
+
+ StubFileTransformerManager fileTransformerManager = new StubFileTransformerManager();
+ fileTransformerManager.addFileTransformer( "jar", transformer );
+ session.setFileTransformerManager( fileTransformerManager );
+
+ request = new DeployRequest();
+ request.addArtifact( artifact );
+ deployer.deploy( session, request );
+
+ Artifact putArtifact = connector.getActualArtifactPutRequests().get( 0 );
+ assertEquals( transformedArtifact, putArtifact );
+ }
+
}
diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultInstallerTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultInstallerTest.java
index efabedd..68a3547 100644
--- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultInstallerTest.java
+++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultInstallerTest.java
@@ -19,10 +19,20 @@
* under the License.
*/
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
import java.io.File;
+import java.io.FileReader;
import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
import java.util.List;
import org.eclipse.aether.DefaultRepositorySystemSession;
@@ -33,8 +43,6 @@
import org.eclipse.aether.installation.InstallRequest;
import org.eclipse.aether.installation.InstallResult;
import org.eclipse.aether.installation.InstallationException;
-import org.eclipse.aether.internal.impl.DefaultFileProcessor;
-import org.eclipse.aether.internal.impl.DefaultInstaller;
import org.eclipse.aether.internal.test.util.TestFileProcessor;
import org.eclipse.aether.internal.test.util.TestFileUtils;
import org.eclipse.aether.internal.test.util.TestLocalRepositoryManager;
@@ -42,6 +50,8 @@
import org.eclipse.aether.metadata.DefaultMetadata;
import org.eclipse.aether.metadata.Metadata;
import org.eclipse.aether.metadata.Metadata.Nature;
+import org.eclipse.aether.transform.FileTransformer;
+import org.eclipse.aether.util.artifact.SubArtifact;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -410,4 +420,44 @@
assertEquals( "artifact timestamp was not set to src file", artifact.getFile().lastModified(),
localArtifactFile.lastModified() );
}
+
+ @Test
+ public void testFileTransformer() throws Exception
+ {
+ final Artifact transformedArtifact = new SubArtifact( artifact, null, "raj" );
+ FileTransformer transformer = new FileTransformer()
+ {
+ @Override
+ public InputStream transformData( File file )
+ throws IOException
+ {
+ return new ByteArrayInputStream( "transformed data".getBytes( StandardCharsets.UTF_8 ) );
+ }
+
+ @Override
+ public Artifact transformArtifact( Artifact artifact )
+ {
+ return transformedArtifact;
+ }
+ };
+
+ StubFileTransformerManager fileTransformerManager = new StubFileTransformerManager();
+ fileTransformerManager.addFileTransformer( "jar", transformer );
+ session.setFileTransformerManager( fileTransformerManager );
+
+ request = new InstallRequest();
+ request.addArtifact( artifact );
+ installer.install( session, request );
+
+ assertFalse( localArtifactFile.exists() );
+
+ String transformedArtifactPath = session.getLocalRepositoryManager().getPathForLocalArtifact( transformedArtifact );
+ File transformedArtifactFile = new File( session.getLocalRepository().getBasedir(), transformedArtifactPath );
+ assertTrue( transformedArtifactFile.exists() );
+
+ try ( BufferedReader r = new BufferedReader( new FileReader( transformedArtifactFile ) ) )
+ {
+ assertEquals( "transformed data", r.readLine() );
+ }
+ }
}
diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/StubFileTransformerManager.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/StubFileTransformerManager.java
new file mode 100644
index 0000000..a079a2a
--- /dev/null
+++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/StubFileTransformerManager.java
@@ -0,0 +1,50 @@
+package org.eclipse.aether.internal.impl;
+
+/*
+ * 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.
+ */
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.transform.FileTransformer;
+import org.eclipse.aether.transform.FileTransformerManager;
+
+public class StubFileTransformerManager implements FileTransformerManager
+{
+ private Map<String, Collection<FileTransformer>> fileTransformers = new HashMap<>();
+
+ @Override
+ public Collection<FileTransformer> getTransformersForArtifact( Artifact artifact )
+ {
+ return fileTransformers.get( artifact.getExtension() );
+ }
+
+ public void addFileTransformer( String extension, FileTransformer fileTransformer )
+ {
+ if ( !fileTransformers.containsKey( extension ) )
+ {
+ fileTransformers.put( extension, new HashSet<FileTransformer>() );
+ }
+ fileTransformers.get( extension ).add( fileTransformer );
+ }
+
+}
diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/ArtifactUpload.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/ArtifactUpload.java
index f85539e..90323b1 100644
--- a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/ArtifactUpload.java
+++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/ArtifactUpload.java
@@ -25,6 +25,7 @@
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.transfer.ArtifactTransferException;
import org.eclipse.aether.transfer.TransferListener;
+import org.eclipse.aether.transform.FileTransformer;
/**
* An upload of an artifact to a remote repository. A repository connector processing this upload has to use
@@ -33,6 +34,7 @@
public final class ArtifactUpload
extends ArtifactTransfer
{
+ private FileTransformer fileTransformer;
/**
* Creates a new uninitialized upload.
@@ -54,6 +56,24 @@
setFile( file );
}
+ /**
+ * <p>Creates a new upload with the specified properties.</p>
+ *
+ * <p><strong>IMPORTANT</strong> When using a fileTransformer, the
+ * content of the file is stored in memory to ensure that file content and checksums stay in sync!
+ * </p>
+ *
+ * @param artifact The artifact to upload, may be {@code null}.
+ * @param file The local file to upload the artifact from, may be {@code null}.
+ * @param fileTransformer The file transformer, may be {@code null}.
+ */
+ public ArtifactUpload( Artifact artifact, File file, FileTransformer fileTransformer )
+ {
+ setArtifact( artifact );
+ setFile( file );
+ setFileTransformer( fileTransformer );
+ }
+
@Override
public ArtifactUpload setArtifact( Artifact artifact )
{
@@ -88,11 +108,30 @@
super.setTrace( trace );
return this;
}
+
+ public ArtifactUpload setFileTransformer( FileTransformer fileTransformer )
+ {
+ this.fileTransformer = fileTransformer;
+ return this;
+ }
+
+ public FileTransformer getFileTransformer()
+ {
+ return fileTransformer;
+ }
@Override
public String toString()
{
- return getArtifact() + " - " + getFile();
+ if ( getFileTransformer() != null )
+ {
+ return getArtifact() + " >>> " + getFileTransformer().transformArtifact( getArtifact() )
+ + " - " + getFile();
+ }
+ else
+ {
+ return getArtifact() + " - " + getFile();
+ }
}
}
diff --git a/maven-resolver-transport-http/src/test/java/org/eclipse/aether/transport/http/HttpTransporterTest.java b/maven-resolver-transport-http/src/test/java/org/eclipse/aether/transport/http/HttpTransporterTest.java
index 880f2d8..384827f 100644
--- a/maven-resolver-transport-http/src/test/java/org/eclipse/aether/transport/http/HttpTransporterTest.java
+++ b/maven-resolver-transport-http/src/test/java/org/eclipse/aether/transport/http/HttpTransporterTest.java
@@ -921,7 +921,7 @@
assertEquals( 1, listener.startedCount );
}
- @Test( timeout = 10000L )
+ @Test( timeout = 20000L )
public void testConcurrency()
throws Exception
{
diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/ChecksumUtils.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/ChecksumUtils.java
index 415e712..9b4714c 100644
--- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/ChecksumUtils.java
+++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/ChecksumUtils.java
@@ -20,6 +20,7 @@
*/
import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
@@ -54,10 +55,8 @@
throws IOException
{
String checksum = "";
- BufferedReader br = null;
- try
+ try ( BufferedReader br = new BufferedReader( new InputStreamReader( new FileInputStream( checksumFile ), StandardCharsets.UTF_8 ), 512 ) )
{
- br = new BufferedReader( new InputStreamReader( new FileInputStream( checksumFile ), StandardCharsets.UTF_8 ), 512 );
while ( true )
{
String line = br.readLine();
@@ -73,21 +72,6 @@
}
}
}
- finally
- {
- try
- {
- if ( br != null )
- {
- br.close();
- br = null;
- }
- }
- catch ( IOException e )
- {
- // Suppressed due to an exception already thrown in the try block.
- }
- }
if ( checksum.matches( ".+= [0-9A-Fa-f]+" ) )
{
@@ -118,6 +102,20 @@
* @throws IOException If the data file could not be read.
*/
public static Map<String, Object> calc( File dataFile, Collection<String> algos )
+ throws IOException
+ {
+ return calc( new FileInputStream( dataFile ), algos );
+ }
+
+
+ public static Map<String, Object> calc( byte[] dataBytes, Collection<String> algos )
+ throws IOException
+ {
+ return calc( new ByteArrayInputStream( dataBytes ), algos );
+ }
+
+
+ private static Map<String, Object> calc( InputStream data, Collection<String> algos )
throws IOException
{
Map<String, Object> results = new LinkedHashMap<String, Object>();
@@ -135,10 +133,8 @@
}
}
- InputStream in = null;
- try
+ try ( InputStream in = data )
{
- in = new FileInputStream( dataFile );
for ( byte[] buffer = new byte[ 32 * 1024 ];; )
{
int read = in.read( buffer );
@@ -151,22 +147,6 @@
digest.update( buffer, 0, read );
}
}
- in.close();
- in = null;
- }
- finally
- {
- try
- {
- if ( in != null )
- {
- in.close();
- }
- }
- catch ( IOException e )
- {
- // Suppressed due to an exception already thrown in the try block.
- }
}
for ( Map.Entry<String, MessageDigest> entry : digests.entrySet() )
@@ -178,6 +158,7 @@
return results;
}
+
/**
* Creates a hexadecimal representation of the specified bytes. Each byte is converted into a two-digit hex number
diff --git a/maven-resolver-util/src/test/java/org/eclipse/aether/util/ChecksumUtilTest.java b/maven-resolver-util/src/test/java/org/eclipse/aether/util/ChecksumUtilTest.java
index b249e82..08d1c64 100644
--- a/maven-resolver-util/src/test/java/org/eclipse/aether/util/ChecksumUtilTest.java
+++ b/maven-resolver-util/src/test/java/org/eclipse/aether/util/ChecksumUtilTest.java
@@ -37,30 +37,32 @@
public class ChecksumUtilTest
{
- private File emptyFile;
+ private static final String EMPTY = "EMPTY";
+ private static final String PATTERN = "PATTERN";
+ private static final String TEXT = "TEXT";
+
+ private Map<String, File> files = new HashMap<>(3);
+
+ private Map<String, byte[]> bytes = new HashMap<>(3);
+
+ private static Map<String, String> emptyChecksums = new HashMap<>();
- private File patternFile;
+ private static Map<String, String> patternChecksums = new HashMap<>();
- private File textFile;
+ private static Map<String, String> textChecksums = new HashMap<>();
- private static Map<String, String> emptyFileChecksums = new HashMap<String, String>();
-
- private static Map<String, String> patternFileChecksums = new HashMap<String, String>();
-
- private static Map<String, String> textFileChecksums = new HashMap<String, String>();
-
- private Map<File, Map<String, String>> sums = new HashMap<File, Map<String, String>>();
+ private Map<String, Map<String, String>> sums = new HashMap<>();
@BeforeClass
public static void beforeClass()
throws IOException
{
- emptyFileChecksums.put( "MD5", "d41d8cd98f00b204e9800998ecf8427e" );
- emptyFileChecksums.put( "SHA-1", "da39a3ee5e6b4b0d3255bfef95601890afd80709" );
- patternFileChecksums.put( "MD5", "14f01d6c7de7d4cf0a4887baa3528b5a" );
- patternFileChecksums.put( "SHA-1", "feeeda19f626f9b0ef6cbf5948c1ec9531694295" );
- textFileChecksums.put( "MD5", "12582d1a662cefe3385f2113998e43ed" );
- textFileChecksums.put( "SHA-1", "a8ae272db549850eef2ff54376f8cac2770745ee" );
+ emptyChecksums.put( "MD5", "d41d8cd98f00b204e9800998ecf8427e" );
+ emptyChecksums.put( "SHA-1", "da39a3ee5e6b4b0d3255bfef95601890afd80709" );
+ patternChecksums.put( "MD5", "14f01d6c7de7d4cf0a4887baa3528b5a" );
+ patternChecksums.put( "SHA-1", "feeeda19f626f9b0ef6cbf5948c1ec9531694295" );
+ textChecksums.put( "MD5", "12582d1a662cefe3385f2113998e43ed" );
+ textChecksums.put( "SHA-1", "a8ae272db549850eef2ff54376f8cac2770745ee" );
}
@Before
@@ -69,15 +71,20 @@
{
sums.clear();
- emptyFile = createTempFile( new byte[] {}, 0 );
- sums.put( emptyFile, emptyFileChecksums );
+ byte[] emptyBytes = new byte[0];
+ bytes.put( EMPTY, emptyBytes );
+ files.put( EMPTY, createTempFile( emptyBytes, 0 ) );
+ sums.put( EMPTY, emptyChecksums );
- patternFile =
- createTempFile( new byte[] { 0, 1, 2, 4, 8, 16, 32, 64, 127, -1, -2, -4, -8, -16, -32, -64, -127 }, 1000 );
- sums.put( patternFile, patternFileChecksums );
+ byte[] patternBytes = writeBytes( new byte[] { 0, 1, 2, 4, 8, 16, 32, 64, 127, -1, -2, -4, -8, -16, -32, -64, -127 }, 1000 );
+ bytes.put( PATTERN, patternBytes );
+ files.put( PATTERN, createTempFile( patternBytes, 1 ) );
+ sums.put( PATTERN, patternChecksums );
- textFile = createTempFile( "the quick brown fox jumps over the lazy dog\n".getBytes( StandardCharsets.UTF_8 ), 500 );
- sums.put( textFile, textFileChecksums );
+ byte[] textBytes = writeBytes( "the quick brown fox jumps over the lazy dog\n".getBytes( StandardCharsets.UTF_8 ), 500 );
+ bytes.put( TEXT, textBytes );
+ files.put( TEXT, createTempFile( textBytes, 1 ) );
+ sums.put( TEXT, textChecksums );
}
@@ -87,10 +94,10 @@
{
Map<String, Object> checksums = null;
- for ( File file : new File[] { emptyFile, patternFile, textFile } )
+ for ( Map.Entry<String,File> fileEntry : files.entrySet() )
{
- checksums = ChecksumUtils.calc( file, Arrays.asList( "SHA-1", "MD5" ) );
+ checksums = ChecksumUtils.calc( fileEntry.getValue(), Arrays.asList( "SHA-1", "MD5" ) );
for ( Entry<String, Object> entry : checksums.entrySet() )
{
@@ -99,11 +106,11 @@
throw (Throwable) entry.getValue();
}
String actual = entry.getValue().toString();
- String expected = sums.get( file ).get( entry.getKey() );
- assertEquals( String.format( "checksums do not match for '%s', algorithm '%s'", file.getName(),
+ String expected = sums.get( fileEntry.getKey() ).get( entry.getKey() );
+ assertEquals( String.format( "checksums do not match for '%s', algorithm '%s'", fileEntry.getValue().getName(),
entry.getKey() ), expected, actual );
}
- assertTrue( "Could not delete file", file.delete() );
+ assertTrue( "Could not delete file", fileEntry.getValue().delete() );
}
}
@@ -111,7 +118,7 @@
public void testFileHandleLeakage()
throws IOException
{
- for ( File file : new File[] { emptyFile, patternFile, textFile } )
+ for ( File file : files.values() )
{
for ( int i = 0; i < 150; i++ )
{
@@ -182,5 +189,38 @@
assertEquals( "ff", ChecksumUtils.toHexString( new byte[] { -1 } ) );
assertEquals( "00017f", ChecksumUtils.toHexString( new byte[] { 0, 1, 127 } ) );
}
+
+ @Test
+ public void testCalcWithByteArray() throws Throwable
+ {
+ Map<String, Object> checksums = null;
+ for ( Map.Entry<String, byte[]> bytesEntry : bytes.entrySet() )
+ {
+ checksums = ChecksumUtils.calc( bytesEntry.getValue(), Arrays.asList( "SHA-1", "MD5" ) );
+
+ for ( Entry<String, Object> entry : checksums.entrySet() )
+ {
+ if ( entry.getValue() instanceof Throwable )
+ {
+ throw (Throwable) entry.getValue();
+ }
+ String actual = entry.getValue().toString();
+ String expected = sums.get( bytesEntry.getKey() ).get( entry.getKey() );
+ assertEquals( String.format( "checksums do not match for '%s', algorithm '%s'", bytesEntry.getKey(),
+ entry.getKey() ), expected, actual );
+ }
+ }
+ }
+
+ private byte[] writeBytes( byte[] pattern, int repeat )
+ {
+ byte[] result = new byte[pattern.length * repeat];
+ for ( int i = 0; i < repeat; i++ )
+ {
+ System.arraycopy( pattern, 0, result, i * pattern.length, pattern.length );
+ }
+ return result;
+ }
}
+