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.artifact.repository.metadata;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.io.Reader;
24  import java.io.Writer;
25  
26  import org.apache.maven.artifact.Artifact;
27  import org.apache.maven.artifact.metadata.ArtifactMetadata;
28  import org.apache.maven.artifact.repository.ArtifactRepository;
29  import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy;
30  import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Writer;
31  import org.apache.maven.repository.internal.metadata.ValidatingMetadataXpp3Reader;
32  import org.codehaus.plexus.util.ReaderFactory;
33  import org.codehaus.plexus.util.WriterFactory;
34  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
35  
36  /**
37   * Shared methods of the repository metadata handling.
38   *
39   * @author <a href="mailto:brett@apache.org">Brett Porter</a>
40   */
41  public abstract class AbstractRepositoryMetadata implements RepositoryMetadata {
42      private Metadata metadata;
43  
44      protected AbstractRepositoryMetadata(Metadata metadata) {
45          this.metadata = metadata;
46      }
47  
48      public String getRemoteFilename() {
49          return "maven-metadata.xml";
50      }
51  
52      public String getLocalFilename(ArtifactRepository repository) {
53          return "maven-metadata-" + validateRepositoryKey(repository.getKey()) + ".xml";
54      }
55  
56      /**
57       * The repository key (its id) is used verbatim as part of a local file name, so it must lie within
58       * the usual coordinate character set.
59       */
60      private static String validateRepositoryKey(String key) {
61          if (key == null || key.isEmpty()) {
62              return key;
63          }
64          if (isInvalidPathToken(key)) {
65              throw new IllegalArgumentException("Invalid repository key '" + key + "'");
66          }
67          return key;
68      }
69  
70      private static boolean isInvalidPathToken(String value) {
71          if ("..".equals(value) || value.indexOf('/') >= 0 || value.indexOf('\\') >= 0 || value.indexOf(':') >= 0) {
72              return true;
73          }
74          for (int i = 0; i < value.length(); i++) {
75              if (Character.isISOControl(value.charAt(i))) {
76                  return true;
77              }
78          }
79          return false;
80      }
81  
82      public void storeInLocalRepository(ArtifactRepository localRepository, ArtifactRepository remoteRepository)
83              throws RepositoryMetadataStoreException {
84          try {
85              updateRepositoryMetadata(localRepository, remoteRepository);
86          } catch (IOException | XmlPullParserException e) {
87              throw new RepositoryMetadataStoreException("Error updating group repository metadata", e);
88          }
89      }
90  
91      protected void updateRepositoryMetadata(ArtifactRepository localRepository, ArtifactRepository remoteRepository)
92              throws IOException, XmlPullParserException {
93          ValidatingMetadataXpp3Reader mappingReader = new ValidatingMetadataXpp3Reader();
94  
95          Metadata metadata = null;
96  
97          File metadataFile = new File(
98                  localRepository.getBasedir(), localRepository.pathOfLocalRepositoryMetadata(this, remoteRepository));
99  
100         if (metadataFile.length() == 0) {
101             if (!metadataFile.delete()) {
102                 // sleep for 10ms just in case this is windows holding a file lock
103                 try {
104                     Thread.sleep(10);
105                 } catch (InterruptedException e) {
106                     // ignore
107                 }
108                 metadataFile.delete(); // if this fails, forget about it, we'll try to overwrite it anyway so no need
109                 // to delete on exit
110             }
111         } else if (metadataFile.exists()) {
112             try (Reader reader = ReaderFactory.newXmlReader(metadataFile)) {
113                 metadata = mappingReader.read(reader, false);
114             }
115         }
116 
117         boolean changed;
118 
119         // If file could not be found or was not valid, start from scratch
120         if (metadata == null) {
121             metadata = this.metadata;
122 
123             changed = true;
124         } else {
125             changed = metadata.merge(this.metadata);
126         }
127 
128         // beware meta-versions!
129         String version = metadata.getVersion();
130         if (version != null && (Artifact.LATEST_VERSION.equals(version) || Artifact.RELEASE_VERSION.equals(version))) {
131             // meta-versions are not valid <version/> values...don't write them.
132             metadata.setVersion(null);
133         }
134 
135         if (changed || !metadataFile.exists()) {
136             metadataFile.getParentFile().mkdirs();
137             try (Writer writer = WriterFactory.newXmlWriter(metadataFile)) {
138                 MetadataXpp3Writer mappingWriter = new MetadataXpp3Writer();
139 
140                 mappingWriter.write(writer, metadata);
141             }
142         } else {
143             metadataFile.setLastModified(System.currentTimeMillis());
144         }
145     }
146 
147     public String toString() {
148         return "repository metadata for: \'" + getKey() + "\'";
149     }
150 
151     protected static Metadata createMetadata(Artifact artifact, Versioning versioning) {
152         Metadata metadata = new Metadata();
153         metadata.setGroupId(artifact.getGroupId());
154         metadata.setArtifactId(artifact.getArtifactId());
155         metadata.setVersion(artifact.getVersion());
156         metadata.setVersioning(versioning);
157         return metadata;
158     }
159 
160     protected static Versioning createVersioning(Snapshot snapshot) {
161         Versioning versioning = new Versioning();
162         versioning.setSnapshot(snapshot);
163         return versioning;
164     }
165 
166     public void setMetadata(Metadata metadata) {
167         this.metadata = metadata;
168     }
169 
170     public Metadata getMetadata() {
171         return metadata;
172     }
173 
174     public void merge(org.apache.maven.repository.legacy.metadata.ArtifactMetadata metadata) {
175         // TODO not sure that it should assume this, maybe the calls to addMetadata should pre-merge, then artifact
176         // replaces?
177         AbstractRepositoryMetadata repoMetadata = (AbstractRepositoryMetadata) metadata;
178         this.metadata.merge(repoMetadata.getMetadata());
179     }
180 
181     public void merge(ArtifactMetadata metadata) {
182         // TODO not sure that it should assume this, maybe the calls to addMetadata should pre-merge, then artifact
183         // replaces?
184         AbstractRepositoryMetadata repoMetadata = (AbstractRepositoryMetadata) metadata;
185         this.metadata.merge(repoMetadata.getMetadata());
186     }
187 
188     public String extendedToString() {
189         StringBuilder buffer = new StringBuilder(256);
190 
191         buffer.append("\nRepository Metadata\n--------------------------");
192         buffer.append("\nGroupId: ").append(getGroupId());
193         buffer.append("\nArtifactId: ").append(getArtifactId());
194         buffer.append("\nMetadata Type: ").append(getClass().getName());
195 
196         return buffer.toString();
197     }
198 
199     public int getNature() {
200         return RELEASE;
201     }
202 
203     public ArtifactRepositoryPolicy getPolicy(ArtifactRepository repository) {
204         int nature = getNature();
205         if ((nature & RepositoryMetadata.RELEASE_OR_SNAPSHOT) == RepositoryMetadata.RELEASE_OR_SNAPSHOT) {
206             ArtifactRepositoryPolicy policy = new ArtifactRepositoryPolicy(repository.getReleases());
207             policy.merge(repository.getSnapshots());
208             return policy;
209         } else if ((nature & RepositoryMetadata.SNAPSHOT) != 0) {
210             return repository.getSnapshots();
211         } else {
212             return repository.getReleases();
213         }
214     }
215 }