View Javadoc

1   package org.apache.maven.plugin.testing;
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 java.io.File;
23  import java.io.IOException;
24  import java.lang.reflect.Field;
25  import java.util.HashSet;
26  import java.util.Set;
27  
28  import org.apache.maven.artifact.Artifact;
29  import org.apache.maven.artifact.DefaultArtifact;
30  import org.apache.maven.artifact.handler.ArtifactHandler;
31  import org.apache.maven.artifact.versioning.VersionRange;
32  import org.apache.maven.plugin.testing.stubs.DefaultArtifactHandlerStub;
33  import org.codehaus.plexus.archiver.Archiver;
34  import org.codehaus.plexus.archiver.ArchiverException;
35  import org.codehaus.plexus.archiver.manager.ArchiverManager;
36  import org.codehaus.plexus.archiver.manager.NoSuchArchiverException;
37  import org.codehaus.plexus.archiver.war.WarArchiver;
38  import org.codehaus.plexus.util.FileUtils;
39  import org.codehaus.plexus.util.ReflectionUtils;
40  import org.codehaus.plexus.util.StringUtils;
41  
42  /**
43   * This class creates artifacts to be used for testing purposes. It can optionally create actual files on the local disk
44   * for things like copying. It can create these files as archives with named files inside to be used for testing things
45   * like unpack. Also provided are some utility methods to quickly get a set of artifacts distinguished by various things
46   * like group,artifact,type,scope, etc It was originally developed for the dependency plugin, but can be useful in other
47   * plugins that need to simulate artifacts for unit tests.
48   *
49   * @author <a href="mailto:brianf@apache.org">Brian Fox</a>
50   * @version $Id$
51   */
52  public class ArtifactStubFactory
53  {
54      private File workingDir;
55  
56      private boolean createFiles;
57  
58      private File srcFile;
59  
60      private boolean createUnpackableFile;
61  
62      private ArchiverManager archiverManager;
63  
64      /**
65       * Default constructor. This should be used only if real files aren't needed...just the artifact objects
66       */
67      public ArtifactStubFactory()
68      {
69          this.workingDir = null;
70          this.createFiles = false;
71      }
72  
73      /**
74       * This constructor is to be used if files are needed and to set a working dir
75       *
76       * @param workingDir
77       * @param createFiles
78       */
79      public ArtifactStubFactory( File workingDir, boolean createFiles )
80      {
81          this.workingDir = new File( workingDir, "localTestRepo" );
82          this.createFiles = createFiles;
83      }
84  
85      /**
86       * If set, the file will be created as a zip/jar/war with a file inside that can be checked to exist after
87       * unpacking.
88       *
89       * @param archiverManager
90       */
91      public void setUnpackableFile( ArchiverManager archiverManager )
92      {
93          this.createUnpackableFile = true;
94          this.archiverManager = archiverManager;
95      }
96  
97      /**
98       * @param groupId
99       * @param artifactId
100      * @param version
101      * @return a <code>DefaultArtifact</code> instance for the given parameters
102      * @throws IOException if any
103      * @see #createArtifact(String, String, String, String, String, String)
104      */
105     public Artifact createArtifact( String groupId, String artifactId, String version )
106         throws IOException
107     {
108         return createArtifact( groupId, artifactId, version, Artifact.SCOPE_COMPILE, "jar", "" );
109     }
110 
111     /**
112      * @param groupId
113      * @param artifactId
114      * @param version
115      * @param scope
116      * @return a <code>DefaultArtifact</code> instance for the given parameters
117      * @throws IOException if any
118      * @see #createArtifact(String, String, String, String, String, String)
119      */
120     public Artifact createArtifact( String groupId, String artifactId, String version, String scope )
121         throws IOException
122     {
123         return createArtifact( groupId, artifactId, version, scope, "jar", "" );
124     }
125 
126     /**
127      * @param groupId
128      * @param artifactId
129      * @param version
130      * @param scope
131      * @param type
132      * @param classifier
133      * @return a <code>DefaultArtifact</code> instance for the given parameters
134      * @throws IOException if any
135      * @see #createArtifact(String, String, VersionRange, String, String, String, boolean)
136      */
137     public Artifact createArtifact( String groupId, String artifactId, String version, String scope, String type,
138                                     String classifier )
139         throws IOException
140     {
141         VersionRange vr = VersionRange.createFromVersion( version );
142         return createArtifact( groupId, artifactId, vr, scope, type, classifier, false );
143     }
144 
145     /**
146      * @param groupId not null
147      * @param artifactId not null
148      * @param versionRange not null
149      * @param scope not null
150      * @param type not null
151      * @param classifier
152      * @param optional not null
153      * @return a <code>DefaultArtifact</code> instance
154      * @throws IOException if any
155      */
156     public Artifact createArtifact( String groupId, String artifactId, VersionRange versionRange, String scope,
157                                     String type, String classifier, boolean optional )
158         throws IOException
159     {
160         ArtifactHandler ah = new DefaultArtifactHandlerStub( type, classifier );
161 
162         Artifact artifact =
163             new DefaultArtifact( groupId, artifactId, versionRange, scope, type, classifier, ah, optional );
164 
165         // i have no idea why this needs to be done manually when isSnapshot is able to figure it out.
166         artifact.setRelease( !artifact.isSnapshot() );
167 
168         if ( createFiles )
169         {
170             setArtifactFile( artifact, this.workingDir, this.srcFile, this.createUnpackableFile );
171         }
172         return artifact;
173     }
174 
175     /**
176      * Creates a new empty file and attaches it to the artifact.
177      *
178      * @param artifact to attach the file to.
179      * @param workingDir where to locate the new file
180      * @throws IOException
181      */
182     public void setArtifactFile( Artifact artifact, File workingDir )
183         throws IOException
184     {
185         setArtifactFile( artifact, workingDir, null, false );
186     }
187 
188     /**
189      * Copyies the srcFile to the workingDir and then attaches it to the artifact. If srcFile is null, a new empty file
190      * will be created.
191      *
192      * @param artifact to attach
193      * @param workingDir where to copy the srcFile.
194      * @param srcFile file to be attached.
195      * @throws IOException
196      */
197     public void setArtifactFile( Artifact artifact, File workingDir, File srcFile )
198         throws IOException
199     {
200         setArtifactFile( artifact, workingDir, srcFile, false );
201     }
202 
203     /**
204      * Creates an unpackable file (zip,jar etc) containing an empty file.
205      *
206      * @param artifact to attach
207      * @param workingDir where to create the file.
208      * @throws IOException
209      */
210     public void setUnpackableArtifactFile( Artifact artifact, File workingDir )
211         throws IOException
212     {
213         setArtifactFile( artifact, workingDir, null, true );
214     }
215 
216     /**
217      * Creates an unpackable file (zip,jar etc) containing the srcFile. If srcFile is null, a new empty file will be
218      * created.
219      *
220      * @param artifact to attach
221      * @param workingDir where to create the file.
222      * @param srcFile
223      * @throws IOException if any
224      */
225     public void setUnpackableArtifactFile( Artifact artifact, File workingDir, File srcFile )
226         throws IOException
227     {
228         setArtifactFile( artifact, workingDir, srcFile, true );
229     }
230 
231     /**
232      * Creates a file that can be copied or unpacked based on the passed in artifact
233      *
234      * @param artifact
235      * @param workingDir
236      * @param srcFile
237      * @param createUnpackableFile
238      * @throws IOException if any
239      */
240     private void setArtifactFile( Artifact artifact, File workingDir, File srcFile, boolean createUnpackableFile )
241         throws IOException
242     {
243         if ( workingDir == null )
244         {
245             throw new IllegalArgumentException(
246                                                 "The workingDir must be set." );
247         }
248 
249         String fileName = getFormattedFileName( artifact, false );
250 
251         File theFile = new File( workingDir, fileName );
252         theFile.getParentFile().mkdirs();
253 
254         if ( srcFile == null )
255         {
256             theFile.createNewFile();
257         }
258         else if ( createUnpackableFile )
259         {
260             try
261             {
262                 createUnpackableFile( artifact, theFile );
263             }
264             catch ( NoSuchArchiverException e )
265             {
266                 throw new IOException( "NoSuchArchiverException: " + e.getMessage() );
267             }
268             catch ( ArchiverException e )
269             {
270                 throw new IOException( "ArchiverException: " + e.getMessage() );
271             }
272         }
273         else
274         {
275             FileUtils.copyFile( srcFile, theFile );
276         }
277 
278         artifact.setFile( theFile );
279     }
280 
281     /**
282      * @param artifact
283      * @return
284      */
285     public static String getUnpackableFileName( Artifact artifact )
286     {
287         return "" + artifact.getGroupId() + "-" + artifact.getArtifactId() + "-" + artifact.getVersion() + "-"
288             + artifact.getClassifier() + "-" + artifact.getType() + ".txt";
289     }
290 
291     /**
292      * @param artifact
293      * @param destFile
294      * @throws NoSuchArchiverException
295      * @throws ArchiverException if any
296      * @throws IOException if any
297      */
298     public void createUnpackableFile( Artifact artifact, File destFile )
299         throws NoSuchArchiverException, ArchiverException, IOException
300     {
301         Archiver archiver = archiverManager.getArchiver( destFile );
302 
303         archiver.setDestFile( destFile );
304         archiver.addFile( srcFile, getUnpackableFileName( artifact ) );
305 
306         try
307         {
308             setVariableValueToObject( archiver, "logger", new SilentLog() );
309         }
310         catch ( IllegalAccessException e )
311         {
312             System.out.println( "Unable to override logger with silent log." );
313             e.printStackTrace();
314         }
315         if ( archiver instanceof WarArchiver )
316         {
317             WarArchiver war = (WarArchiver) archiver;
318             // the use of this is counter-intuitive:
319             // http://jira.codehaus.org/browse/PLX-286
320             war.setIgnoreWebxml( false );
321         }
322         archiver.createArchive();
323     }
324 
325     /**
326      * @return a <code>DefaultArtifact</code> instance for <code>testGroupId:release:jar:1.0</code>
327      * @throws IOException if any
328      */
329     public Artifact getReleaseArtifact()
330         throws IOException
331     {
332         return createArtifact( "testGroupId", "release", "1.0" );
333     }
334 
335     /**
336      * @return a default <code>DefaultArtifact</code> instance for <code>testGroupId:snapshot:jar:2.0-SNAPSHOT</code>
337      * @throws IOException if any
338      */
339     public Artifact getSnapshotArtifact()
340         throws IOException
341     {
342         return createArtifact( "testGroupId", "snapshot", "2.0-SNAPSHOT" );
343     }
344 
345     /**
346      * @return a default set of release and snapshot <code>DefaultArtifact</code>, i.e.:
347      * <code>testGroupId:snapshot:jar:2.0-SNAPSHOT, testGroupId:release:jar:1.0</code>
348      * @throws IOException if any
349      * @see #getReleaseArtifact()
350      * @see #getSnapshotArtifact()
351      */
352     public Set<Artifact> getReleaseAndSnapshotArtifacts()
353         throws IOException
354     {
355         Set<Artifact> set = new HashSet<Artifact>();
356         set.add( getReleaseArtifact() );
357         set.add( getSnapshotArtifact() );
358         return set;
359     }
360 
361     /**
362      * @return a default set of <code>DefaultArtifact</code>, i.e.:
363      * <code>g:provided:jar:1.0, g:compile:jar:1.0, g:system:jar:1.0, g:test:jar:1.0, g:runtime:jar:1.0</code>
364      * @throws IOException if any
365      */
366     public Set<Artifact> getScopedArtifacts()
367         throws IOException
368     {
369         Set<Artifact> set = new HashSet<Artifact>();
370         set.add( createArtifact( "g", "compile", "1.0", Artifact.SCOPE_COMPILE ) );
371         set.add( createArtifact( "g", "provided", "1.0", Artifact.SCOPE_PROVIDED ) );
372         set.add( createArtifact( "g", "test", "1.0", Artifact.SCOPE_TEST ) );
373         set.add( createArtifact( "g", "runtime", "1.0", Artifact.SCOPE_RUNTIME ) );
374         set.add( createArtifact( "g", "system", "1.0", Artifact.SCOPE_SYSTEM ) );
375         return set;
376     }
377 
378     /**
379      * @return a set of <code>DefaultArtifact</code>, i.e.:
380      * <code>g:d:zip:1.0, g:a:war:1.0, g:b:jar:1.0, g:c:sources:1.0, g:e:rar:1.0</code>
381      * @throws IOException if any
382      */
383     public Set<Artifact> getTypedArtifacts()
384         throws IOException
385     {
386         Set<Artifact> set = new HashSet<Artifact>();
387         set.add( createArtifact( "g", "a", "1.0", Artifact.SCOPE_COMPILE, "war", null ) );
388         set.add( createArtifact( "g", "b", "1.0", Artifact.SCOPE_COMPILE, "jar", null ) );
389         set.add( createArtifact( "g", "c", "1.0", Artifact.SCOPE_COMPILE, "sources", null ) );
390         set.add( createArtifact( "g", "d", "1.0", Artifact.SCOPE_COMPILE, "zip", null ) );
391         set.add( createArtifact( "g", "e", "1.0", Artifact.SCOPE_COMPILE, "rar", null ) );
392         return set;
393     }
394 
395     /**
396      * @return a set of <code>DefaultArtifact</code>, i.e.:
397      * <code>g:c:jar:three:1.0, g:b:jar:two:1.0, g:d:jar:four:1.0, g:a:jar:one:1.0</code>
398      * @throws IOException if any
399      */
400     public Set<Artifact> getClassifiedArtifacts()
401         throws IOException
402     {
403         Set<Artifact> set = new HashSet<Artifact>();
404         set.add( createArtifact( "g", "a", "1.0", Artifact.SCOPE_COMPILE, "jar", "one" ) );
405         set.add( createArtifact( "g", "b", "1.0", Artifact.SCOPE_COMPILE, "jar", "two" ) );
406         set.add( createArtifact( "g", "c", "1.0", Artifact.SCOPE_COMPILE, "jar", "three" ) );
407         set.add( createArtifact( "g", "d", "1.0", Artifact.SCOPE_COMPILE, "jar", "four" ) );
408         return set;
409     }
410 
411     /**
412      * @return a set of <code>DefaultArtifact</code>, i.e.:
413      * <code>g:d:zip:1.0, g:a:war:1.0, g:b:jar:1.0, g:e:rar:1.0</code>
414      * @throws IOException if any
415      */
416     public Set<Artifact> getTypedArchiveArtifacts()
417         throws IOException
418     {
419         Set<Artifact> set = new HashSet<Artifact>();
420         set.add( createArtifact( "g", "a", "1.0", Artifact.SCOPE_COMPILE, "war", null ) );
421         set.add( createArtifact( "g", "b", "1.0", Artifact.SCOPE_COMPILE, "jar", null ) );
422         set.add( createArtifact( "g", "d", "1.0", Artifact.SCOPE_COMPILE, "zip", null ) );
423         set.add( createArtifact( "g", "e", "1.0", Artifact.SCOPE_COMPILE, "rar", null ) );
424         return set;
425     }
426 
427     /**
428      * @return a set of <code>DefaultArtifact</code>, i.e.:
429      * <code>g:one:jar:a:1.0, g:two:jar:a:1.0, g:four:jar:a:1.0, g:three:jar:a:1.0</code>
430      * @throws IOException if any
431      */
432     public Set<Artifact> getArtifactArtifacts()
433         throws IOException
434     {
435         Set<Artifact> set = new HashSet<Artifact>();
436         set.add( createArtifact( "g", "one", "1.0", Artifact.SCOPE_COMPILE, "jar", "a" ) );
437         set.add( createArtifact( "g", "two", "1.0", Artifact.SCOPE_COMPILE, "jar", "a" ) );
438         set.add( createArtifact( "g", "three", "1.0", Artifact.SCOPE_COMPILE, "jar", "a" ) );
439         set.add( createArtifact( "g", "four", "1.0", Artifact.SCOPE_COMPILE, "jar", "a" ) );
440         return set;
441     }
442 
443     /**
444      * @return a set of <code>DefaultArtifact</code>, i.e.:
445      * <code>one:group-one:jar:a:1.0, three:group-three:jar:a:1.0, four:group-four:jar:a:1.0,
446      * two:group-two:jar:a:1.0</code>
447      * @throws IOException if any
448      */
449     public Set<Artifact> getGroupIdArtifacts()
450         throws IOException
451     {
452         Set<Artifact> set = new HashSet<Artifact>();
453         set.add( createArtifact( "one", "group-one", "1.0", Artifact.SCOPE_COMPILE, "jar", "a" ) );
454         set.add( createArtifact( "two", "group-two", "1.0", Artifact.SCOPE_COMPILE, "jar", "a" ) );
455         set.add( createArtifact( "three", "group-three", "1.0", Artifact.SCOPE_COMPILE, "jar", "a" ) );
456         set.add( createArtifact( "four", "group-four", "1.0", Artifact.SCOPE_COMPILE, "jar", "a" ) );
457         return set;
458     }
459 
460     /**
461      * @return a set of <code>DefaultArtifact</code>
462      * @throws IOException if any
463      * @see #getTypedArtifacts()
464      * @see #getScopedArtifacts()
465      * @see #getReleaseAndSnapshotArtifacts()
466      */
467     public Set<Artifact> getMixedArtifacts()
468         throws IOException
469     {
470         Set<Artifact> set = new HashSet<Artifact>();
471         set.addAll( getTypedArtifacts() );
472         set.addAll( getScopedArtifacts() );
473         set.addAll( getReleaseAndSnapshotArtifacts() );
474         return set;
475     }
476 
477     /**
478      * @return Returns the createFiles.
479      */
480     public boolean isCreateFiles()
481     {
482         return this.createFiles;
483     }
484 
485     /**
486      * @param createFiles The createFiles to set.
487      */
488     public void setCreateFiles( boolean createFiles )
489     {
490         this.createFiles = createFiles;
491     }
492 
493     /**
494      * @return Returns the workingDir.
495      */
496     public File getWorkingDir()
497     {
498         return this.workingDir;
499     }
500 
501     /**
502      * @param workingDir The workingDir to set.
503      */
504     public void setWorkingDir( File workingDir )
505     {
506         this.workingDir = workingDir;
507     }
508 
509     /**
510      * @return Returns the srcFile.
511      */
512     public File getSrcFile()
513     {
514         return this.srcFile;
515     }
516 
517     /**
518      * @param srcFile The srcFile to set.
519      */
520     public void setSrcFile( File srcFile )
521     {
522         this.srcFile = srcFile;
523     }
524 
525     /**
526      * Convenience method to set values to variables in objects that don't have setters
527      *
528      * @param object
529      * @param variable
530      * @param value
531      * @throws IllegalAccessException
532      */
533     public static void setVariableValueToObject( Object object, String variable, Object value )
534         throws IllegalAccessException
535     {
536         Field field = ReflectionUtils.getFieldByNameIncludingSuperclasses( variable, object.getClass() );
537 
538         field.setAccessible( true );
539 
540         field.set( object, value );
541     }
542 
543     /**
544      * Builds the file name. If removeVersion is set, then the file name must be reconstructed from the artifactId,
545      * Classifier (if used) and Type. Otherwise, this method returns the artifact file name.
546      *
547      * @param artifact File to be formatted.
548      * @param removeVersion Specifies if the version should be removed from the file name.
549      * @return Formatted file name in the format artifactId-[version]-[classifier].[type]
550      */
551     public static String getFormattedFileName( Artifact artifact, boolean removeVersion )
552     {
553         String destFileName = null;
554 
555         // if there is a file and we aren't stripping the version, just get the
556         // name directly
557         if ( artifact.getFile() != null && !removeVersion )
558         {
559             destFileName = artifact.getFile().getName();
560         }
561         else
562         // if offline
563         {
564             String versionString = null;
565             if ( !removeVersion )
566             {
567                 versionString = "-" + artifact.getVersion();
568             }
569             else
570             {
571                 versionString = "";
572             }
573 
574             String classifierString = "";
575 
576             if ( StringUtils.isNotEmpty( artifact.getClassifier() ) )
577             {
578                 classifierString = "-" + artifact.getClassifier();
579             }
580 
581             destFileName = artifact.getArtifactId() + versionString + classifierString + "."
582                 + artifact.getArtifactHandler().getExtension();
583         }
584         return destFileName;
585     }
586 
587 }