This project has retired. For details please refer to its Attic page.
MavenRepositoryProxyHandler xref
View Javadoc
1   package org.apache.archiva.proxy.maven;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.archiva.model.RepositoryURL;
23  import org.apache.archiva.proxy.DefaultRepositoryProxyHandler;
24  import org.apache.archiva.proxy.NotFoundException;
25  import org.apache.archiva.proxy.NotModifiedException;
26  import org.apache.archiva.proxy.ProxyException;
27  import org.apache.archiva.proxy.model.NetworkProxy;
28  import org.apache.archiva.proxy.model.ProxyConnector;
29  import org.apache.archiva.proxy.model.RepositoryProxyHandler;
30  import org.apache.archiva.repository.*;
31  import org.apache.archiva.repository.base.PasswordCredentials;
32  import org.apache.archiva.repository.storage.StorageAsset;
33  import org.apache.commons.lang3.StringUtils;
34  import org.apache.maven.wagon.ConnectionException;
35  import org.apache.maven.wagon.ResourceDoesNotExistException;
36  import org.apache.maven.wagon.Wagon;
37  import org.apache.maven.wagon.WagonException;
38  import org.apache.maven.wagon.authentication.AuthenticationException;
39  import org.apache.maven.wagon.authentication.AuthenticationInfo;
40  import org.apache.maven.wagon.proxy.ProxyInfo;
41  import org.apache.maven.wagon.repository.Repository;
42  import org.springframework.stereotype.Service;
43  
44  import javax.inject.Inject;
45  import java.io.IOException;
46  import java.nio.file.Files;
47  import java.nio.file.Path;
48  import java.util.ArrayList;
49  import java.util.List;
50  import java.util.Map;
51  import java.util.concurrent.ConcurrentHashMap;
52  import java.util.concurrent.ConcurrentMap;
53  
54  /**
55   * DefaultRepositoryProxyHandler
56   * TODO exception handling needs work - "not modified" is not really an exceptional case, and it has more layers than
57   * your average brown onion
58   */
59  @Service( "repositoryProxyHandler#maven" )
60  public class MavenRepositoryProxyHandler extends DefaultRepositoryProxyHandler {
61  
62      private static final List<RepositoryType> REPOSITORY_TYPES = new ArrayList<>();
63  
64      static {
65          REPOSITORY_TYPES.add(RepositoryType.MAVEN);
66      }
67  
68      @Inject
69      private WagonFactory wagonFactory;
70  
71      private ConcurrentMap<String, ProxyInfo> networkProxyMap = new ConcurrentHashMap<>();
72  
73      @Override
74      public void initialize() {
75          super.initialize();
76      }
77  
78      private void updateWagonProxyInfo(Map<String, NetworkProxy> proxyList) {
79          this.networkProxyMap.clear();
80          for (Map.Entry<String, NetworkProxy> proxyEntry : proxyList.entrySet()) {
81              String key = proxyEntry.getKey();
82              NetworkProxy networkProxyDef = proxyEntry.getValue();
83  
84              ProxyInfo proxy = new ProxyInfo();
85  
86              proxy.setType(networkProxyDef.getProtocol());
87              proxy.setHost(networkProxyDef.getHost());
88              proxy.setPort(networkProxyDef.getPort());
89              proxy.setUserName(networkProxyDef.getUsername());
90              proxy.setPassword(new String(networkProxyDef.getPassword()));
91  
92              this.networkProxyMap.put(key, proxy);
93          }
94      }
95  
96      @Override
97      public void setNetworkProxies(Map<String, NetworkProxy> networkProxies ) {
98          super.setNetworkProxies( networkProxies );
99          updateWagonProxyInfo( networkProxies );
100     }
101 
102     /**
103      * @param connector
104      * @param remoteRepository
105      * @param tmpResource
106      * @param checksumFiles
107      * @param url
108      * @param remotePath
109      * @param resource
110      * @param workingDirectory
111      * @param repository
112      * @throws ProxyException
113      * @throws NotModifiedException
114      */
115     protected void transferResources( ProxyConnector connector, RemoteRepository remoteRepository,
116                                       StorageAsset/storage/StorageAsset.html#StorageAsset">StorageAsset../../org/apache/archiva/repository/storage/StorageAsset.html#StorageAsset">StorageAsset tmpResource, StorageAsset/storage/StorageAsset.html#StorageAsset">StorageAsset[] checksumFiles, String url, String remotePath, StorageAsset resource,
117                                       Path workingDirectory, ManagedRepository repository )
118             throws ProxyException, NotModifiedException {
119         Wagon wagon = null;
120         try {
121             RepositoryURL repoUrl = remoteRepository.getContent().getURL();
122             String protocol = repoUrl.getProtocol();
123             NetworkProxy networkProxy = null;
124             String proxyId = connector.getProxyId();
125             if (StringUtils.isNotBlank(proxyId)) {
126 
127                 networkProxy = getNetworkProxy(proxyId);
128             }
129             WagonFactoryRequesthtml#WagonFactoryRequest">WagonFactoryRequest wagonFactoryRequest = new WagonFactoryRequest("wagon#" + protocol,
130                     remoteRepository.getExtraHeaders());
131             if (networkProxy == null) {
132 
133                 log.warn("No network proxy with id {} found for connector {}->{}", proxyId,
134                         connector.getSourceRepository().getId(), connector.getTargetRepository().getId());
135             } else {
136                 wagonFactoryRequest = wagonFactoryRequest.networkProxy(networkProxy);
137             }
138             wagon = wagonFactory.getWagon(wagonFactoryRequest);
139             if (wagon == null) {
140                 throw new ProxyException("Unsupported target repository protocol: " + protocol);
141             }
142 
143             if (wagon == null) {
144                 throw new ProxyException("Unsupported target repository protocol: " + protocol);
145             }
146 
147             boolean connected = connectToRepository(connector, wagon, remoteRepository);
148             if (connected) {
149                 transferArtifact(wagon, remoteRepository, remotePath, repository, resource.getFilePath(), workingDirectory,
150                         tmpResource);
151 
152                 // TODO: these should be used to validate the download based on the policies, not always downloaded
153                 // to
154                 // save on connections since md5 is rarely used
155                 for (int i=0; i<checksumFiles.length; i++) {
156                     String ext = "."+StringUtils.substringAfterLast(checksumFiles[i].getName( ), "." );
157                     transferChecksum(wagon, remoteRepository, remotePath, repository, resource.getFilePath(), ext,
158                         checksumFiles[i].getFilePath());
159                 }
160             }
161         } catch (NotFoundException e) {
162             urlFailureCache.cacheFailure(url);
163             throw e;
164         } catch (NotModifiedException e) {
165             // Do not cache url here.
166             throw e;
167         } catch (ProxyException e) {
168             urlFailureCache.cacheFailure(url);
169             throw e;
170         } catch (WagonFactoryException e) {
171             throw new ProxyException(e.getMessage(), e);
172         } finally {
173             if (wagon != null) {
174                 try {
175                     wagon.disconnect();
176                 } catch (ConnectionException e) {
177                     log.warn("Unable to disconnect wagon.", e);
178                 }
179             }
180         }
181     }
182 
183     protected void transferArtifact(Wagon wagon, RemoteRepository remoteRepository, String remotePath,
184                                     ManagedRepository repository, Path resource, Path tmpDirectory,
185                                     StorageAsset destFile)
186             throws ProxyException {
187         transferSimpleFile(wagon, remoteRepository, remotePath, repository, resource, destFile.getFilePath());
188     }
189 
190     /**
191      * <p>
192      * Quietly transfer the checksum file from the remote repository to the local file.
193      * </p>
194      *
195      * @param wagon            the wagon instance (should already be connected) to use.
196      * @param remoteRepository the remote repository to transfer from.
197      * @param remotePath       the remote path to the resource to get.
198      * @param repository       the managed repository that will hold the file
199      * @param resource         the local file that should contain the downloaded contents
200      * @param ext              the type of checksum to transfer (example: ".md5" or ".sha1")
201      * @throws ProxyException if copying the downloaded file into place did not succeed.
202      */
203     protected void transferChecksum( Wagon wagon, RemoteRepository remoteRepository, String remotePath,
204                                      ManagedRepository repository, Path resource, String ext,
205                                      Path destFile )
206             throws ProxyException {
207         String url = remoteRepository.getLocation().toString() + remotePath + ext;
208 
209         // Transfer checksum does not use the policy.
210         if (urlFailureCache.hasFailedBefore(url)) {
211             return;
212         }
213 
214         try {
215             transferSimpleFile(wagon, remoteRepository, remotePath + ext, repository, resource, destFile);
216             log.debug("Checksum {} Downloaded: {} to move to {}", url, destFile, resource);
217         } catch (NotFoundException e) {
218             urlFailureCache.cacheFailure(url);
219             log.debug("Transfer failed, checksum not found: {}", url);
220             // Consume it, do not pass this on.
221         } catch (NotModifiedException e) {
222             log.debug("Transfer skipped, checksum not modified: {}", url);
223             // Consume it, do not pass this on.
224         } catch (ProxyException e) {
225             urlFailureCache.cacheFailure(url);
226             log.warn("Transfer failed on checksum: {} : {}", url, e.getMessage(), e);
227             // Critical issue, pass it on.
228             throw e;
229         }
230     }
231 
232     /**
233      * Perform the transfer of the remote file to the local file specified.
234      *
235      * @param wagon            the wagon instance to use.
236      * @param remoteRepository the remote repository to use
237      * @param remotePath       the remote path to attempt to get
238      * @param repository       the managed repository that will hold the file
239      * @param origFile         the local file to save to
240      * @throws ProxyException if there was a problem moving the downloaded file into place.
241      */
242     protected void transferSimpleFile(Wagon wagon, RemoteRepository remoteRepository, String remotePath,
243                                       ManagedRepository repository, Path origFile, Path destFile)
244             throws ProxyException {
245         assert (remotePath != null);
246 
247         // Transfer the file.
248         try {
249             boolean success = false;
250 
251             if (!Files.exists(origFile)) {
252                 log.debug("Retrieving {} from {}", remotePath, remoteRepository.getId());
253                 wagon.get(addParameters(remotePath, remoteRepository), destFile.toFile());
254                 success = true;
255 
256                 // You wouldn't get here on failure, a WagonException would have been thrown.
257                 log.debug("Downloaded successfully.");
258             } else {
259                 log.debug("Retrieving {} from {} if updated", remotePath, remoteRepository.getId());
260                 try {
261                     success = wagon.getIfNewer(addParameters(remotePath, remoteRepository), destFile.toFile(),
262                             Files.getLastModifiedTime(origFile).toMillis());
263                 } catch (IOException e) {
264                     throw new ProxyException("Failed to the modification time of " + origFile.toAbsolutePath());
265                 }
266                 if (!success) {
267                     throw new NotModifiedException(
268                             "Not downloaded, as local file is newer than remote side: " + origFile.toAbsolutePath());
269                 }
270 
271                 if (Files.exists(destFile)) {
272                     log.debug("Downloaded successfully.");
273                 }
274             }
275         } catch (ResourceDoesNotExistException e) {
276             throw new NotFoundException(
277                     "Resource [" + remoteRepository.getLocation() + "/" + remotePath + "] does not exist: " + e.getMessage(),
278                     e);
279         } catch (WagonException e) {
280             // TODO: shouldn't have to drill into the cause, but TransferFailedException is often not descriptive enough
281 
282             String msg =
283                     "Download failure on resource [" + remoteRepository.getLocation() + "/" + remotePath + "]:" + e.getMessage();
284             if (e.getCause() != null) {
285                 msg += " (cause: " + e.getCause() + ")";
286             }
287             throw new ProxyException(msg, e);
288         }
289     }
290 
291     /**
292      * Using wagon, connect to the remote repository.
293      *
294      * @param connector        the connector configuration to utilize (for obtaining network proxy configuration from)
295      * @param wagon            the wagon instance to establish the connection on.
296      * @param remoteRepository the remote repository to connect to.
297      * @return true if the connection was successful. false if not connected.
298      */
299     protected boolean connectToRepository(ProxyConnector connector, Wagon wagon,
300                                           RemoteRepository remoteRepository) {
301         boolean connected = false;
302 
303         final ProxyInfo networkProxy =
304                 connector.getProxyId() == null ? null : this.networkProxyMap.get(connector.getProxyId());
305 
306         if (log.isDebugEnabled()) {
307             if (networkProxy != null) {
308                 // TODO: move to proxyInfo.toString()
309                 String msg = "Using network proxy " + networkProxy.getHost() + ":" + networkProxy.getPort()
310                         + " to connect to remote repository " + remoteRepository.getLocation();
311                 if (networkProxy.getNonProxyHosts() != null) {
312                     msg += "; excluding hosts: " + networkProxy.getNonProxyHosts();
313                 }
314                 if (StringUtils.isNotBlank(networkProxy.getUserName())) {
315                     msg += "; as user: " + networkProxy.getUserName();
316                 }
317                 log.debug(msg);
318             }
319         }
320 
321         AuthenticationInfo authInfo = null;
322         String username = "";
323         String password = "";
324         RepositoryCredentials repCred = remoteRepository.getLoginCredentials();
325         if (repCred != null && repCred instanceof PasswordCredentials ) {
326             PasswordCredentials/org/apache/archiva/repository/base/PasswordCredentials.html#PasswordCredentials">PasswordCredentials pwdCred = (PasswordCredentials) repCred;
327             username = pwdCred.getUsername();
328             password = pwdCred.getPassword() == null ? "" : new String(pwdCred.getPassword());
329         }
330 
331         if (StringUtils.isNotBlank(username) && StringUtils.isNotBlank(password)) {
332             log.debug("Using username {} to connect to remote repository {}", username, remoteRepository.getLocation());
333             authInfo = new AuthenticationInfo();
334             authInfo.setUserName(username);
335             authInfo.setPassword(password);
336         }
337 
338         // Convert seconds to milliseconds
339 
340         long timeoutInMilliseconds = remoteRepository.getTimeout().toMillis();
341 
342         // Set timeout  read and connect
343         // FIXME olamy having 2 config values
344         wagon.setReadTimeout((int) timeoutInMilliseconds);
345         wagon.setTimeout((int) timeoutInMilliseconds);
346 
347         try {
348             Repository wagonRepository =
349                     new Repository(remoteRepository.getId(), remoteRepository.getLocation().toString());
350             wagon.connect(wagonRepository, authInfo, networkProxy);
351             connected = true;
352         } catch (ConnectionException | AuthenticationException e) {
353             log.warn("Could not connect to {}: {}", remoteRepository.getId(), e.getMessage());
354             connected = false;
355         }
356 
357         return connected;
358     }
359 
360 
361     public WagonFactory getWagonFactory() {
362         return wagonFactory;
363     }
364 
365     public void setWagonFactory(WagonFactory wagonFactory) {
366         this.wagonFactory = wagonFactory;
367     }
368 
369     @Override
370     public List<RepositoryType> supports() {
371         return REPOSITORY_TYPES;
372     }
373 
374     @Override
375     public void addNetworkproxy( String id, NetworkProxy networkProxy )
376     {
377 
378     }
379 
380     @Override
381     public <T extends RepositoryProxyHandler> T getHandler( Class<T> clazz ) throws IllegalArgumentException
382     {
383         if (clazz.isAssignableFrom( this.getClass() )) {
384             return (T)this;
385         } else {
386             throw new IllegalArgumentException( "This Proxy Handler is no subclass of " + clazz );
387         }
388     }
389 }