View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.repository.legacy;
20  
21  import javax.inject.Inject;
22  import javax.inject.Named;
23  import javax.inject.Singleton;
24  
25  import java.io.File;
26  import java.util.Date;
27  import java.util.HashMap;
28  import java.util.Properties;
29  
30  import org.apache.maven.artifact.Artifact;
31  import org.apache.maven.artifact.repository.ArtifactRepository;
32  import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy;
33  import org.apache.maven.artifact.repository.Authentication;
34  import org.apache.maven.artifact.repository.metadata.RepositoryMetadata;
35  import org.apache.maven.repository.Proxy;
36  import org.codehaus.plexus.logging.AbstractLogEnabled;
37  import org.codehaus.plexus.logging.Logger;
38  import org.eclipse.aether.internal.impl.TrackingFileManager;
39  
40  /**
41   * DefaultUpdateCheckManager
42   */
43  @Named
44  @Singleton
45  @Deprecated
46  public class DefaultUpdateCheckManager extends AbstractLogEnabled implements UpdateCheckManager {
47      private final TrackingFileManager trackingFileManager;
48  
49      private static final String ERROR_KEY_SUFFIX = ".error";
50  
51      @Inject
52      public DefaultUpdateCheckManager(TrackingFileManager trackingFileManager) {
53          this.trackingFileManager = trackingFileManager;
54      }
55  
56      /**
57       * For testing purposes.
58       */
59      public DefaultUpdateCheckManager(Logger logger, TrackingFileManager trackingFileManager) {
60          enableLogging(logger);
61          this.trackingFileManager = trackingFileManager;
62      }
63  
64      public static final String LAST_UPDATE_TAG = ".lastUpdated";
65  
66      private static final String TOUCHFILE_NAME = "resolver-status.properties";
67  
68      @Override
69      public boolean isUpdateRequired(Artifact artifact, ArtifactRepository repository) {
70          File file = artifact.getFile();
71  
72          ArtifactRepositoryPolicy policy = artifact.isSnapshot() ? repository.getSnapshots() : repository.getReleases();
73  
74          if (!policy.isEnabled()) {
75              if (getLogger().isDebugEnabled()) {
76                  getLogger()
77                          .debug("Skipping update check for " + artifact + " (" + file + ") from " + repository.getId()
78                                  + " (" + repository.getUrl() + ")");
79              }
80  
81              return false;
82          }
83  
84          if (getLogger().isDebugEnabled()) {
85              getLogger()
86                      .debug("Determining update check for " + artifact + " (" + file + ") from " + repository.getId()
87                              + " (" + repository.getUrl() + ")");
88          }
89  
90          if (file == null) {
91              // TODO throw something instead?
92              return true;
93          }
94  
95          Date lastCheckDate;
96  
97          if (file.exists()) {
98              lastCheckDate = new Date(file.lastModified());
99          } else {
100             File touchfile = getTouchfile(artifact);
101             lastCheckDate = readLastUpdated(touchfile, getRepositoryKey(repository));
102         }
103 
104         return (lastCheckDate == null) || policy.checkOutOfDate(lastCheckDate);
105     }
106 
107     @Override
108     public boolean isUpdateRequired(RepositoryMetadata metadata, ArtifactRepository repository, File file) {
109         // Here, we need to determine which policy to use. Release updateInterval will be used when
110         // the metadata refers to a release artifact or meta-version, and snapshot updateInterval will be used when
111         // it refers to a snapshot artifact or meta-version.
112         // NOTE: Release metadata includes version information about artifacts that have been released, to allow
113         // meta-versions like RELEASE and LATEST to resolve, and also to allow retrieval of the range of valid, released
114         // artifacts available.
115         ArtifactRepositoryPolicy policy = metadata.getPolicy(repository);
116 
117         if (!policy.isEnabled()) {
118             if (getLogger().isDebugEnabled()) {
119                 getLogger()
120                         .debug("Skipping update check for " + metadata.getKey() + " (" + file + ") from "
121                                 + repository.getId() + " (" + repository.getUrl() + ")");
122             }
123 
124             return false;
125         }
126 
127         if (getLogger().isDebugEnabled()) {
128             getLogger()
129                     .debug("Determining update check for " + metadata.getKey() + " (" + file + ") from "
130                             + repository.getId() + " (" + repository.getUrl() + ")");
131         }
132 
133         if (file == null) {
134             // TODO throw something instead?
135             return true;
136         }
137 
138         Date lastCheckDate = readLastUpdated(metadata, repository, file);
139 
140         return (lastCheckDate == null) || policy.checkOutOfDate(lastCheckDate);
141     }
142 
143     private Date readLastUpdated(RepositoryMetadata metadata, ArtifactRepository repository, File file) {
144         File touchfile = getTouchfile(metadata, file);
145 
146         String key = getMetadataKey(repository, file);
147 
148         return readLastUpdated(touchfile, key);
149     }
150 
151     @Override
152     public String getError(Artifact artifact, ArtifactRepository repository) {
153         File touchFile = getTouchfile(artifact);
154         return getError(touchFile, getRepositoryKey(repository));
155     }
156 
157     @Override
158     public void touch(Artifact artifact, ArtifactRepository repository, String error) {
159         File file = artifact.getFile();
160 
161         File touchfile = getTouchfile(artifact);
162 
163         if (file.exists()) {
164             trackingFileManager.delete(touchfile);
165         } else {
166             writeLastUpdated(touchfile, getRepositoryKey(repository), error);
167         }
168     }
169 
170     @Override
171     public void touch(RepositoryMetadata metadata, ArtifactRepository repository, File file) {
172         File touchfile = getTouchfile(metadata, file);
173 
174         String key = getMetadataKey(repository, file);
175 
176         writeLastUpdated(touchfile, key, null);
177     }
178 
179     String getMetadataKey(ArtifactRepository repository, File file) {
180         return repository.getId() + '.' + file.getName() + LAST_UPDATE_TAG;
181     }
182 
183     String getRepositoryKey(ArtifactRepository repository) {
184         StringBuilder buffer = new StringBuilder(256);
185 
186         Proxy proxy = repository.getProxy();
187         if (proxy != null) {
188             if (proxy.getUserName() != null) {
189                 int hash = (proxy.getUserName() + proxy.getPassword()).hashCode();
190                 buffer.append(hash).append('@');
191             }
192             buffer.append(proxy.getHost()).append(':').append(proxy.getPort()).append('>');
193         }
194 
195         // consider the username&password because a repo manager might block artifacts depending on authorization
196         Authentication auth = repository.getAuthentication();
197         if (auth != null) {
198             int hash = (auth.getUsername() + auth.getPassword()).hashCode();
199             buffer.append(hash).append('@');
200         }
201 
202         // consider the URL (instead of the id) as this most closely relates to the contents in the repo
203         buffer.append(repository.getUrl());
204 
205         return buffer.toString();
206     }
207 
208     private void writeLastUpdated(File touchfile, String key, String error) {
209         HashMap<String, String> update = new HashMap<>();
210         update.put(key, Long.toString(System.currentTimeMillis()));
211         update.put(key + ERROR_KEY_SUFFIX, error); // error==null => remove mapping
212         trackingFileManager.update(touchfile, update);
213     }
214 
215     Date readLastUpdated(File touchfile, String key) {
216         getLogger().debug("Searching for " + key + " in resolution tracking file.");
217 
218         Properties props = read(touchfile);
219         if (props != null) {
220             String rawVal = props.getProperty(key);
221             if (rawVal != null) {
222                 try {
223                     return new Date(Long.parseLong(rawVal));
224                 } catch (NumberFormatException e) {
225                     getLogger().debug("Cannot parse lastUpdated date: '" + rawVal + "'. Ignoring.", e);
226                 }
227             }
228         }
229         return null;
230     }
231 
232     private String getError(File touchFile, String key) {
233         Properties props = read(touchFile);
234         if (props != null) {
235             return props.getProperty(key + ERROR_KEY_SUFFIX);
236         }
237         return null;
238     }
239 
240     private Properties read(File touchfile) {
241         return trackingFileManager.read(touchfile);
242     }
243 
244     File getTouchfile(Artifact artifact) {
245         StringBuilder sb = new StringBuilder(128);
246         sb.append(artifact.getArtifactId());
247         sb.append('-').append(artifact.getBaseVersion());
248         if (artifact.getClassifier() != null) {
249             sb.append('-').append(artifact.getClassifier());
250         }
251         sb.append('.').append(artifact.getType()).append(LAST_UPDATE_TAG);
252         return new File(artifact.getFile().getParentFile(), sb.toString());
253     }
254 
255     File getTouchfile(RepositoryMetadata metadata, File file) {
256         return new File(file.getParent(), TOUCHFILE_NAME);
257     }
258 }