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.archetype.common;
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.io.IOException;
27  import java.io.InputStream;
28  import java.io.InputStreamReader;
29  import java.io.Reader;
30  import java.net.MalformedURLException;
31  import java.net.URL;
32  import java.net.URLClassLoader;
33  import java.nio.charset.StandardCharsets;
34  import java.util.ArrayList;
35  import java.util.Enumeration;
36  import java.util.List;
37  import java.util.Map;
38  import java.util.TreeMap;
39  import java.util.zip.ZipEntry;
40  import java.util.zip.ZipFile;
41  
42  import org.apache.commons.io.IOUtils;
43  import org.apache.maven.archetype.downloader.DownloadException;
44  import org.apache.maven.archetype.downloader.Downloader;
45  import org.apache.maven.archetype.exception.UnknownArchetype;
46  import org.apache.maven.archetype.metadata.ArchetypeDescriptor;
47  import org.apache.maven.archetype.metadata.io.xpp3.ArchetypeDescriptorXpp3Reader;
48  import org.apache.maven.archetype.old.descriptor.ArchetypeDescriptorBuilder;
49  import org.apache.maven.model.Model;
50  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
51  import org.eclipse.aether.RepositorySystemSession;
52  import org.eclipse.aether.repository.RemoteRepository;
53  import org.slf4j.Logger;
54  import org.slf4j.LoggerFactory;
55  
56  @Named
57  @Singleton
58  public class DefaultArchetypeArtifactManager implements ArchetypeArtifactManager {
59      private static final Logger LOGGER = LoggerFactory.getLogger(DefaultArchetypeArtifactManager.class);
60  
61      @Inject
62      private Downloader downloader;
63  
64      @Inject
65      private PomManager pomManager;
66  
67      private Map<String, File> archetypeCache = new TreeMap<>();
68  
69      @Override
70      public File getArchetypeFile(
71              final String groupId,
72              final String artifactId,
73              final String version,
74              final List<RemoteRepository> repositories,
75              RepositorySystemSession repositorySystemSession)
76              throws UnknownArchetype {
77          try {
78              File archetype = getArchetype(groupId, artifactId, version);
79  
80              if (archetype == null) {
81                  archetype = downloader.download(groupId, artifactId, version, repositories, repositorySystemSession);
82  
83                  setArchetype(groupId, artifactId, version, archetype);
84              }
85              return archetype;
86          } catch (DownloadException ex) {
87              throw new UnknownArchetype(ex);
88          }
89      }
90  
91      @Override
92      public ClassLoader getArchetypeJarLoader(File archetypeFile) throws UnknownArchetype {
93          try {
94              URL[] urls = new URL[1];
95  
96              urls[0] = archetypeFile.toURI().toURL();
97  
98              return new URLClassLoader(urls);
99          } catch (MalformedURLException e) {
100             throw new UnknownArchetype(e);
101         }
102     }
103 
104     @Override
105     public Model getArchetypePom(File jar) throws XmlPullParserException, UnknownArchetype, IOException {
106 
107         try (ZipFile zipFile = getArchetypeZipFile(jar)) {
108             String pomFileName = null;
109 
110             Enumeration<? extends ZipEntry> enumeration = zipFile.entries();
111             while (enumeration.hasMoreElements()) {
112                 ZipEntry el = enumeration.nextElement();
113 
114                 String entry = el.getName();
115                 if (entry.startsWith("META-INF") && entry.endsWith("pom.xml")) {
116                     pomFileName = entry;
117                 }
118             }
119 
120             if (pomFileName == null) {
121                 return null;
122             }
123 
124             ZipEntry pom = zipFile.getEntry(pomFileName);
125 
126             if (pom == null) {
127                 return null;
128             }
129             return pomManager.readPom(zipFile.getInputStream(pom));
130         }
131     }
132 
133     @Override
134     public ZipFile getArchetypeZipFile(File archetypeFile) throws UnknownArchetype {
135         try {
136             return new ZipFile(archetypeFile);
137         } catch (IOException e) {
138             throw new UnknownArchetype(e);
139         }
140     }
141 
142     @Override
143     public boolean isFileSetArchetype(File archetypeFile) {
144         LOGGER.debug("checking fileset archetype status on " + archetypeFile);
145 
146         try (ZipFile zipFile = getArchetypeZipFile(archetypeFile)) {
147             return isFileSetArchetype(zipFile);
148         } catch (IOException | UnknownArchetype e) {
149             LOGGER.debug(e.toString());
150             return false;
151         }
152     }
153 
154     @Override
155     public boolean isOldArchetype(File archetypeFile) {
156         LOGGER.debug("checking old archetype status on " + archetypeFile);
157 
158         try (ZipFile zipFile = getArchetypeZipFile(archetypeFile)) {
159             return isOldArchetype(zipFile);
160         } catch (IOException | UnknownArchetype e) {
161             LOGGER.debug(e.toString());
162             return false;
163         }
164     }
165 
166     @Override
167     public boolean exists(
168             String archetypeGroupId,
169             String archetypeArtifactId,
170             String archetypeVersion,
171             List<RemoteRepository> remoteRepositories,
172             RepositorySystemSession repositorySystemSession) {
173         try {
174             File archetype = getArchetype(archetypeGroupId, archetypeArtifactId, archetypeVersion);
175             if (archetype == null) {
176                 archetype = downloader.download(
177                         archetypeGroupId,
178                         archetypeArtifactId,
179                         archetypeVersion,
180                         remoteRepositories,
181                         repositorySystemSession);
182                 setArchetype(archetypeGroupId, archetypeArtifactId, archetypeVersion, archetype);
183             }
184 
185             return archetype.exists();
186         } catch (DownloadException e) {
187             LOGGER.debug(
188                     "Archetype " + archetypeGroupId + ":" + archetypeArtifactId + ":" + archetypeVersion
189                             + " doesn't exist",
190                     e);
191             return false;
192         }
193     }
194 
195     @Override
196     public String getPostGenerationScript(File archetypeFile) throws UnknownArchetype {
197         try (ZipFile zipFile = getArchetypeZipFile(archetypeFile)) {
198             Reader reader = getDescriptorReader(zipFile, Constants.ARCHETYPE_POST_GENERATION_SCRIPT);
199             return reader == null ? null : IOUtils.toString(reader);
200         } catch (IOException e) {
201             throw new UnknownArchetype(e);
202         }
203     }
204 
205     @Override
206     public ArchetypeDescriptor getFileSetArchetypeDescriptor(File archetypeFile) throws UnknownArchetype {
207         try (ZipFile zipFile = getArchetypeZipFile(archetypeFile)) {
208             return loadFileSetArchetypeDescriptor(zipFile);
209         } catch (XmlPullParserException | IOException e) {
210             throw new UnknownArchetype(e);
211         }
212     }
213 
214     @Override
215     public List<String> getFilesetArchetypeResources(File archetypeFile) throws UnknownArchetype {
216         LOGGER.debug("getFilesetArchetypeResources( \"" + archetypeFile.getAbsolutePath() + "\" )");
217         List<String> archetypeResources = new ArrayList<>();
218 
219         try (ZipFile zipFile = getArchetypeZipFile(archetypeFile)) {
220             Enumeration<? extends ZipEntry> enumeration = zipFile.entries();
221             while (enumeration.hasMoreElements()) {
222                 ZipEntry entry = enumeration.nextElement();
223 
224                 if (entry.getName().startsWith(Constants.ARCHETYPE_RESOURCES)) {
225                     // not supposed to be file.separator
226                     String resource = entry.getName().substring(Constants.ARCHETYPE_RESOURCES.length() + 1);
227                     LOGGER.debug("  - found resource (" + Constants.ARCHETYPE_RESOURCES + "/)" + resource);
228                     // TODO:FIXME
229                     archetypeResources.add(resource);
230                 } else {
231                     LOGGER.debug("  - ignored resource " + entry.getName());
232                 }
233             }
234             return archetypeResources;
235         } catch (IOException e) {
236             throw new UnknownArchetype(e);
237         }
238     }
239 
240     @Override
241     public org.apache.maven.archetype.old.descriptor.ArchetypeDescriptor getOldArchetypeDescriptor(File archetypeFile)
242             throws UnknownArchetype {
243         try (ZipFile zipFile = getArchetypeZipFile(archetypeFile)) {
244             return loadOldArchetypeDescriptor(zipFile);
245         } catch (XmlPullParserException | IOException e) {
246             throw new UnknownArchetype(e);
247         }
248     }
249 
250     private File getArchetype(String archetypeGroupId, String archetypeArtifactId, String archetypeVersion) {
251         String key = archetypeGroupId + ":" + archetypeArtifactId + ":" + archetypeVersion;
252 
253         if (archetypeCache.containsKey(key)) {
254             LOGGER.debug("Found archetype " + key + " in cache: " + archetypeCache.get(key));
255 
256             return archetypeCache.get(key);
257         }
258 
259         LOGGER.debug("Not found archetype " + key + " in cache");
260         return null;
261     }
262 
263     private void setArchetype(
264             String archetypeGroupId, String archetypeArtifactId, String archetypeVersion, File archetype) {
265         String key = archetypeGroupId + ":" + archetypeArtifactId + ":" + archetypeVersion;
266 
267         archetypeCache.put(key, archetype);
268     }
269 
270     private boolean isFileSetArchetype(ZipFile zipFile) throws IOException {
271         try (Reader reader = getArchetypeDescriptorReader(zipFile)) {
272             return (reader != null);
273         }
274     }
275 
276     private boolean isOldArchetype(ZipFile zipFile) throws IOException {
277         try (Reader reader = getOldArchetypeDescriptorReader(zipFile)) {
278             return (reader != null);
279         }
280     }
281 
282     private org.apache.maven.archetype.metadata.ArchetypeDescriptor loadFileSetArchetypeDescriptor(ZipFile zipFile)
283             throws IOException, XmlPullParserException {
284 
285         try (Reader reader = getArchetypeDescriptorReader(zipFile)) {
286             if (reader == null) {
287                 return null;
288             }
289 
290             ArchetypeDescriptorXpp3Reader archetypeReader = new ArchetypeDescriptorXpp3Reader();
291             return archetypeReader.read(reader, false);
292         } catch (IOException | XmlPullParserException e) {
293             throw e;
294         }
295     }
296 
297     private org.apache.maven.archetype.old.descriptor.ArchetypeDescriptor loadOldArchetypeDescriptor(ZipFile zipFile)
298             throws IOException, XmlPullParserException {
299         try (Reader reader = getOldArchetypeDescriptorReader(zipFile)) {
300             if (reader == null) {
301                 return null;
302             }
303 
304             ArchetypeDescriptorBuilder builder = new ArchetypeDescriptorBuilder();
305             return builder.build(reader);
306         }
307     }
308 
309     private Reader getArchetypeDescriptorReader(ZipFile zipFile) throws IOException {
310         return getDescriptorReader(zipFile, Constants.ARCHETYPE_DESCRIPTOR);
311     }
312 
313     private Reader getOldArchetypeDescriptorReader(ZipFile zipFile) throws IOException {
314         Reader reader = getDescriptorReader(zipFile, Constants.OLD_ARCHETYPE_DESCRIPTOR);
315 
316         if (reader == null) {
317             reader = getDescriptorReader(zipFile, Constants.OLDER_ARCHETYPE_DESCRIPTOR);
318         }
319 
320         return reader;
321     }
322 
323     private Reader getDescriptorReader(ZipFile zipFile, String descriptor) throws IOException {
324         ZipEntry entry = searchEntry(zipFile, descriptor);
325 
326         if (entry == null) {
327             return null;
328         }
329 
330         InputStream is = zipFile.getInputStream(entry);
331 
332         if (is == null) {
333             throw new IOException("The " + descriptor + " descriptor cannot be read in " + zipFile.getName() + ".");
334         }
335 
336         return new InputStreamReader(is, StandardCharsets.UTF_8);
337     }
338 
339     private ZipEntry searchEntry(ZipFile zipFile, String searchString) {
340         LOGGER.debug("Searching for " + searchString + " inside " + zipFile.getName());
341 
342         Enumeration<? extends ZipEntry> enu = zipFile.entries();
343         while (enu.hasMoreElements()) {
344             ZipEntry entryfound = enu.nextElement();
345             LOGGER.debug("  - " + entryfound.getName());
346 
347             if (searchString.equals(entryfound.getName())) {
348                 LOGGER.debug("Entry found");
349                 return entryfound;
350             }
351         }
352         return null;
353     }
354 }