This is an automated email from the ASF dual-hosted git repository. gnodet pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/maven-build-cache-extension.git
The following commit(s) were added to refs/heads/master by this push: new a3a44e7 [MBUILDCACHE-18] Use m-resolver transport layer instead of redefining another one (#10) a3a44e7 is described below commit a3a44e72404228c536b5a8c21d2e67f0183452a1 Author: Guillaume Nodet <gno...@gmail.com> AuthorDate: Tue May 10 08:52:58 2022 +0200 [MBUILDCACHE-18] Use m-resolver transport layer instead of redefining another one (#10) * [MBUILDCACHE-18] Use m-resolver transport layer instead of redefining another one * Move m-resolver as provided, clean other dependencies --- pom.xml | 17 +- .../maven/buildcache/CacheControllerImpl.java | 2 +- .../maven/buildcache/RemoteCacheRepository.java | 3 + ...oryImpl.java => RemoteCacheRepositoryImpl.java} | 164 ++--- .../apache/maven/buildcache/WagonConfigurator.java | 101 --- .../org/apache/maven/buildcache/WagonProvider.java | 65 -- .../buildcache/WagonRemoteCacheRepository.java | 696 --------------------- src/main/mdo/build-cache-config.mdo | 4 +- src/site/markdown/getting-started.md | 2 +- src/site/markdown/remote-cache.md | 4 +- 10 files changed, 96 insertions(+), 962 deletions(-) diff --git a/pom.xml b/pom.xml index 5700d9a..9f625cc 100644 --- a/pom.xml +++ b/pom.xml @@ -98,9 +98,10 @@ under the License. <scope>provided</scope> </dependency> <dependency> - <groupId>org.apache.httpcomponents</groupId> - <artifactId>httpclient</artifactId> - <version>4.5.13</version> + <groupId>org.apache.maven.resolver</groupId> + <artifactId>maven-resolver-transport-http</artifactId> + <version>${resolverVersion}</version> + <scope>provided</scope> </dependency> <dependency> <groupId>net.openhft</groupId> @@ -132,16 +133,6 @@ under the License. <artifactId>plexus-utils</artifactId> <version>${plexusUtilsVersion}</version> </dependency> - <dependency> - <groupId>org.apache.maven.wagon</groupId> - <artifactId>wagon-http</artifactId> - <version>${wagonVersion}</version> - </dependency> - <dependency> - <groupId>org.apache.maven.resolver</groupId> - <artifactId>maven-resolver-util</artifactId> - <version>${resolverVersion}</version> - </dependency> <dependency> <groupId>com.google.code.findbugs</groupId> <artifactId>jsr305</artifactId> diff --git a/src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java b/src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java index 9989d94..8cfb145 100644 --- a/src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java +++ b/src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java @@ -91,7 +91,7 @@ import static org.apache.maven.buildcache.CacheResult.failure; import static org.apache.maven.buildcache.CacheResult.partialSuccess; import static org.apache.maven.buildcache.CacheResult.rebuilded; import static org.apache.maven.buildcache.CacheResult.success; -import static org.apache.maven.buildcache.HttpCacheRepositoryImpl.BUILDINFO_XML; +import static org.apache.maven.buildcache.RemoteCacheRepository.BUILDINFO_XML; import static org.apache.maven.buildcache.checksum.KeyUtils.getVersionlessProjectKey; import static org.apache.maven.buildcache.checksum.MavenProjectInput.CACHE_IMPLEMENTATION_VERSION; diff --git a/src/main/java/org/apache/maven/buildcache/RemoteCacheRepository.java b/src/main/java/org/apache/maven/buildcache/RemoteCacheRepository.java index 7e5768f..2615803 100644 --- a/src/main/java/org/apache/maven/buildcache/RemoteCacheRepository.java +++ b/src/main/java/org/apache/maven/buildcache/RemoteCacheRepository.java @@ -32,6 +32,9 @@ import org.apache.maven.project.MavenProject; public interface RemoteCacheRepository extends CacheRepository { + String BUILDINFO_XML = "buildinfo.xml"; + String CACHE_REPORT_XML = "build-cache-report.xml"; + /** * Download an artifact to the given location. * Returns <code>true</code> if success, <code>false</code> if the artifact does not exists diff --git a/src/main/java/org/apache/maven/buildcache/HttpCacheRepositoryImpl.java b/src/main/java/org/apache/maven/buildcache/RemoteCacheRepositoryImpl.java similarity index 66% rename from src/main/java/org/apache/maven/buildcache/HttpCacheRepositoryImpl.java rename to src/main/java/org/apache/maven/buildcache/RemoteCacheRepositoryImpl.java index 748fcee..b4664fd 100644 --- a/src/main/java/org/apache/maven/buildcache/HttpCacheRepositoryImpl.java +++ b/src/main/java/org/apache/maven/buildcache/RemoteCacheRepositoryImpl.java @@ -18,10 +18,10 @@ */ package org.apache.maven.buildcache; -import java.io.ByteArrayInputStream; +import java.io.Closeable; +import java.io.File; import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; +import java.net.URI; import java.nio.file.Path; import java.util.List; import java.util.Optional; @@ -29,16 +29,6 @@ import java.util.concurrent.atomic.AtomicReference; import javax.annotation.Nonnull; import javax.inject.Inject; import javax.inject.Named; -import org.apache.commons.io.IOUtils; -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; -import org.apache.http.client.HttpClient; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPut; -import org.apache.http.entity.InputStreamEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; import org.apache.maven.SessionScoped; import org.apache.maven.buildcache.checksum.MavenProjectInput; import org.apache.maven.buildcache.xml.Build; @@ -50,46 +40,69 @@ import org.apache.maven.buildcache.xml.report.CacheReport; import org.apache.maven.buildcache.xml.report.ProjectReport; import org.apache.maven.execution.MavenSession; import org.apache.maven.project.MavenProject; +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.repository.Authentication; +import org.eclipse.aether.repository.Proxy; +import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.spi.connector.transport.GetTask; +import org.eclipse.aether.spi.connector.transport.PutTask; +import org.eclipse.aether.spi.connector.transport.Transporter; +import org.eclipse.aether.spi.connector.transport.TransporterProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * HTTP remote cache repository implementation. + * Remote cache repository implementation. */ @SessionScoped -@Named( "http" ) -public class HttpCacheRepositoryImpl implements RemoteCacheRepository +@Named( "resolver" ) +public class RemoteCacheRepositoryImpl implements RemoteCacheRepository, Closeable { - public static final String BUILDINFO_XML = "buildinfo.xml"; - public static final String CACHE_REPORT_XML = "build-cache-report.xml"; - - private static final Logger LOGGER = LoggerFactory.getLogger( HttpCacheRepositoryImpl.class ); + private static final Logger LOGGER = LoggerFactory.getLogger( RemoteCacheRepositoryImpl.class ); private final XmlService xmlService; private final CacheConfig cacheConfig; + private final Transporter transporter; @Inject - public HttpCacheRepositoryImpl( XmlService xmlService, CacheConfig cacheConfig ) + public RemoteCacheRepositoryImpl( + XmlService xmlService, + CacheConfig cacheConfig, + MavenSession mavenSession, + TransporterProvider transporterProvider ) + throws Exception { this.xmlService = xmlService; this.cacheConfig = cacheConfig; + if ( cacheConfig.isRemoteCacheEnabled() ) + { + RepositorySystemSession session = mavenSession.getRepositorySession(); + RemoteRepository repo = new RemoteRepository.Builder( + cacheConfig.getId(), "cache", cacheConfig.getUrl() ).build(); + RemoteRepository mirror = session.getMirrorSelector().getMirror( repo ); + RemoteRepository repoOrMirror = mirror != null ? mirror : repo; + Proxy proxy = session.getProxySelector().getProxy( repoOrMirror ); + Authentication auth = session.getAuthenticationSelector().getAuthentication( repoOrMirror ); + RemoteRepository repository = new RemoteRepository.Builder( repoOrMirror ) + .setProxy( proxy ) + .setAuthentication( auth ) + .build(); + this.transporter = transporterProvider.newTransporter( session, repository ); + } + else + { + this.transporter = null; + } } - @SuppressWarnings( "checkstyle:constantname" ) - private static final ThreadLocal<HttpClient> httpClient = ThreadLocal - .withInitial( HttpCacheRepositoryImpl::newHttpClient ); - - @SuppressWarnings( "checkstyle:magicnumber" ) - private static CloseableHttpClient newHttpClient() + @Override + public void close() throws IOException { - int timeoutSeconds = 60; - RequestConfig config = RequestConfig.custom() - .setConnectTimeout( timeoutSeconds * 1000 ) - .setConnectionRequestTimeout( timeoutSeconds * 1000 ) - .setSocketTimeout( timeoutSeconds * 1000 ) - .build(); - return HttpClientBuilder.create().setDefaultRequestConfig( config ).build(); + if ( transporter != null ) + { + transporter.close(); + } } @Nonnull @@ -113,7 +126,7 @@ public class HttpCacheRepositoryImpl implements RemoteCacheRepository throws IOException { final String resourceUrl = getResourceUrl( cacheResult.getContext(), BUILDINFO_XML ); - putToRemoteCache( new ByteArrayInputStream( xmlService.toBytes( build.getDto() ) ), resourceUrl ); + putToRemoteCache( xmlService.toBytes( build.getDto() ), resourceUrl ); } @Override @@ -125,7 +138,7 @@ public class HttpCacheRepositoryImpl implements RemoteCacheRepository + "/" + rootProject.getArtifactId() + "/" + buildId + "/" + CACHE_REPORT_XML; - putToRemoteCache( new ByteArrayInputStream( xmlService.toBytes( cacheReport ) ), resourceUrl ); + putToRemoteCache( xmlService.toBytes( cacheReport ), resourceUrl ); } @Override @@ -133,10 +146,7 @@ public class HttpCacheRepositoryImpl implements RemoteCacheRepository org.apache.maven.artifact.Artifact artifact ) throws IOException { final String resourceUrl = getResourceUrl( cacheResult.getContext(), CacheUtils.normalizedName( artifact ) ); - try ( InputStream inputStream = Files.newInputStream( artifact.getFile().toPath() ) ) - { - putToRemoteCache( inputStream, resourceUrl ); - } + putToRemoteCache( artifact.getFile(), resourceUrl ); } /** @@ -147,50 +157,33 @@ public class HttpCacheRepositoryImpl implements RemoteCacheRepository @Nonnull public Optional<byte[]> getResourceContent( String url ) throws IOException { - HttpGet get = new HttpGet( url ); try { LOGGER.info( "Downloading {}", url ); - HttpResponse response = httpClient.get().execute( get ); - int statusCode = response.getStatusLine().getStatusCode(); - if ( statusCode != HttpStatus.SC_OK ) - { - LOGGER.info( "Cannot download {}, status code: {}", url, statusCode ); - return Optional.empty(); - } - try ( InputStream content = response.getEntity().getContent() ) - { - return Optional.of( IOUtils.toByteArray( content ) ); - } + GetTask task = new GetTask( new URI( url ) ); + transporter.get( task ); + return Optional.of( task.getDataBytes() ); } - finally + catch ( Exception e ) { - get.releaseConnection(); + LOGGER.info( "Cannot download {}", url, e ); + return Optional.empty(); } } public boolean getResourceContent( String url, Path target ) throws IOException { - HttpGet get = new HttpGet( url ); try { LOGGER.info( "Downloading {}", url ); - HttpResponse response = httpClient.get().execute( get ); - int statusCode = response.getStatusLine().getStatusCode(); - if ( statusCode != HttpStatus.SC_OK ) - { - LOGGER.info( "Cannot download {}, status code: {}", url, statusCode ); - return false; - } - try ( InputStream content = response.getEntity().getContent() ) - { - Files.copy( content, target ); - return true; - } + GetTask task = new GetTask( new URI( url ) ).setDataFile( target.toFile() ); + transporter.get( task ); + return true; } - finally + catch ( Exception e ) { - get.releaseConnection(); + LOGGER.info( "Cannot download {}: {}", url, e.toString() ); + return false; } } @@ -208,22 +201,33 @@ public class HttpCacheRepositoryImpl implements RemoteCacheRepository + artifactId + "/" + checksum + "/" + filename; } - /** - * @param instream to be closed externally - */ - private void putToRemoteCache( InputStream instream, String url ) throws IOException + private void putToRemoteCache( byte[] bytes, String url ) throws IOException { - HttpPut httpPut = new HttpPut( url ); try { - httpPut.setEntity( new InputStreamEntity( instream ) ); - HttpResponse response = httpClient.get().execute( httpPut ); - int statusCode = response.getStatusLine().getStatusCode(); - LOGGER.info( "Saved to remote cache {}. Status: {}", url, statusCode ); + PutTask put = new PutTask( new URI( url ) ); + put.setDataBytes( bytes ); + transporter.put( put ); + LOGGER.info( "Saved to remote cache {}", url ); } - finally + catch ( Exception e ) + { + LOGGER.info( "Unable to save to remote cache {}", url, e ); + } + } + + private void putToRemoteCache( File file, String url ) throws IOException + { + try + { + PutTask put = new PutTask( new URI( url ) ); + put.setDataFile( file ); + transporter.put( put ); + LOGGER.info( "Saved to remote cache {}", url ); + } + catch ( Exception e ) { - httpPut.releaseConnection(); + LOGGER.info( "Unable to save to remote cache {}", url, e ); } } diff --git a/src/main/java/org/apache/maven/buildcache/WagonConfigurator.java b/src/main/java/org/apache/maven/buildcache/WagonConfigurator.java deleted file mode 100644 index a41c0cc..0000000 --- a/src/main/java/org/apache/maven/buildcache/WagonConfigurator.java +++ /dev/null @@ -1,101 +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.buildcache; - -import javax.inject.Inject; -import javax.inject.Singleton; -import org.apache.maven.wagon.Wagon; -import org.codehaus.plexus.PlexusContainer; -import org.codehaus.plexus.classworlds.realm.ClassRealm; -import org.codehaus.plexus.component.configurator.AbstractComponentConfigurator; -import org.codehaus.plexus.component.configurator.ComponentConfigurationException; -import org.codehaus.plexus.component.configurator.ConfigurationListener; -import org.codehaus.plexus.component.configurator.converters.composite.ObjectWithFieldsConverter; -import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator; -import org.codehaus.plexus.configuration.PlexusConfiguration; -import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration; -import org.codehaus.plexus.util.xml.Xpp3Dom; - -import static java.util.Objects.requireNonNull; - -/** - * A wagon configurator based on the Plexus component configuration framework. - */ -@Singleton -public class WagonConfigurator -{ - - private final PlexusContainer container; - - /** - * Creates a wagon configurator using the specified Plexus container. - * - * @param container The Plexus container instance to use, must not be {@code null}. - */ - @Inject - public WagonConfigurator( final PlexusContainer container ) - { - this.container = requireNonNull( container, "plexus container cannot be null" ); - } - - public void configure( Wagon wagon, Object configuration ) - throws Exception - { - requireNonNull( wagon, "wagon cannot be null" ); - requireNonNull( configuration, "configuration cannot be null" ); - - PlexusConfiguration config; - if ( configuration instanceof PlexusConfiguration ) - { - config = ( PlexusConfiguration ) configuration; - } - else if ( configuration instanceof Xpp3Dom ) - { - config = new XmlPlexusConfiguration( ( Xpp3Dom ) configuration ); - } - else - { - throw new IllegalArgumentException( "unexpected configuration type: " - + configuration.getClass().getName() ); - } - - WagonComponentConfigurator configurator = new WagonComponentConfigurator(); - - configurator.configureComponent( wagon, config, container.getContainerRealm() ); - } - - static class WagonComponentConfigurator - extends AbstractComponentConfigurator - { - - @Override - public void configureComponent( Object component, PlexusConfiguration configuration, - ExpressionEvaluator expressionEvaluator, ClassRealm containerRealm, - ConfigurationListener listener ) - throws ComponentConfigurationException - { - ObjectWithFieldsConverter converter = new ObjectWithFieldsConverter(); - - converter.processConfiguration( converterLookup, component, containerRealm, configuration, - expressionEvaluator, listener ); - } - - } - -} diff --git a/src/main/java/org/apache/maven/buildcache/WagonProvider.java b/src/main/java/org/apache/maven/buildcache/WagonProvider.java deleted file mode 100644 index f98c52a..0000000 --- a/src/main/java/org/apache/maven/buildcache/WagonProvider.java +++ /dev/null @@ -1,65 +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.buildcache; - -import javax.inject.Inject; -import javax.inject.Singleton; -import org.apache.maven.wagon.Wagon; -import org.codehaus.plexus.PlexusContainer; -import org.codehaus.plexus.component.repository.exception.ComponentLifecycleException; -import org.codehaus.plexus.component.repository.exception.ComponentLookupException; - -import static java.util.Objects.requireNonNull; - -/** - * A wagon provider backed by a Plexus container and the wagons registered with this container. - */ -@Singleton -public class WagonProvider -{ - - private final PlexusContainer container; - - /** - * Creates a wagon provider using the specified Plexus container. - * - * @param container The Plexus container instance to use, must not be {@code null}. - */ - @Inject - public WagonProvider( final PlexusContainer container ) - { - this.container = requireNonNull( container, "plexus container cannot be null" ); - } - - public Wagon lookup( String roleHint ) - throws ComponentLookupException - { - return container.lookup( Wagon.class, roleHint ); - } - - public void release( Wagon wagon ) - throws ComponentLifecycleException - { - if ( wagon != null ) - { - container.release( wagon ); - } - } - -} diff --git a/src/main/java/org/apache/maven/buildcache/WagonRemoteCacheRepository.java b/src/main/java/org/apache/maven/buildcache/WagonRemoteCacheRepository.java deleted file mode 100644 index b5ca255..0000000 --- a/src/main/java/org/apache/maven/buildcache/WagonRemoteCacheRepository.java +++ /dev/null @@ -1,696 +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.buildcache; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Optional; -import java.util.Properties; -import java.util.Queue; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; -import javax.annotation.Nonnull; -import javax.annotation.PreDestroy; -import javax.inject.Inject; -import javax.inject.Named; -import org.apache.maven.SessionScoped; -import org.apache.maven.buildcache.checksum.MavenProjectInput; -import org.apache.maven.buildcache.xml.Build; -import org.apache.maven.buildcache.xml.CacheConfig; -import org.apache.maven.buildcache.xml.CacheSource; -import org.apache.maven.buildcache.xml.XmlService; -import org.apache.maven.buildcache.xml.build.Artifact; -import org.apache.maven.buildcache.xml.report.CacheReport; -import org.apache.maven.buildcache.xml.report.ProjectReport; -import org.apache.maven.execution.MavenExecutionRequest; -import org.apache.maven.execution.MavenSession; -import org.apache.maven.project.MavenProject; -import org.apache.maven.repository.RepositorySystem; -import org.apache.maven.wagon.ResourceDoesNotExistException; -import org.apache.maven.wagon.StreamingWagon; -import org.apache.maven.wagon.Wagon; -import org.apache.maven.wagon.WagonException; -import org.apache.maven.wagon.authentication.AuthenticationInfo; -import org.apache.maven.wagon.proxy.ProxyInfo; -import org.apache.maven.wagon.proxy.ProxyInfoProvider; -import org.apache.maven.wagon.repository.Repository; -import org.apache.maven.wagon.repository.RepositoryPermissions; -import org.codehaus.plexus.component.repository.exception.ComponentLifecycleException; -import org.codehaus.plexus.component.repository.exception.ComponentLookupException; -import org.eclipse.aether.ConfigurationProperties; -import org.eclipse.aether.RepositoryException; -import org.eclipse.aether.RepositorySystemSession; -import org.eclipse.aether.repository.Authentication; -import org.eclipse.aether.repository.AuthenticationContext; -import org.eclipse.aether.repository.Proxy; -import org.eclipse.aether.repository.RemoteRepository; -import org.eclipse.aether.transfer.NoTransporterException; -import org.eclipse.aether.util.ConfigUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@SessionScoped -@Named( "wagon" ) -@SuppressWarnings( "unused" ) -public class WagonRemoteCacheRepository implements RemoteCacheRepository -{ - - public static final String BUILDINFO_XML = "buildinfo.xml"; - public static final String CACHE_REPORT_XML = "cache-report.xml"; - - private static final String CONFIG_PROP_CONFIG = "remote.caching.wagon.config"; - private static final String CONFIG_PROP_FILE_MODE = "remote.caching.wagon.perms.fileMode"; - private static final String CONFIG_PROP_DIR_MODE = "remote.caching.wagon.perms.dirMode"; - private static final String CONFIG_PROP_GROUP = "remote.caching.wagon.perms.group"; - - private static final Logger LOGGER = LoggerFactory.getLogger( HttpCacheRepositoryImpl.class ); - - private final MavenSession mavenSession; - private final XmlService xmlService; - private final CacheConfig cacheConfig; - private final WagonConfigurator wagonConfigurator; - private final WagonProvider wagonProvider; - - private final RemoteRepository repository; - private final AuthenticationContext repoAuthContext; - private final AuthenticationContext proxyAuthContext; - private final String wagonHint; - private final Repository wagonRepo; - private final AuthenticationInfo wagonAuth; - private final ProxyInfoProvider wagonProxy; - private final Properties headers; - private final AtomicBoolean closed = new AtomicBoolean(); - private final Queue<Wagon> wagons = new ConcurrentLinkedQueue<>(); - private final AtomicReference<Optional<CacheReport>> cacheReportSupplier = new AtomicReference<>(); - - @Inject - public WagonRemoteCacheRepository( MavenSession mavenSession, - XmlService xmlService, - CacheConfig cacheConfig, - WagonProvider wagonProvider, - WagonConfigurator wagonConfigurator, - RepositorySystem repositorySystem ) - throws RepositoryException - { - this.xmlService = xmlService; - this.cacheConfig = cacheConfig; - this.mavenSession = mavenSession; - this.wagonProvider = wagonProvider; - this.wagonConfigurator = wagonConfigurator; - - MavenExecutionRequest request = mavenSession.getRequest(); - RepositorySystemSession session = mavenSession.getRepositorySession(); - - cacheConfig.initialize(); - - RemoteRepository repo = new RemoteRepository.Builder( - cacheConfig.getId(), "cache", cacheConfig.getUrl() ).build(); - RemoteRepository mirror = session.getMirrorSelector().getMirror( repo ); - RemoteRepository repoOrMirror = mirror != null ? mirror : repo; - Proxy proxy = session.getProxySelector().getProxy( repoOrMirror ); - Authentication auth = session.getAuthenticationSelector().getAuthentication( repoOrMirror ); - repository = new RemoteRepository.Builder( repoOrMirror ) - .setProxy( proxy ) - .setAuthentication( auth ) - .build(); - - wagonRepo = new Repository( repository.getId(), repository.getUrl() ); - wagonRepo.setPermissions( getPermissions( repository.getId(), session ) ); - - wagonHint = wagonRepo.getProtocol().toLowerCase( Locale.ENGLISH ); - if ( wagonHint.isEmpty() ) - { - throw new IllegalArgumentException( "Could not find a wagon provider for " + wagonRepo ); - } - - try - { - wagons.add( lookupWagon() ); - } - catch ( Exception e ) - { - LOGGER.debug( "No transport {}", e, e ); - throw new NoTransporterException( repository, e ); - } - - repoAuthContext = AuthenticationContext.forRepository( session, repository ); - proxyAuthContext = AuthenticationContext.forProxy( session, repository ); - - wagonAuth = getAuthenticationInfo( repoAuthContext ); - wagonProxy = getProxy( repository, proxyAuthContext ); - - this.headers = new Properties(); - this.headers.put( "User-Agent", ConfigUtils.getString( session, ConfigurationProperties.DEFAULT_USER_AGENT, - ConfigurationProperties.USER_AGENT ) ); - Map<?, ?> headers = ConfigUtils.getMap( session, null, - ConfigurationProperties.HTTP_HEADERS + "." + repository.getId(), - ConfigurationProperties.HTTP_HEADERS ); - if ( headers != null ) - { - this.headers.putAll( headers ); - } - } - - @PreDestroy - void destroy() - { - if ( closed.compareAndSet( false, true ) ) - { - AuthenticationContext.close( repoAuthContext ); - AuthenticationContext.close( proxyAuthContext ); - - for ( Wagon wagon = wagons.poll(); wagon != null; wagon = wagons.poll() ) - { - disconnectWagon( wagon ); - releaseWagon( wagon ); - } - } - } - - private static RepositoryPermissions getPermissions( String repoId, RepositorySystemSession session ) - { - RepositoryPermissions result = null; - RepositoryPermissions perms = new RepositoryPermissions(); - String suffix = '.' + repoId; - String fileMode = ConfigUtils.getString( session, null, CONFIG_PROP_FILE_MODE + suffix ); - if ( fileMode != null ) - { - perms.setFileMode( fileMode ); - result = perms; - } - String dirMode = ConfigUtils.getString( session, null, CONFIG_PROP_DIR_MODE + suffix ); - if ( dirMode != null ) - { - perms.setDirectoryMode( dirMode ); - result = perms; - } - String group = ConfigUtils.getString( session, null, CONFIG_PROP_GROUP + suffix ); - if ( group != null ) - { - perms.setGroup( group ); - result = perms; - } - return result; - } - - private AuthenticationInfo getAuthenticationInfo( final AuthenticationContext authContext ) - { - AuthenticationInfo auth = null; - - if ( authContext != null ) - { - auth = new AuthenticationInfo() - { - - @Override - public String getUserName() - { - return authContext.get( AuthenticationContext.USERNAME ); - } - - @Override - public String getPassword() - { - return authContext.get( AuthenticationContext.PASSWORD ); - } - - @Override - public String getPrivateKey() - { - return authContext.get( AuthenticationContext.PRIVATE_KEY_PATH ); - } - - @Override - public String getPassphrase() - { - return authContext.get( AuthenticationContext.PRIVATE_KEY_PASSPHRASE ); - } - }; - } - - return auth; - } - - private ProxyInfoProvider getProxy( RemoteRepository repository, final AuthenticationContext authContext ) - { - ProxyInfoProvider proxy = null; - - Proxy p = repository.getProxy(); - if ( p != null ) - { - final ProxyInfo prox; - if ( authContext != null ) - { - prox = new ProxyInfo() - { - - @Override - public String getUserName() - { - return authContext.get( AuthenticationContext.USERNAME ); - } - - @Override - public String getPassword() - { - return authContext.get( AuthenticationContext.PASSWORD ); - } - - @Override - public String getNtlmDomain() - { - return authContext.get( AuthenticationContext.NTLM_DOMAIN ); - } - - @Override - public String getNtlmHost() - { - return authContext.get( AuthenticationContext.NTLM_WORKSTATION ); - } - }; - } - else - { - prox = new ProxyInfo(); - } - prox.setType( p.getType() ); - prox.setHost( p.getHost() ); - prox.setPort( p.getPort() ); - - proxy = protocol -> prox; - } - - return proxy; - } - - @Override - @Nonnull - public Optional<Build> findBuild( CacheContext context ) throws IOException - { - final String resourceUrl = doGetResourceUrl( context, BUILDINFO_XML ); - return doGet( resourceUrl ) - .map( content -> new Build( xmlService.loadBuild( content ), CacheSource.REMOTE ) ); - } - - @Override - public boolean getArtifactContent( CacheContext context, Artifact artifact, Path target ) - throws IOException - { - return doGet( doGetResourceUrl( context, artifact.getFileName() ), target ); - } - - @Override - public void saveBuildInfo( CacheResult cacheResult, Build build ) - throws IOException - { - final String resourceUrl = doGetResourceUrl( cacheResult.getContext(), BUILDINFO_XML ); - doPut( resourceUrl, xmlService.toBytes( build.getDto() ) ); - } - - @Override - public void saveCacheReport( String buildId, MavenSession session, CacheReport cacheReport ) throws IOException - { - MavenProject rootProject = session.getTopLevelProject(); - final String resourceUrl = doGetResourceUrl( CACHE_REPORT_XML, rootProject, buildId ); - doPut( resourceUrl, xmlService.toBytes( cacheReport ) ); - } - - @Override - public void saveArtifactFile( CacheResult cacheResult, - org.apache.maven.artifact.Artifact artifact ) throws IOException - { - final String resourceUrl = doGetResourceUrl( cacheResult.getContext(), CacheUtils.normalizedName( artifact ) ); - doPut( resourceUrl, artifact.getFile().toPath() ); - } - - @Override - public String getResourceUrl( CacheContext context, String filename ) - { - String base = cacheConfig.getUrl(); - return base.endsWith( "/" ) - ? base + doGetResourceUrl( context, filename ) - : base + "/" + doGetResourceUrl( context, filename ); - } - - public String doGetResourceUrl( CacheContext context, String filename ) - { - return doGetResourceUrl( filename, context.getProject(), context.getInputInfo().getChecksum() ); - } - - private String doGetResourceUrl( String filename, MavenProject project, String checksum ) - { - return doGetResourceUrl( filename, project.getGroupId(), project.getArtifactId(), checksum ); - } - - private String doGetResourceUrl( String filename, String groupId, String artifactId, String checksum ) - { - return MavenProjectInput.CACHE_IMPLEMENTATION_VERSION + "/" + groupId + "/" - + artifactId + "/" + checksum + "/" + filename; - } - - @Override - public Optional<Build> findBaselineBuild( MavenProject project ) - { - Optional<List<ProjectReport>> cachedProjectsHolder = findCacheInfo() - .map( CacheReport::getProjects ); - - if ( !cachedProjectsHolder.isPresent() ) - { - return Optional.empty(); - } - - final List<ProjectReport> projects = cachedProjectsHolder.get(); - final Optional<ProjectReport> projectReportHolder = projects.stream() - .filter( p -> project.getArtifactId().equals( p.getArtifactId() ) - && project.getGroupId().equals( p.getGroupId() ) ) - .findFirst(); - - if ( !projectReportHolder.isPresent() ) - { - return Optional.empty(); - } - - final ProjectReport projectReport = projectReportHolder.get(); - - String url; - if ( projectReport.getUrl() != null ) - { - url = projectReport.getUrl(); - LOGGER.info( "Retrieving baseline buildinfo: {}", url ); - } - else - { - url = doGetResourceUrl( BUILDINFO_XML, project, projectReport.getChecksum() ); - LOGGER.info( "Baseline project record doesn't have url, trying default location {}", url ); - } - - try - { - return doGet( url ) - .map( content -> new Build( xmlService.loadBuild( content ), CacheSource.REMOTE ) ); - } - catch ( Exception e ) - { - LOGGER.warn( "Error restoring baseline build at url: {}, skipping diff", url, e ); - return Optional.empty(); - } - } - - private Optional<CacheReport> findCacheInfo() - { - Optional<CacheReport> report = cacheReportSupplier.get(); - if ( !report.isPresent() ) - { - try - { - LOGGER.info( "Downloading baseline cache report from: {}", cacheConfig.getBaselineCacheUrl() ); - return doGet( cacheConfig.getBaselineCacheUrl() ).map( xmlService::loadCacheReport ); - } - catch ( Exception e ) - { - LOGGER.error( "Error downloading baseline report from: {}, skipping diff.", - cacheConfig.getBaselineCacheUrl(), e ); - report = Optional.empty(); - } - cacheReportSupplier.compareAndSet( null, report ); - } - return report; - } - - private void doPut( String url, Path path ) throws IOException - { - try - { - Wagon wagon = pollWagon(); - try - { - wagon.put( path.toFile(), url ); - } - finally - { - wagons.add( wagon ); - } - } - catch ( Exception e ) - { - throw new IOException( "Unable to upload resource " + url, e ); - } - } - - private void doPut( String url, byte[] data ) throws IOException - { - try - { - Wagon wagon = pollWagon(); - try - { - if ( wagon instanceof StreamingWagon ) - { - ( ( StreamingWagon ) wagon ).putFromStream( - new ByteArrayInputStream( data ), url, data.length, 0 ); - } - else - { - File temp = createTempFile(); - try - { - Files.write( temp.toPath(), data ); - wagon.put( temp, url ); - } - finally - { - delete( temp ); - } - } - } - finally - { - wagons.add( wagon ); - } - } - catch ( Exception e ) - { - throw new IOException( "Unable to upload resource " + url, e ); - } - } - - private Optional<byte[]> doGet( String url ) throws IOException - { - try - { - Wagon wagon = pollWagon(); - try - { - if ( wagon instanceof StreamingWagon ) - { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ( ( StreamingWagon ) wagon ).getToStream( url, baos ); - return Optional.of( baos.toByteArray() ); - } - else - { - File temp = createTempFile(); - try - { - wagon.get( url, temp ); - return Optional.of( Files.readAllBytes( temp.toPath() ) ); - } - finally - { - delete( temp ); - } - } - } - finally - { - wagons.add( wagon ); - } - } - catch ( ResourceDoesNotExistException e ) - { - return Optional.empty(); - } - catch ( Exception e ) - { - throw new IOException( "Unable to download resource " + url, e ); - } - } - - private boolean doGet( String url, Path target ) throws IOException - { - try - { - Wagon wagon = pollWagon(); - try - { - wagon.get( url, target.toFile() ); - return true; - } - finally - { - wagons.add( wagon ); - } - } - catch ( ResourceDoesNotExistException e ) - { - return false; - } - catch ( Exception e ) - { - throw new IOException( "Unable to download resource " + url, e ); - } - } - - private File createTempFile() throws IOException - { - return File.createTempFile( "maven-build-cache-", ".temp" ); - } - - @SuppressWarnings( "ResultOfMethodCallIgnored" ) - private void delete( File temp ) - { - temp.delete(); - } - - private Wagon lookupWagon() throws ComponentLookupException - { - return wagonProvider.lookup( wagonHint ); - } - - private void releaseWagon( Wagon wagon ) - { - try - { - wagonProvider.release( wagon ); - } - catch ( ComponentLifecycleException e ) - { - // ignore - } - } - - private void connectWagon( Wagon wagon ) - throws WagonException - { - if ( !headers.isEmpty() ) - { - try - { - Method setHttpHeaders = wagon.getClass().getMethod( "setHttpHeaders", Properties.class ); - setHttpHeaders.invoke( wagon, headers ); - } - catch ( NoSuchMethodException e ) - { - // normal for non-http wagons - } - catch ( InvocationTargetException | IllegalAccessException | RuntimeException e ) - { - LOGGER.debug( "Could not set user agent for Wagon {}", wagon.getClass().getName(), e ); - } - } - - RepositorySystemSession session = mavenSession.getRepositorySession(); - int connectTimeout = ConfigUtils.getInteger( session, ConfigurationProperties.DEFAULT_CONNECT_TIMEOUT, - ConfigurationProperties.CONNECT_TIMEOUT ); - int requestTimeout = ConfigUtils.getInteger( session, ConfigurationProperties.DEFAULT_REQUEST_TIMEOUT, - ConfigurationProperties.REQUEST_TIMEOUT ); - - wagon.setTimeout( Math.max( Math.max( connectTimeout, requestTimeout ), 0 ) ); - - wagon.setInteractive( ConfigUtils.getBoolean( session, ConfigurationProperties.DEFAULT_INTERACTIVE, - ConfigurationProperties.INTERACTIVE ) ); - - Object configuration = ConfigUtils.getObject( session, null, - CONFIG_PROP_CONFIG + "." + repository.getId() ); - if ( configuration != null && wagonConfigurator != null ) - { - try - { - wagonConfigurator.configure( wagon, configuration ); - } - catch ( Exception e ) - { - LOGGER.warn( "Could not apply configuration for {} to Wagon {}", - repository.getId(), wagon.getClass().getName(), e ); - } - } - - wagon.connect( wagonRepo, wagonAuth, wagonProxy ); - } - - private void disconnectWagon( Wagon wagon ) - { - try - { - if ( wagon != null ) - { - wagon.disconnect(); - } - } - catch ( WagonException e ) - { - LOGGER.debug( "Could not disconnect Wagon {}", wagon, e ); - } - } - - private Wagon pollWagon() - throws Exception - { - Wagon wagon = wagons.poll(); - - if ( wagon == null ) - { - try - { - wagon = lookupWagon(); - connectWagon( wagon ); - } - catch ( Exception e ) - { - releaseWagon( wagon ); - throw e; - } - } - else if ( wagon.getRepository() == null ) - { - try - { - connectWagon( wagon ); - } - catch ( WagonException e ) - { - wagons.add( wagon ); - throw e; - } - } - - return wagon; - } - -} diff --git a/src/main/mdo/build-cache-config.mdo b/src/main/mdo/build-cache-config.mdo index 9465aa5..b7815e7 100644 --- a/src/main/mdo/build-cache-config.mdo +++ b/src/main/mdo/build-cache-config.mdo @@ -92,8 +92,8 @@ under the License. <field xml.attribute="true"> <name>transport</name> <type>String</type> - <defaultValue>http</defaultValue> - <description>The transport to use, either 'http' or 'wagon'.</description> + <defaultValue>resolver</defaultValue> + <description>The transport to use, currently, only 'resolver' is supported.</description> </field> <field xml.attribute="true"> <name>id</name> diff --git a/src/site/markdown/getting-started.md b/src/site/markdown/getting-started.md index f93212d..eb83536 100644 --- a/src/site/markdown/getting-started.md +++ b/src/site/markdown/getting-started.md @@ -66,6 +66,6 @@ checks could reduce both performance and cache efficiency (hit rate). ### Adding caching CI and remote cache To leverage remote cache feature there should a shared storage provide. Any technology supported -by [Maven Wagon](https://maven.apache.org/wagon/) will suffice. In simplest form it could be a http web server which +by [Maven Resolver](https://maven.apache.org/resolver/) will suffice. In simplest form it could be a http web server which supports get/put operations ([Nginx OSS](http://nginx.org/en/) with fs module or any other equivalent). See [Remote cache setup](remote-cache.md) for detailed description of cache setup. diff --git a/src/site/markdown/remote-cache.md b/src/site/markdown/remote-cache.md index fa35ff6..3cadd1f 100644 --- a/src/site/markdown/remote-cache.md +++ b/src/site/markdown/remote-cache.md @@ -69,9 +69,7 @@ in [Servers](https://maven.apache.org/settings.html#Servers). The server should TBD ``` -Beside the http server, remote cache could be configured using any storage which is supported -by [Maven Wagon](https://maven.apache.org/wagon/). That includes a wide set of options, including SSH, FTP and many -others. See Wagon documentation for a full list of options and other details. +Beside the http server, remote cache could be configured using any storage which is supported by [Maven Resolver](https://maven.apache.org/resolver/). That includes a wide set of options, including SSH, FTP and many others through the use of [Maven Wagon](https://maven.apache.org/wagon/). See Wagon documentation for a full list of options and other details. ### Build selection