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