View Javadoc
1   package org.apache.maven.archetype.common;
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.commons.io.IOUtils;
23  import org.apache.maven.archetype.downloader.DownloadException;
24  import org.apache.maven.archetype.downloader.DownloadNotFoundException;
25  import org.apache.maven.archetype.downloader.Downloader;
26  import org.apache.maven.archetype.exception.UnknownArchetype;
27  import org.apache.maven.archetype.metadata.ArchetypeDescriptor;
28  import org.apache.maven.archetype.metadata.io.xpp3.ArchetypeDescriptorXpp3Reader;
29  import org.apache.maven.archetype.old.descriptor.ArchetypeDescriptorBuilder;
30  import org.apache.maven.artifact.repository.ArtifactRepository;
31  import org.apache.maven.model.Model;
32  import org.apache.maven.project.ProjectBuildingRequest;
33  import org.codehaus.plexus.component.annotations.Component;
34  import org.codehaus.plexus.component.annotations.Requirement;
35  import org.codehaus.plexus.logging.AbstractLogEnabled;
36  import org.codehaus.plexus.util.ReaderFactory;
37  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
38  
39  import java.io.File;
40  import java.io.IOException;
41  import java.io.InputStream;
42  import java.io.Reader;
43  
44  import java.net.MalformedURLException;
45  import java.net.URL;
46  import java.net.URLClassLoader;
47  
48  import java.util.ArrayList;
49  import java.util.Enumeration;
50  import java.util.List;
51  import java.util.Map;
52  import java.util.TreeMap;
53  import java.util.zip.ZipEntry;
54  import java.util.zip.ZipFile;
55  
56  @Component( role = ArchetypeArtifactManager.class )
57  public class DefaultArchetypeArtifactManager
58      extends AbstractLogEnabled
59      implements ArchetypeArtifactManager
60  {
61      @Requirement
62      private Downloader downloader;
63  
64      @Requirement
65      private PomManager pomManager;
66  
67      private Map<String, File> archetypeCache = new TreeMap<>();
68  
69      @Override
70      public File getArchetypeFile( final String groupId, final String artifactId, final String version,
71                                    ArtifactRepository archetypeRepository, final ArtifactRepository localRepository,
72                                    final List<ArtifactRepository> repositories, ProjectBuildingRequest buildingRequest )
73          throws UnknownArchetype
74      {
75          try
76          {
77              File archetype = getArchetype( groupId, artifactId, version );
78  
79              if ( archetype == null )
80              {
81                  archetype =
82                      downloader.download( groupId, artifactId, version, archetypeRepository, localRepository,
83                                           repositories, buildingRequest );
84  
85                  setArchetype( groupId, artifactId, version, archetype );
86              }
87              return archetype;
88          }
89          catch ( DownloadNotFoundException ex )
90          {
91              throw new UnknownArchetype( ex );
92          }
93          catch ( DownloadException ex )
94          {
95              throw new UnknownArchetype( ex );
96          }
97      }
98  
99      @Override
100     public ClassLoader getArchetypeJarLoader( File archetypeFile )
101         throws UnknownArchetype
102     {
103         try
104         {
105             URL[] urls = new URL[1];
106 
107             urls[0] = archetypeFile.toURI().toURL();
108 
109             return new URLClassLoader( urls );
110         }
111         catch ( MalformedURLException e )
112         {
113             throw new UnknownArchetype( e );
114         }
115     }
116 
117     @Override
118     public Model getArchetypePom( File jar )
119         throws XmlPullParserException, UnknownArchetype, IOException
120     {
121         
122         try ( ZipFile zipFile = getArchetypeZipFile( jar ) )
123         {
124             String pomFileName = null;
125 
126             Enumeration<? extends ZipEntry> enumeration = zipFile.entries();
127             while ( enumeration.hasMoreElements() )
128             {
129                 ZipEntry el = enumeration.nextElement();
130 
131                 String entry = el.getName();
132                 if ( entry.startsWith( "META-INF" ) && entry.endsWith( "pom.xml" ) )
133                 {
134                     pomFileName = entry;
135                 }
136             }
137 
138             if ( pomFileName == null )
139             {
140                 return null;
141             }
142 
143             ZipEntry pom = zipFile.getEntry( pomFileName );
144 
145             if ( pom == null )
146             {
147                 return null;
148             }
149             return pomManager.readPom( zipFile.getInputStream( pom ) );
150         }
151     }
152 
153     @Override
154     public ZipFile getArchetypeZipFile( File archetypeFile )
155         throws UnknownArchetype
156     {
157         try
158         {
159             return new ZipFile( archetypeFile );
160         }
161         catch ( IOException e )
162         {
163             throw new UnknownArchetype( e );
164         }
165     }
166 
167     @Override
168     public boolean isFileSetArchetype( File archetypeFile )
169     {
170         getLogger().debug( "checking fileset archetype status on " + archetypeFile );
171         
172         try ( ZipFile zipFile = getArchetypeZipFile( archetypeFile ) )
173         {
174             return isFileSetArchetype( zipFile );
175         }
176         catch ( IOException | UnknownArchetype e )
177         {
178             getLogger().debug( e.toString() );
179             return false;
180         }
181     }
182 
183     @Override
184     public boolean isFileSetArchetype( String groupId, String artifactId, String version,
185                                        ArtifactRepository archetypeRepository, ArtifactRepository localRepository,
186                                        List<ArtifactRepository> repositories, ProjectBuildingRequest buildingRequest )
187     {
188         try
189         {
190             File archetypeFile = getArchetypeFile( groupId, artifactId, version, archetypeRepository,
191                                                    localRepository, repositories, buildingRequest );
192 
193             return isFileSetArchetype( archetypeFile );
194         }
195         catch ( UnknownArchetype e )
196         {
197             getLogger().debug( e.toString() );
198             return false;
199         }
200     }
201 
202     @Override
203     public boolean isOldArchetype( File archetypeFile )
204     {
205         getLogger().debug( "checking old archetype status on " + archetypeFile );
206 
207         try ( ZipFile zipFile = getArchetypeZipFile( archetypeFile ) )
208         {
209             return isOldArchetype( zipFile );
210         }
211         catch ( IOException | UnknownArchetype e )
212         {
213             getLogger().debug( e.toString() );
214             return false;
215         }
216     }
217 
218     @Override
219     public boolean isOldArchetype( String groupId, String artifactId, String version,
220                                    ArtifactRepository archetypeRepository, ArtifactRepository localRepository,
221                                    List<ArtifactRepository> repositories, ProjectBuildingRequest buildingRequest )
222     {
223         try
224         {
225             File archetypeFile = getArchetypeFile( groupId, artifactId, version, archetypeRepository,
226                                                    localRepository, repositories, buildingRequest );
227 
228             return isOldArchetype( archetypeFile );
229         }
230         catch ( UnknownArchetype e )
231         {
232             getLogger().debug( e.toString() );
233             return false;
234         }
235     }
236 
237     @Override
238     public boolean exists( String archetypeGroupId, String archetypeArtifactId, String archetypeVersion,
239                            ArtifactRepository archetypeRepository, ArtifactRepository localRepository,
240                            List<ArtifactRepository> remoteRepositories, ProjectBuildingRequest buildingRequest )
241     {
242         try
243         {
244             File archetype = getArchetype( archetypeGroupId, archetypeArtifactId, archetypeVersion );
245             if ( archetype == null )
246             {
247                 archetype =
248                     downloader.download( archetypeGroupId, archetypeArtifactId, archetypeVersion, archetypeRepository,
249                                          localRepository, remoteRepositories, buildingRequest );
250                 setArchetype( archetypeGroupId, archetypeArtifactId, archetypeVersion, archetype );
251             }
252 
253             return archetype.exists();
254         }
255         catch ( DownloadException e )
256         {
257             getLogger().debug(
258                                "Archetype " + archetypeGroupId + ":" + archetypeArtifactId + ":" + archetypeVersion
259                                    + " doesn't exist", e );
260             return false;
261         }
262         catch ( DownloadNotFoundException e )
263         {
264             getLogger().debug(
265                               "Archetype " + archetypeGroupId + ":" + archetypeArtifactId + ":" + archetypeVersion
266                                   + " doesn't exist", e );
267             return false;
268         }
269     }
270 
271     @Override
272     public String getPostGenerationScript( File archetypeFile ) throws UnknownArchetype
273     {
274         try ( ZipFile zipFile = getArchetypeZipFile( archetypeFile ) )
275         {
276             Reader reader = getDescriptorReader( zipFile, Constants.ARCHETYPE_POST_GENERATION_SCRIPT );
277             return reader == null ? null : IOUtils.toString( reader );
278         }
279         catch ( IOException e )
280         {
281             throw new UnknownArchetype( e );
282         }
283     }
284 
285     @Override
286     public ArchetypeDescriptor getFileSetArchetypeDescriptor( File archetypeFile )
287         throws UnknownArchetype
288     {
289         try ( ZipFile zipFile = getArchetypeZipFile( archetypeFile ) )
290         {
291             return loadFileSetArchetypeDescriptor( zipFile );
292         }
293         catch ( XmlPullParserException | IOException e )
294         {
295             throw new UnknownArchetype( e );
296         }
297     }
298 
299     @Override
300     public org.apache.maven.archetype.metadata.ArchetypeDescriptor getFileSetArchetypeDescriptor( String groupId,
301                                                                           String artifactId,
302                                                                           String version,
303                                                                           ArtifactRepository archetypeRepository,
304                                                                           ArtifactRepository localRepository,
305                                                                           List<ArtifactRepository> repositories,
306                                                                           ProjectBuildingRequest buildingRequest )
307         throws UnknownArchetype
308     {
309         File archetypeFile = getArchetypeFile( groupId, artifactId, version, archetypeRepository, localRepository,
310                                                repositories, buildingRequest );
311 
312         return getFileSetArchetypeDescriptor( archetypeFile );
313     }
314 
315     @Override
316     public List<String> getFilesetArchetypeResources( File archetypeFile )
317         throws UnknownArchetype
318     {
319         getLogger().debug( "getFilesetArchetypeResources( \"" + archetypeFile.getAbsolutePath() + "\" )" );
320         List<String> archetypeResources = new ArrayList<>();
321 
322         try ( ZipFile zipFile = getArchetypeZipFile( archetypeFile )  ) 
323         {
324             Enumeration<? extends ZipEntry> enumeration = zipFile.entries();
325             while ( enumeration.hasMoreElements() )
326             {
327                 ZipEntry entry = enumeration.nextElement();
328 
329                 if ( entry.getName().startsWith( Constants.ARCHETYPE_RESOURCES ) )
330                 {
331                     // not supposed to be file.separator
332                     String resource = entry.getName().substring( Constants.ARCHETYPE_RESOURCES.length() + 1 );
333                     getLogger().debug( "  - found resource (" + Constants.ARCHETYPE_RESOURCES + "/)" + resource );
334                     // TODO:FIXME
335                     archetypeResources.add( resource );
336                 }
337                 else
338                 {
339                     getLogger().debug( "  - ignored resource " + entry.getName() );
340                 }
341             }
342             return archetypeResources;
343         }
344         catch ( IOException e )
345         {
346             throw new UnknownArchetype( e );
347         }
348     }
349 
350     @Override
351     public org.apache.maven.archetype.old.descriptor.ArchetypeDescriptor getOldArchetypeDescriptor( File archetypeFile )
352         throws UnknownArchetype
353     {
354         try ( ZipFile zipFile = getArchetypeZipFile( archetypeFile ) )
355         {
356             return loadOldArchetypeDescriptor( zipFile );
357         }
358         catch ( XmlPullParserException | IOException e )
359         {
360             throw new UnknownArchetype( e );
361         }
362     }
363 
364     @Override
365     public org.apache.maven.archetype.old.descriptor.ArchetypeDescriptor getOldArchetypeDescriptor( String groupId,
366                                                                             String artifactId,
367                                                                             String version,
368                                                                             ArtifactRepository archetypeRepository,
369                                                                             ArtifactRepository localRepository,
370                                                                             List<ArtifactRepository> repositories,
371                                                                             ProjectBuildingRequest buildingRequest )
372         throws UnknownArchetype
373     {
374         File archetypeFile = getArchetypeFile( groupId, artifactId, version, archetypeRepository, localRepository,
375                                                repositories, buildingRequest );
376 
377         return getOldArchetypeDescriptor( archetypeFile );
378     }
379 
380     private File getArchetype( String archetypeGroupId, String archetypeArtifactId, String archetypeVersion )
381     {
382         String key = archetypeGroupId + ":" + archetypeArtifactId + ":" + archetypeVersion;
383 
384         if ( archetypeCache.containsKey( key ) )
385         {
386             getLogger().debug( "Found archetype " + key + " in cache: " + archetypeCache.get( key ) );
387 
388             return archetypeCache.get( key );
389         }
390 
391         getLogger().debug( "Not found archetype " + key + " in cache" );
392         return null;
393     }
394 
395     private void setArchetype( String archetypeGroupId, String archetypeArtifactId, String archetypeVersion,
396                                File archetype )
397     {
398         String key = archetypeGroupId + ":" + archetypeArtifactId + ":" + archetypeVersion;
399 
400         archetypeCache.put( key, archetype );
401     }
402 
403     private boolean isFileSetArchetype( ZipFile zipFile )
404         throws IOException
405     {
406         try ( Reader reader = getArchetypeDescriptorReader( zipFile ); )
407         {
408             return ( reader != null );
409         }
410     }
411 
412     private boolean isOldArchetype( ZipFile zipFile )
413         throws IOException
414     {
415         try ( Reader reader = getOldArchetypeDescriptorReader( zipFile ) )
416         {
417             return ( reader != null );
418         }
419     }
420 
421     private org.apache.maven.archetype.metadata.ArchetypeDescriptor loadFileSetArchetypeDescriptor( ZipFile zipFile )
422         throws IOException, XmlPullParserException
423     {
424         
425         try ( Reader reader = getArchetypeDescriptorReader( zipFile ) )
426         {
427             if ( reader == null )
428             {
429                 return null;
430             }
431 
432             ArchetypeDescriptorXpp3Reader archetypeReader = new ArchetypeDescriptorXpp3Reader();
433             return archetypeReader.read( reader, false );
434         }
435         catch ( IOException | XmlPullParserException e )
436         {
437             throw e;
438         }
439     }
440 
441     private org.apache.maven.archetype.old.descriptor.ArchetypeDescriptor loadOldArchetypeDescriptor( ZipFile zipFile )
442         throws IOException, XmlPullParserException
443     {
444         try ( Reader reader = getOldArchetypeDescriptorReader( zipFile ) )
445         {
446             if ( reader == null )
447             {
448                 return null;
449             }
450 
451             ArchetypeDescriptorBuilder builder = new ArchetypeDescriptorBuilder();
452             return builder.build( reader );
453         }
454         catch ( IOException | XmlPullParserException  ex )
455         {
456             throw ex;
457         }
458     }
459 
460     private Reader getArchetypeDescriptorReader( ZipFile zipFile )
461         throws IOException
462     {
463         return getDescriptorReader( zipFile, Constants.ARCHETYPE_DESCRIPTOR );
464     }
465 
466     private Reader getOldArchetypeDescriptorReader( ZipFile zipFile )
467         throws IOException
468     {
469         Reader reader = getDescriptorReader( zipFile, Constants.OLD_ARCHETYPE_DESCRIPTOR );
470 
471         if ( reader == null )
472         {
473             reader = getDescriptorReader( zipFile, Constants.OLDER_ARCHETYPE_DESCRIPTOR );
474         }
475 
476         return reader;
477     }
478 
479     private Reader getDescriptorReader( ZipFile zipFile, String descriptor )
480         throws IOException
481     {
482         ZipEntry entry = searchEntry( zipFile, descriptor );
483 
484         if ( entry == null )
485         {
486             return null;
487         }
488 
489         InputStream is = zipFile.getInputStream( entry );
490 
491         if ( is == null )
492         {
493             throw new IOException( "The " + descriptor + " descriptor cannot be read in " + zipFile.getName() + "." );
494         }
495 
496         return ReaderFactory.newReader( is, ReaderFactory.UTF_8 );
497     }
498 
499     private ZipEntry searchEntry( ZipFile zipFile, String searchString )
500     {
501         getLogger().debug( "Searching for " + searchString + " inside " + zipFile.getName() );
502 
503         Enumeration<? extends ZipEntry> enu = zipFile.entries();
504         while ( enu.hasMoreElements() )
505         {
506             ZipEntry entryfound = enu.nextElement();
507             getLogger().debug( "  - " + entryfound.getName() );
508 
509             if ( searchString.equals( entryfound.getName() ) )
510             {
511                 getLogger().debug( "Entry found" );
512                 return entryfound;
513             }
514         }
515         return null;
516     }
517 
518 }