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