View Javadoc

1   package net.sourceforge.basher.maven.plugins;
2   
3   /*
4    * Copyright 2001-2005 The Apache Software Foundation.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * 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, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  import java.io.File;
20  import java.util.*;
21  
22  import net.sourceforge.basher.BasherContext;
23  import net.sourceforge.basher.booter.*;
24  import org.apache.maven.artifact.Artifact;
25  import org.apache.maven.artifact.factory.ArtifactFactory;
26  import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
27  import org.apache.maven.artifact.repository.ArtifactRepository;
28  import org.apache.maven.artifact.resolver.*;
29  import org.apache.maven.artifact.resolver.filter.*;
30  import org.apache.maven.execution.MavenSession;
31  import org.apache.maven.plugin.*;
32  import org.apache.maven.project.MavenProject;
33  import org.apache.maven.surefire.booter.ForkConfiguration;
34  import org.apache.maven.toolchain.*;
35  import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
36  import org.codehaus.plexus.util.DirectoryScanner;
37  
38  /**
39   * Mojo will instantiate and run Basher according to the settings configured in the pom and associated
40   * Basher context (both from POM and from within Gaderian registry).
41   * <br/>
42   * The original functionality of this plugin was copied from the Maven SureFire plugin.
43   *
44   * @goal initiate-run
45   * @phase test
46   * @requiresDependencyResolution test
47   */
48  public class BasherMojo extends AbstractMojo
49  {
50      /**
51       * Base directory where all reports are written to.
52       *
53       * @parameter expression="${project.build.directory}/basher-reports"
54       */
55      private File reportsDirectory;
56  
57      /**
58       * The plugin remote repositories declared in the pom.
59       *
60       * @parameter expression="${project.pluginArtifactRepositories}"
61       */
62      private List remoteRepositories;
63      /**
64       * For retrieval of artifact's metadata.
65       *
66       * @component
67       */
68      private ArtifactMetadataSource metadataSource;
69  
70      /**
71       * Resolves the artifacts needed.
72       *
73       * @component
74       */
75      private ArtifactResolver artifactResolver;
76  
77      /**
78       * Map of of plugin artifacts.
79       *
80       * @parameter expression="${plugin.artifactMap}"
81       * @required
82       * @readonly
83       */
84      private Map pluginArtifactMap;
85  
86      /**
87       * Creates the artifact
88       *
89       * @component
90       */
91      private ArtifactFactory artifactFactory;
92  
93  
94      /**
95       * ArtifactRepository of the localRepository. To obtain the directory of localRepository in tasks use
96       * System.setProperty( "localRepository").
97       *
98       * @parameter expression="${localRepository}"
99       * @required
100      * @readonly
101      */
102     private ArtifactRepository localRepository;
103 
104 
105     /**
106      * Location of the build directory.
107      *
108      * @parameter expression="${project.build.outputDirectory}"
109      * @required
110      */
111     private File classesDirectory;
112 
113     /**
114      * Location of the build directory.
115      *
116      * @parameter expression="${project.build.testOutputDirectory}"
117      * @required
118      */
119     private File buildDirectory;
120 
121 
122     /**
123      * The classpath elements of the project being tested.
124      *
125      * @parameter expression="${project.testClasspathElements}"
126      * @required
127      * @readonly
128      */
129     private List classpathElements;
130 
131     /**
132      * The classpath elements of the project being tested.
133      *
134      * @parameter expression="${project.runtimeClasspathElements}"
135      * @required
136      * @readonly
137      */
138     private List additionalClasspathElements;
139 
140     /**
141      * List of patterns (separated by commas) used to specify the tasks that should be included in the Basher run. When not
142      * specified and when the <code>task</code> parameter is not specified, the default includes will be
143      * <code>**&#47;Task*.java   **&#47;*Task.java  </code>.
144      *
145      * @parameter
146      */
147     private List<String> includes;
148 
149     /**
150      * List of patterns (separated by commas) used to specify the tasks that should be excluded in the Basher run. When not
151      * specified and when the <code>task</code> parameter is not specified, the default excludes will be
152      * <code>**&#47;*$*</code> (which excludes all inner classes).
153      *
154      * @parameter
155      */
156     private List<String> excludes;
157 
158     /**
159      * List of System properties to pass to the Basher tasks.
160      *
161      * @parameter
162      */
163     private Properties systemProperties;
164 
165     private Properties originalSystemProperties;
166 
167 
168     /**
169      * List of Basher contexts which can be used for a run.
170      *
171      * @parameter
172      */
173     private List<BasherContext> basherContexts = new ArrayList<BasherContext>();
174 
175      /**
176      * Attach a profiler to the forked JVM.  If set to "yourkit", the process will activate
177       * a YourKit based profiler agent by default.<br/>
178       *
179       * If set to some other string, that
180      * string will be appended to the JVM arguments, allowing you to configure arbitrary
181      * profiling options (without overwriting the other options specified in the argLine).<br/>
182       * If set to "none", no profiler is used. This is useful when wanting to override a POM
183       * defined profiler.
184       * 
185      * @parameter expression="${maven.basher.profiler}"
186      */
187     private String profiler;
188 
189     /**
190      * The name of the Basher context to use
191      *
192      * @parameter default-value="default" expression="${maven.basher.activeBasherContext}"
193      */
194     private String activeBasherContext;
195 
196     /**
197      * The Maven Project Object
198      *
199      * @parameter expression="${project}"
200      * @required
201      * @readonly
202      */
203     protected MavenProject project;
204 
205     /**
206      * Option to specify the forking mode. Can be "never" or "once".
207      *
208      * @parameter expression="${maven.basher.forkMode}" default-value="once"
209      */
210     private String forkMode;
211 
212     /**
213      * Option to specify the jvm (or path to the java executable) to use with the forking options. For the default, the
214      * jvm will be the same as the one used to run Maven.
215      *
216      * @parameter expression="${maven.basher.jvm}"
217      */
218     private String jvm;
219 
220     /**
221      * Option to pass dependencies to the system's classloader instead of using an isolated class loader when forking.
222      * Prevents problems with JDKs which implement the service provider lookup mechanism by using the system's
223      * classloader.  Default value is "true".
224      *
225      * @parameter expression="${basher.useSystemClassLoader}"
226      */
227     private Boolean useSystemClassLoader;
228 
229 
230     /**
231      * By default, Basher forks your run using a manifest-only jar; set this parameter
232      * to "false" to force it to launch your Basher run with a plain old Java classpath.
233      * (See http://maven.apache.org/plugins/maven-surefire-plugin/examples/class-loading.html
234      * for a more detailed explanation of manifest-only jars and their benefits.)
235      * <p/>
236      * Default value is "true".  Beware, setting this to "false" may cause your tasks to
237      * fail on Windows if your classpath is too long.
238      *
239      * @parameter expression="${maven.basher.useManifestOnlyJar}" default-value="true"
240      */
241     private boolean useManifestOnlyJar;
242 
243     /**
244      * Attach a debugger to the forked JVM.  If set to "true", the process will suspend and
245      * wait for a debugger to attach on port 5005.  If set to some other string, that
246      * string will be appended to the argLine, allowing you to configure arbitrary
247      * debuggability options (without overwriting the other options specified in the argLine).
248      *
249      * @parameter expression="${maven.basher.debug}"
250      */
251     private String debugForkedProcess;
252     /**
253      * Command line working directory.
254      *
255      * @parameter expression="${basedir}"
256      */
257     private File workingDirectory;
258     /**
259      * The base directory of the project being used. This can be obtained in your Basher tasks by
260      * System.getProperty("basedir").
261      *
262      * @parameter expression="${basedir}"
263      * @required
264      */
265     private File basedir;
266 
267     /**
268      * Arbitrary JVM options to set on the command line.
269      *
270      * @parameter expression="${argLine}"
271      */
272     private String argLine;
273 
274     /**
275      * Additional environments to set on the command line.
276      *
277      * @parameter
278      */
279     private Map environmentVariables = new HashMap();
280 
281     /**
282      * By default, Basher enables JVM assertions for the execution of your tasks. To disable the assertions, set
283      * this flag to <code>false</code>.
284      *
285      * @parameter expression="${enableAssertions}" default-value="true"
286      */
287     private boolean enableAssertions;
288 
289     /**
290      * The current build session instance.
291      *
292      * @parameter expression="${session}"
293      * @required
294      * @readonly
295      */
296     private MavenSession session;
297 
298     /** Defines the timeout to use for monitoring a forked of Basher run.
299      *
300      *
301      * @parameter expression="${processTimeOut}" default-value="0"
302      */
303     private int processTimeOut = 0;
304 
305     public void execute() throws MojoExecutionException
306     {
307         try
308         {
309             if (excludes == null)
310             {
311                 excludes = new ArrayList<String>( );
312             }
313             if (excludes.isEmpty())
314             {
315                 excludes.add( "**/*$*" );
316             }
317             if (includes == null)
318             {
319                 includes= new ArrayList<String>( );
320             }
321             if (includes.isEmpty())
322             {
323                 includes.add( "**/*Task.java" );
324                 includes.add( "**/Task*.java" );
325             }
326 
327 
328 
329             BasherBooter basherBooter = new BasherBooter();
330 
331             Artifact basherBooterArtifact = (Artifact) pluginArtifactMap.get("net.sourceforge.basher:basher-booter");
332             if (basherBooterArtifact == null)
333             {
334                 throw new MojoExecutionException("Unable to locate basher-booter in the list of plugin artifacts");
335             }
336             addArtifact(basherBooter, basherBooterArtifact);
337 
338             if (!project.getBuild().getOutputDirectory().equals(classesDirectory.getAbsolutePath()))
339             {
340                 classpathElements.remove(project.getBuild().getOutputDirectory());
341                 classpathElements.add(classesDirectory.getAbsolutePath());
342             }
343             if (!project.getBuild().getTestOutputDirectory().equals(buildDirectory.getAbsolutePath()))
344             {
345                 classpathElements.remove(project.getBuild().getTestOutputDirectory());
346                 classpathElements.add(buildDirectory.getAbsolutePath());
347             }
348 
349             getLog().debug( "Basher ClassPaths: " );
350 
351             for (Iterator i = classpathElements.iterator(); i.hasNext();)
352             {
353                 String classpathElement = (String) i.next();
354 
355                 getLog().debug("  " + classpathElement);
356 
357                 basherBooter.addClassPathUrl(classpathElement);
358             }
359 
360 
361             Toolchain tc = getToolchain();
362 
363             if (tc != null)
364             {
365                 getLog().info("Toolchain in basher-plugin: " + tc);
366                 if (ForkConfiguration.FORK_NEVER.equals(forkMode))
367                 {
368                     forkMode = ForkConfiguration.FORK_ONCE;
369                 }
370                 if (jvm != null)
371                 {
372                     getLog().warn("Toolchains are ignored, 'executable' parameter is set to " + jvm);
373                 }
374                 else
375                 {
376                     jvm = tc.findTool("java");  //NOI18N
377                 }
378             }
379 
380         if (additionalClasspathElements != null)
381         {
382             for (Iterator i = additionalClasspathElements.iterator(); i.hasNext();)
383             {
384                 String classpathElement = (String) i.next();
385 
386                 getLog().debug("  " + classpathElement);
387 
388                 basherBooter.addClassPathUrl(classpathElement);
389             }
390         }
391 
392             // ----------------------------------------------------------------------
393             // Forking
394             // ----------------------------------------------------------------------
395 
396             BasherForkConfiguration fork = new BasherForkConfiguration();
397 
398             fork.setForkMode(forkMode);
399 
400             processSystemProperties(!fork.isForking());
401 
402             DirectoryScanner directoryScanner = new DirectoryScanner();
403             final List<String> list = project.getTestCompileSourceRoots();
404             for ( String compileSourceRoot : list )
405             {
406                 directoryScanner.setBasedir( compileSourceRoot );
407                 directoryScanner.setIncludes( includes.toArray(new String[includes.size()]) );
408                 directoryScanner.setExcludes( excludes.toArray(new String[excludes.size()]) );
409             }
410 
411             directoryScanner.scan();
412 
413             String[] includedFiles = directoryScanner.getIncludedFiles();
414             basherBooter.setIncludedFiles(includedFiles);
415 
416             if (fork.isForking())
417             {
418                 getLog().info("Initiating forking run");
419                 useSystemClassLoader = useSystemClassLoader == null ? Boolean.TRUE : useSystemClassLoader;
420                 fork.setUseSystemClassLoader(useSystemClassLoader.booleanValue());
421                 fork.setUseManifestOnlyJar(useManifestOnlyJar);
422 
423                 fork.setSystemProperties(systemProperties);
424 
425                 if ("true".equals(debugForkedProcess))
426                 {
427                     debugForkedProcess = "-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005";
428                 }
429 
430                 if ("yourkit".equals(profiler))
431                 {
432                     // FIXME: this should (optionally) use a port number
433                     profiler = "-agentlib:yjpagent=port=10020,onlylocal,quiet,dir=" + reportsDirectory.getAbsolutePath();
434                 }
435                 else if ("none".equals( profiler ))
436                 {
437                     profiler = null;
438                 }
439 
440                 if (profiler != null)
441                 {
442                     fork.setProfileLine( profiler );
443                 }
444 
445                 fork.setDebugLine(debugForkedProcess);
446 
447                 if (jvm == null || "".equals(jvm))
448                 {
449                     // use the same JVM as the one used to run Maven (the "java.home" one)
450                     jvm = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
451                     getLog().debug("Using JVM: " + jvm);
452                 }
453 
454                 fork.setJvmExecutable(jvm);
455 
456                 if (workingDirectory != null)
457                 {
458                     fork.setWorkingDirectory(workingDirectory);
459                 }
460                 else
461                 {
462                     fork.setWorkingDirectory(basedir);
463                 }
464 
465                 fork.setArgLine(argLine);
466 
467                 fork.setEnvironmentVariables(environmentVariables);
468 
469                 if (getLog().isDebugEnabled())
470                 {
471                     // showMap(environmentVariables, "environment variable");
472                     fork.setDebug(true);
473                 }
474 
475                 if (argLine != null)
476                 {
477                     List args = Arrays.asList(argLine.split(" "));
478                     if (args.contains("-da") || args.contains("-disableassertions"))
479                     {
480                         enableAssertions = false;
481                     }
482                 }
483             }
484 
485             basherBooter.setFailIfNoTasks(Boolean.FALSE);
486 
487             basherBooter.setForkedProcessTimeoutInSeconds(processTimeOut);
488 
489             basherBooter.setRedirectTasksOutputToFile(false);
490 
491             basherBooter.setForkConfiguration(fork);
492 
493             basherBooter.setEnableAssertions(enableAssertions);
494 
495             basherBooter.setBasherContexts(basherContexts);
496 
497             basherBooter.setActiveBasherContext(activeBasherContext);
498 
499             basherBooter.setReportsDirectory(reportsDirectory);
500 
501             // basherBooter.setAwaitShutdownEvent(awaitShutdownEvent);
502 
503             // addReporters(surefireBooter, fork.isForking());
504 
505             int result = basherBooter.run();
506 
507             // FIXME: Handle result
508         }
509         catch (Exception e)
510         {
511             e.printStackTrace();
512         }
513 
514     }
515 
516     private Toolchain getToolchain()
517     {
518         Toolchain tc = null;
519         try
520         {
521             if (session != null) //session is null in tests..
522             {
523                 ToolchainManager toolchainManager = (ToolchainManager) session.getContainer().lookup(ToolchainManager.ROLE);
524                 if (toolchainManager != null)
525                 {
526                     tc = toolchainManager.getToolchainFromBuildContext("jdk", session);
527                 }
528             }
529         }
530         catch (ComponentLookupException componentLookupException)
531         {
532             //just ignore, could happen in pre-3.0.9 builds..
533         }
534         return tc;
535     }
536 
537     protected void processSystemProperties(boolean setInSystem)
538     {
539         if (systemProperties == null)
540         {
541             systemProperties = new Properties();
542         }
543 
544         originalSystemProperties = (Properties) System.getProperties().clone();
545 
546         // We used to take all of our system properties and dump them in with the
547         // user specified properties for SUREFIRE-121, causing SUREFIRE-491.
548         // Not gonna do THAT any more... but I'm leaving this code here in case
549         // we need it later when we try to fix SUREFIRE-121 again.
550 
551         // Get the properties from the MavenSession instance to make embedded use work correctly
552         Properties userSpecifiedProperties = (Properties) session.getExecutionProperties().clone();
553         userSpecifiedProperties.putAll(systemProperties);
554         //systemProperties = userSpecifiedProperties;
555 
556         systemProperties.setProperty("basedir", basedir.getAbsolutePath());
557         systemProperties.setProperty("user.dir", workingDirectory.getAbsolutePath());
558 
559         systemProperties.setProperty("localRepository", localRepository.getBasedir());
560 
561         if (setInSystem)
562         {
563             // Add all system properties configured by the user
564             Iterator iter = systemProperties.keySet().iterator();
565 
566             while (iter.hasNext())
567             {
568                 String key = (String) iter.next();
569 
570                 String value = systemProperties.getProperty(key);
571 
572                 System.setProperty(key, value);
573             }
574         }
575     }
576 
577 
578     private void addArtifact(final BasherBooter basherBooter, final Artifact basherArtifact)
579             throws ArtifactNotFoundException, ArtifactResolutionException
580     {
581         ArtifactResolutionResult result = resolveArtifact(null, basherArtifact);
582 
583         for (Iterator i = result.getArtifacts().iterator(); i.hasNext();)
584         {
585             Artifact artifact = (Artifact) i.next();
586 
587             getLog().debug("Adding to basher booter classpath: " + artifact.getFile().getAbsolutePath());
588 
589             basherBooter.addBasherBootClassPathUrl(artifact.getFile().getAbsolutePath());
590         }
591     }
592 
593     private ArtifactResolutionResult resolveArtifact(final Artifact filteredArtifact, Artifact providerArtifact)
594             throws ArtifactResolutionException, ArtifactNotFoundException
595     {
596         ArtifactFilter filter = null;
597         if (filteredArtifact != null)
598         {
599             filter =
600                     new ExcludesArtifactFilter(Collections.singletonList(filteredArtifact.getGroupId() + ":" +
601                             filteredArtifact.getArtifactId()));
602         }
603 
604         Artifact originatingArtifact = artifactFactory.createBuildArtifact("dummy", "dummy", "1.0", "jar");
605 
606         return artifactResolver.resolveTransitively(Collections.singleton(providerArtifact), originatingArtifact,
607                 localRepository, remoteRepositories, metadataSource, filter);
608     }
609 
610 }