1 package org.apache.archiva.scheduler.indexing;
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
21 import org.apache.archiva.admin.model.beans.NetworkProxy;
22 import org.apache.archiva.admin.model.remote.RemoteRepositoryAdmin;
23 import org.apache.archiva.proxy.common.WagonFactory;
24 import org.apache.archiva.proxy.common.WagonFactoryRequest;
25 import org.apache.archiva.repository.PasswordCredentials;
26 import org.apache.archiva.repository.RemoteRepository;
27 import org.apache.archiva.repository.RepositoryException;
28 import org.apache.archiva.repository.RepositoryType;
29 import org.apache.archiva.repository.features.RemoteIndexFeature;
30 import org.apache.commons.lang.time.StopWatch;
31 import org.apache.http.auth.UsernamePasswordCredentials;
32 import org.apache.maven.index.context.IndexingContext;
33 import org.apache.maven.index.updater.IndexUpdateRequest;
34 import org.apache.maven.index.updater.IndexUpdateResult;
35 import org.apache.maven.index.updater.IndexUpdater;
36 import org.apache.maven.index.updater.ResourceFetcher;
37 import org.apache.maven.index_shaded.lucene.index.IndexNotFoundException;
38 import org.apache.maven.wagon.ResourceDoesNotExistException;
39 import org.apache.maven.wagon.StreamWagon;
40 import org.apache.maven.wagon.TransferFailedException;
41 import org.apache.maven.wagon.Wagon;
42 import org.apache.maven.wagon.authentication.AuthenticationInfo;
43 import org.apache.maven.wagon.authorization.AuthorizationException;
44 import org.apache.maven.wagon.events.TransferEvent;
45 import org.apache.maven.wagon.events.TransferListener;
46 import org.apache.maven.wagon.proxy.ProxyInfo;
47 import org.apache.maven.wagon.repository.Repository;
48 import org.apache.maven.wagon.shared.http.AbstractHttpClientWagon;
49 import org.apache.maven.wagon.shared.http.HttpConfiguration;
50 import org.apache.maven.wagon.shared.http.HttpMethodConfiguration;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
54 import java.io.FileNotFoundException;
55 import java.io.IOException;
56 import java.io.InputStream;
58 import java.nio.file.Files;
59 import java.nio.file.Path;
60 import java.nio.file.Paths;
61 import java.util.List;
65 * @author Olivier Lamy
68 public class DownloadRemoteIndexTask
71 private Logger log = LoggerFactory.getLogger( getClass() );
73 private RemoteRepository remoteRepository;
75 private WagonFactory wagonFactory;
77 private NetworkProxy networkProxy;
79 private boolean fullDownload;
81 private List<String> runningRemoteDownloadIds;
83 private IndexUpdater indexUpdater;
86 public DownloadRemoteIndexTask( DownloadRemoteIndexTaskRequest downloadRemoteIndexTaskRequest,
87 List<String> runningRemoteDownloadIds )
89 this.remoteRepository = downloadRemoteIndexTaskRequest.getRemoteRepository();
90 this.wagonFactory = downloadRemoteIndexTaskRequest.getWagonFactory();
91 this.networkProxy = downloadRemoteIndexTaskRequest.getNetworkProxy();
92 this.fullDownload = downloadRemoteIndexTaskRequest.isFullDownload();
93 this.runningRemoteDownloadIds = runningRemoteDownloadIds;
94 this.indexUpdater = downloadRemoteIndexTaskRequest.getIndexUpdater();
101 // so short lock : not sure we need it
102 synchronized ( this.runningRemoteDownloadIds )
104 if ( this.runningRemoteDownloadIds.contains( this.remoteRepository.getId() ) )
106 // skip it as it's running
107 log.info( "skip download index remote for repo {} it's already running",
108 this.remoteRepository.getId() );
111 this.runningRemoteDownloadIds.add( this.remoteRepository.getId() );
113 Path tempIndexDirectory = null;
114 StopWatch stopWatch = new StopWatch();
118 log.info( "start download remote index for remote repository {}", this.remoteRepository.getId() );
119 if (this.remoteRepository.getIndexingContext()==null) {
120 throw new IndexNotFoundException("No index context set for repository "+remoteRepository.getId());
122 if (this.remoteRepository.getType()!= RepositoryType.MAVEN) {
123 throw new RepositoryException("Bad repository type");
125 if (!this.remoteRepository.supportsFeature(RemoteIndexFeature.class)) {
126 throw new RepositoryException("Repository does not support RemotIndexFeature "+remoteRepository.getId());
128 RemoteIndexFeature rif = this.remoteRepository.getFeature(RemoteIndexFeature.class).get();
129 IndexingContext indexingContext = this.remoteRepository.getIndexingContext().getBaseContext(IndexingContext.class);
130 // create a temp directory to download files
131 tempIndexDirectory = Paths.get(indexingContext.getIndexDirectoryFile().getParent(), ".tmpIndex" );
132 Path indexCacheDirectory = Paths.get( indexingContext.getIndexDirectoryFile().getParent(), ".indexCache" );
133 Files.createDirectories( indexCacheDirectory );
134 if ( Files.exists(tempIndexDirectory) )
136 org.apache.archiva.common.utils.FileUtils.deleteDirectory( tempIndexDirectory );
138 Files.createDirectories( tempIndexDirectory );
139 tempIndexDirectory.toFile().deleteOnExit();
140 String baseIndexUrl = indexingContext.getIndexUpdateUrl();
142 String wagonProtocol = this.remoteRepository.getLocation().getScheme();
144 final StreamWagon wagon = (StreamWagon) wagonFactory.getWagon(
145 new WagonFactoryRequest( wagonProtocol, this.remoteRepository.getExtraHeaders() ).networkProxy(
148 // FIXME olamy having 2 config values
149 wagon.setReadTimeout( (int)rif.getDownloadTimeout().toMillis());
150 wagon.setTimeout( (int)remoteRepository.getTimeout().toMillis());
152 if ( wagon instanceof AbstractHttpClientWagon )
154 HttpConfiguration httpConfiguration = new HttpConfiguration();
155 HttpMethodConfiguration httpMethodConfiguration = new HttpMethodConfiguration();
156 httpMethodConfiguration.setUsePreemptive( true );
157 httpMethodConfiguration.setReadTimeout( (int)rif.getDownloadTimeout().toMillis() );
158 httpConfiguration.setGet( httpMethodConfiguration );
159 AbstractHttpClientWagon.class.cast( wagon ).setHttpConfiguration( httpConfiguration );
162 wagon.addTransferListener( new DownloadListener() );
163 ProxyInfo proxyInfo = null;
164 if ( this.networkProxy != null )
166 proxyInfo = new ProxyInfo();
167 proxyInfo.setType( this.networkProxy.getProtocol() );
168 proxyInfo.setHost( this.networkProxy.getHost() );
169 proxyInfo.setPort( this.networkProxy.getPort() );
170 proxyInfo.setUserName( this.networkProxy.getUsername() );
171 proxyInfo.setPassword( this.networkProxy.getPassword() );
173 AuthenticationInfo authenticationInfo = null;
174 if ( this.remoteRepository.getLoginCredentials()!=null && this.remoteRepository.getLoginCredentials() instanceof PasswordCredentials )
176 PasswordCredentials creds = (PasswordCredentials) this.remoteRepository.getLoginCredentials();
177 authenticationInfo = new AuthenticationInfo();
178 authenticationInfo.setUserName( creds.getUsername());
179 authenticationInfo.setPassword( new String(creds.getPassword()) );
181 log.debug("Connection to {}, authInfo={}", this.remoteRepository.getId(), authenticationInfo);
182 wagon.connect( new Repository( this.remoteRepository.getId(), baseIndexUrl ), authenticationInfo,
185 Path indexDirectory = indexingContext.getIndexDirectoryFile().toPath();
186 if ( !Files.exists(indexDirectory) )
188 Files.createDirectories( indexDirectory );
190 log.debug("Downloading index file to {}", indexDirectory);
191 log.debug("Index cache dir {}", indexCacheDirectory);
193 ResourceFetcher resourceFetcher =
194 new WagonResourceFetcher( log, tempIndexDirectory, wagon, remoteRepository );
195 IndexUpdateRequest request = new IndexUpdateRequest( indexingContext, resourceFetcher );
196 request.setForceFullUpdate( this.fullDownload );
197 request.setLocalIndexCacheDir( indexCacheDirectory.toFile() );
199 IndexUpdateResult result = this.indexUpdater.fetchAndUpdateIndex(request);
200 log.debug("Update result success: {}", result.isSuccessful());
202 log.info( "time update index from remote for repository {}: {}ms", this.remoteRepository.getId(),
203 ( stopWatch.getTime() ) );
205 // index packing optionnal ??
206 //IndexPackingRequest indexPackingRequest =
207 // new IndexPackingRequest( indexingContext, indexingContext.getIndexDirectoryFile() );
208 //indexPacker.packIndex( indexPackingRequest );
209 indexingContext.updateTimestamp( true );
212 catch ( Exception e )
214 log.error( e.getMessage(), e );
215 throw new RuntimeException( e.getMessage(), e );
219 deleteDirectoryQuiet( tempIndexDirectory );
220 this.runningRemoteDownloadIds.remove( this.remoteRepository.getId() );
222 log.info( "end download remote index for remote repository {}", this.remoteRepository.getId() );
225 private void deleteDirectoryQuiet( Path f )
229 org.apache.archiva.common.utils.FileUtils.deleteDirectory( f );
231 catch ( IOException e )
233 log.warn( "skip error delete {} : {}", f, e.getMessage() );
238 private static final class DownloadListener
239 implements TransferListener
241 private Logger log = LoggerFactory.getLogger( getClass() );
243 private String resourceName;
245 private long startTime;
247 private int totalLength = 0;
250 public void transferInitiated( TransferEvent transferEvent )
252 startTime = System.currentTimeMillis();
253 resourceName = transferEvent.getResource().getName();
254 log.debug( "initiate transfer of {}", resourceName );
258 public void transferStarted( TransferEvent transferEvent )
260 this.totalLength = 0;
261 resourceName = transferEvent.getResource().getName();
262 log.info("Transferring: {}, {}", transferEvent.getResource().getContentLength(), transferEvent.getLocalFile().toString());
263 log.info( "start transfer of {}", transferEvent.getResource().getName() );
267 public void transferProgress( TransferEvent transferEvent, byte[] buffer, int length )
269 log.debug( "transfer of {} : {}/{}", transferEvent.getResource().getName(), buffer.length, length );
270 this.totalLength += length;
274 public void transferCompleted( TransferEvent transferEvent )
276 resourceName = transferEvent.getResource().getName();
277 long endTime = System.currentTimeMillis();
278 log.info( "end of transfer file {}: {}b, {}ms", transferEvent.getResource().getName(),
279 this.totalLength, ( endTime - startTime ) );
283 public void transferError( TransferEvent transferEvent )
285 log.info( "error of transfer file {}: {}", transferEvent.getResource().getName(),
286 transferEvent.getException().getMessage(), transferEvent.getException() );
290 public void debug( String message )
292 log.debug( "transfer debug {}", message );
296 private static class WagonResourceFetcher
297 implements ResourceFetcher
302 Path tempIndexDirectory;
306 RemoteRepository remoteRepository;
308 private WagonResourceFetcher( Logger log, Path tempIndexDirectory, Wagon wagon,
309 RemoteRepository remoteRepository )
312 this.tempIndexDirectory = tempIndexDirectory;
314 this.remoteRepository = remoteRepository;
318 public void connect( String id, String url )
325 public void disconnect()
332 public InputStream retrieve( String name )
333 throws IOException, FileNotFoundException
337 log.info( "index update retrieve file, name:{}", name );
338 Path file = tempIndexDirectory.resolve( name );
339 Files.deleteIfExists( file );
340 file.toFile().deleteOnExit();
341 wagon.get( addParameters( name, this.remoteRepository ), file.toFile() );
342 return Files.newInputStream( file );
344 catch ( AuthorizationException | TransferFailedException e )
346 throw new IOException( e.getMessage(), e );
348 catch ( ResourceDoesNotExistException e )
350 FileNotFoundException fnfe = new FileNotFoundException( e.getMessage() );
356 // FIXME remove crappy copy/paste
357 protected String addParameters( String path, RemoteRepository remoteRepository )
359 if ( remoteRepository.getExtraParameters().isEmpty() )
364 boolean question = false;
366 StringBuilder res = new StringBuilder( path == null ? "" : path );
368 for ( Map.Entry<String, String> entry : remoteRepository.getExtraParameters().entrySet() )
372 res.append( '?' ).append( entry.getKey() ).append( '=' ).append( entry.getValue() );
376 return res.toString();