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>**/Task*.java **/*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>**/*$*</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 }