1 package org.codehaus.mojo.fitnesse;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 import java.io.File;
18 import java.io.FileInputStream;
19 import java.io.FileNotFoundException;
20 import java.io.FileWriter;
21 import java.io.IOException;
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Set;
27 import java.util.StringTokenizer;
28 import java.util.logging.Level;
29
30 import org.apache.maven.artifact.Artifact;
31 import org.apache.maven.artifact.factory.ArtifactFactory;
32 import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
33 import org.apache.maven.artifact.repository.ArtifactRepository;
34 import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
35 import org.apache.maven.artifact.resolver.ArtifactResolutionException;
36 import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
37 import org.apache.maven.artifact.resolver.ArtifactResolver;
38 import org.apache.maven.plugin.MojoExecutionException;
39 import org.apache.maven.plugin.MojoFailureException;
40 import org.apache.maven.project.MavenProject;
41 import org.apache.maven.project.artifact.InvalidDependencyVersionException;
42 import org.apache.maven.project.artifact.MavenMetadataSource;
43 import org.apache.maven.reporting.MavenReportException;
44 import org.codehaus.mojo.fitnesse.log.FileConsumer;
45 import org.codehaus.mojo.fitnesse.log.FitnesseStreamConsumer;
46 import org.codehaus.mojo.fitnesse.log.LogConsumer;
47 import org.codehaus.mojo.fitnesse.log.MultipleConsumer;
48 import org.codehaus.mojo.fitnesse.plexus.FCommandLineException;
49 import org.codehaus.mojo.fitnesse.plexus.FCommandLineUtils;
50 import org.codehaus.mojo.fitnesse.plexus.FCommandline;
51 import org.codehaus.mojo.fitnesse.runner.ClassPathBuilder;
52
53
54
55
56
57
58
59
60
61
62 public class FitnesseRunnerMojo
63 extends FitnesseAbstractMojo
64 {
65
66
67
68
69
70
71
72
73 private String classPathProvider;
74
75
76
77
78
79 private ArtifactMetadataSource metadataSource;
80
81
82
83
84
85 private List remoteRepositories;
86
87
88
89
90
91
92
93
94
95
96
97 private MavenProject project;
98
99
100
101
102
103
104
105
106
107
108
109
110
111 boolean copyDependencies;
112
113
114
115
116
117
118
119
120
121
122
123 private List pluginArtifacts;
124
125
126
127
128
129
130
131
132 private java.util.List dependencies;
133
134
135
136
137
138
139 private ArtifactFactory artifactFactory;
140
141
142
143
144
145
146
147
148 private ArtifactResolver artifactResolver;
149
150
151
152
153
154
155
156
157 private ArtifactRepository localRepository;
158
159
160
161
162
163
164 private String pluginArtifactId;
165
166
167
168
169
170
171 private String pluginGroupId;
172
173
174
175
176
177
178 private String pluginVersion;
179
180
181
182
183
184
185 private String jdk;
186
187
188
189
190
191
192 private String jdkOpts;
193
194
195
196
197
198
199 private boolean displayOutput;
200
201
202
203
204
205
206 private boolean debug;
207
208
209
210
211
212
213 private String fitnesseRunnerClass;
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232 private List classPathSubstitutions = new ArrayList();
233
234
235
236
237 private FCommandline mCmd = new FCommandline();
238
239
240 private Artifact pluginArtifact;
241
242
243
244
245
246
247
248 public void execute()
249 throws MojoExecutionException, MojoFailureException
250 {
251 new File( this.workingDir ).mkdirs();
252 checkConfiguration();
253
254 try
255 {
256 FitnesseReportMojo.copyAllResources( new File( this.workingDir ), getLog(), getClass().getClassLoader() );
257 }
258 catch ( MavenReportException e )
259 {
260 throw new MojoExecutionException( "Unable to copy resources", e.getCause() );
261 }
262
263 getLog().info( "Found " + getFitnesseSize() + " Fitnesse configuration." );
264 for ( int i = 0; i < getFitnesseSize(); i++ )
265 {
266 Fitnesse tServer = getFitnesse( i );
267 callFitnesse( tServer );
268 transformResultPage( tServer );
269 }
270 }
271
272
273
274
275
276
277
278 void transformResultPage( Fitnesse pServer )
279 throws MojoExecutionException
280 {
281 FileInputStream tIn = null;
282 try
283 {
284 String tSrcFile = getTmpFileName( pServer );
285 tIn = new FileInputStream( tSrcFile );
286 File tDestFile = new File( getFinalFileName( pServer ) );
287 if ( tDestFile.exists() )
288 {
289 tDestFile.delete();
290 }
291 FileWriter tWriter = new FileWriter( tDestFile );
292 FitnessePage tResult = new FitnessePage( new File( tSrcFile ) );
293 transformHtml( tIn, tWriter, getOutputUrl( pServer ), tResult.getStatus() );
294 tIn.close();
295 tIn = null;
296 if ( !new File( tSrcFile ).delete() )
297 {
298 getLog().error( "Unable to delete tmp file [" + tSrcFile + "]" );
299 }
300 }
301 catch ( FileNotFoundException e )
302 {
303 throw new MojoExecutionException( "Unable to tranform html", e );
304 }
305 catch ( IOException e )
306 {
307 throw new MojoExecutionException( "Unable to tranform html", e );
308 }
309 finally
310 {
311 if ( tIn != null )
312 {
313 try
314 {
315 tIn.close();
316 }
317 catch ( IOException e )
318 {
319 throw new MojoExecutionException( "Unable to close tmp file stream", e );
320 }
321 }
322 }
323
324 }
325
326
327
328
329
330
331
332
333
334
335 private String getClassPath( Fitnesse tServer )
336 throws MojoExecutionException
337 {
338 String tResult;
339 if ( "fitnesse".equals( classPathProvider ) )
340 {
341 StringBuffer tBuffer = new StringBuffer();
342 ClassPathBuilder tBuilder =
343 new ClassPathBuilder( tServer.getHostName(), tServer.getPort(), tServer.getPageName(), getLog() );
344 tBuffer.append( tBuilder.getPath( classPathSubstitutions, getLog() ) );
345 Artifact curArt;
346 for ( Iterator tIt = pluginArtifacts.iterator(); tIt.hasNext(); )
347 {
348 curArt = (Artifact) tIt.next();
349 if ( !curArt.getScope().equals( Artifact.SCOPE_PROVIDED )
350 && !curArt.getScope().equals( Artifact.SCOPE_TEST ) )
351 {
352 tBuffer.append( File.pathSeparatorChar ).append( curArt.getFile().getAbsolutePath() );
353 }
354 }
355 tBuffer.append( File.pathSeparatorChar ).append( resolvePlugin().getFile().getAbsolutePath() );
356 getLog().info( "Try to download classpath from FitNesse server..." );
357 tResult = tBuffer.toString();
358 }
359 else
360 {
361 tResult = getMavenClassPath();
362 }
363 if ( copyDependencies )
364 {
365 tResult = copyDependenciesLocally( tResult );
366 }
367
368 return tResult;
369 }
370
371
372
373
374
375
376
377
378
379
380
381 String copyDependenciesLocally( String pClasspath )
382 throws MojoExecutionException
383 {
384 String tPathSep = System.getProperty( "path.separator" );
385 String tFileSep = System.getProperty( "file.separator" );
386 File tFolder = new File( workingDir + tFileSep + "lib" );
387 if ( !tFolder.exists() )
388 {
389 tFolder.mkdirs();
390 }
391 StringTokenizer tToken = new StringTokenizer( pClasspath, tPathSep );
392 String tFileName, tShortFileName;
393 File tFile;
394 FileInputStream tFileInput;
395 StringBuffer tBuffer = new StringBuffer();
396 File tResultFile;
397 try
398 {
399 while ( tToken.hasMoreTokens() )
400 {
401 tFileName = tToken.nextToken();
402 tFile = new File( tFileName );
403 if ( tFile.exists() )
404 {
405 tFileInput = new FileInputStream( tFile );
406 int tIndex = tFileName.lastIndexOf( tFileSep );
407 tShortFileName = tFileName.substring( tIndex + 1 );
408 tResultFile = new File( workingDir + tFileSep + "lib" + tFileSep + tShortFileName );
409 FitnesseReportMojo.copyFile( getLog(), tFileInput, tResultFile );
410 tBuffer.append( "lib" + tFileSep + tShortFileName + tPathSep );
411 }
412 else
413 {
414 getLog().warn( "Unable to find the file [" + tFileName + "], skipping this file" );
415 }
416 }
417 }
418 catch ( FileNotFoundException e )
419 {
420 throw new MojoExecutionException( "File not found", e );
421 }
422 catch ( MavenReportException e )
423 {
424 throw new MojoExecutionException( "File not found", e );
425 }
426 return tBuffer.toString();
427 }
428
429
430
431
432
433
434
435 String getMavenClassPath()
436 throws MojoExecutionException
437 {
438 getLog().error( "PKE MavenClassPath" );
439 StringBuffer tBuffer = new StringBuffer();
440 Set tArtifacts = transitivelyResolvePomDependencies();
441 if ( tArtifacts != null && !tArtifacts.isEmpty() )
442 {
443 for ( Iterator it = tArtifacts.iterator(); it.hasNext(); )
444 {
445 Artifact curArtififact = (Artifact) it.next();
446 tBuffer.append( curArtififact.getFile().getAbsolutePath() ).append( File.pathSeparatorChar );
447 }
448 }
449
450 tBuffer.append( project.getBuild().getOutputDirectory() ).append( File.pathSeparatorChar );
451 tBuffer.append( project.getBuild().getTestOutputDirectory() ).append( File.pathSeparatorChar );
452 getLog().error( "PKE " + tBuffer );
453 return tBuffer.toString();
454 }
455
456
457
458
459
460
461
462 public Set transitivelyResolvePomDependencies()
463 throws MojoExecutionException
464 {
465
466 Set dependencyArtifacts;
467 try
468 {
469 dependencyArtifacts = MavenMetadataSource.createArtifacts( artifactFactory, dependencies, null, null, null );
470 }
471 catch ( InvalidDependencyVersionException e )
472 {
473 throw new MojoExecutionException( "Invalid dependency", e );
474 }
475
476
477 dependencyArtifacts.add( project.getArtifact() );
478
479 List listeners = Collections.EMPTY_LIST;
480
481
482
483 ArtifactResolutionResult result;
484 try
485 {
486 result =
487 artifactResolver.resolveTransitively( dependencyArtifacts, project.getArtifact(),
488 Collections.EMPTY_MAP, localRepository, remoteRepositories,
489 metadataSource, null, listeners );
490 }
491 catch ( ArtifactResolutionException e )
492 {
493 throw new MojoExecutionException( "Unable to resolve Artifact.", e );
494 }
495 catch ( ArtifactNotFoundException e )
496 {
497 throw new MojoExecutionException( "Unable to resolve Artifact.", e );
498 }
499
500 return result.getArtifacts();
501 }
502
503
504
505
506
507
508
509
510 void callFitnesse( Fitnesse pServer )
511 throws MojoFailureException, MojoExecutionException
512 {
513 getLog().info( "Call result of the server," + pServer );
514 FCommandline tCmd = prepareCommandLine( pServer, getClassPath( pServer ) );
515 executeCommand( pServer, tCmd );
516 }
517
518
519
520
521
522
523
524
525
526 void executeCommand( Fitnesse pServer, FCommandline pCmd )
527 throws MojoFailureException, MojoExecutionException
528 {
529 FitnesseStreamConsumer tInfoConsumer = null;
530 int tResult;
531 try
532 {
533 tInfoConsumer = getStandardConsumer( pServer );
534 tResult = FCommandLineUtils.executeCommandLine( pCmd, tInfoConsumer, getErrorConsumer( tInfoConsumer ) );
535 }
536 catch ( FCommandLineException e )
537 {
538 getLog().error( "Unable to start fitnesse [" + pCmd.toString() + "]", e );
539 throw new MojoExecutionException( "Unable to start fitnesse [" + pCmd.toString() + "]", e );
540 }
541 finally
542 {
543 if ( tInfoConsumer != null )
544 {
545 closeConsumer( tInfoConsumer );
546 }
547 }
548 if ( tResult != 0 )
549 {
550 if ( tInfoConsumer.hasGeneratedResultFile() )
551 {
552 if ( isFailOnError() )
553 {
554 throw new MojoFailureException( "Fitnesse command ended with errors, exit code:" + tResult );
555 }
556 else
557 {
558 getLog().info(
559 "Fitnesse command ended with errors, exit code:" + tResult
560 + ", but failOnError is configure to \"false\""
561 + " change your configuration if you want to fail your build" );
562 }
563 }
564 else
565 {
566 throw new MojoExecutionException( "Unable to run Fitnesse, exit code [" + tResult + "]" );
567 }
568 }
569 getLog().info( "Fitnesse invocation ended with result code [" + tResult + "]" );
570 }
571
572
573
574
575
576
577 private void closeConsumer( FitnesseStreamConsumer pInfoConsumer )
578 {
579 if ( pInfoConsumer instanceof MultipleConsumer )
580 {
581 ( (MultipleConsumer) pInfoConsumer ).getFileConsumer().close();
582 }
583 else
584 {
585 ( (FileConsumer) pInfoConsumer ).close();
586 }
587
588 }
589
590
591
592
593
594
595
596 FitnesseStreamConsumer getErrorConsumer( FitnesseStreamConsumer pConsumer )
597 {
598
599 if ( displayOutput )
600 {
601 MultipleConsumer tMultiConsumer = (MultipleConsumer) pConsumer;
602 return new MultipleConsumer( new LogConsumer( getLog(), Level.SEVERE ), tMultiConsumer.getFileConsumer() );
603 }
604 else
605 {
606 return (FileConsumer) pConsumer;
607 }
608 }
609
610
611
612
613
614
615
616 FitnesseStreamConsumer getStandardConsumer( Fitnesse pServer )
617 {
618 File tOutputFile = new File( getOutputFileName( pServer ) );
619 if ( tOutputFile.exists() )
620 {
621 tOutputFile.delete();
622 }
623 FileConsumer tFileConsumer = new FileConsumer( tOutputFile );
624
625 if ( displayOutput )
626 {
627 return new MultipleConsumer( new LogConsumer( getLog(), Level.INFO ), tFileConsumer );
628 }
629 else
630 {
631 return tFileConsumer;
632 }
633 }
634
635
636
637
638
639 void checkConfiguration()
640 throws MojoExecutionException
641 {
642 super.checkConfiguration();
643 try
644 {
645 Class tClass = Class.forName( fitnesseRunnerClass );
646 tClass.getMethod( "main", new Class[] { String[].class } );
647 }
648 catch ( ClassNotFoundException e )
649 {
650 throw new MojoExecutionException( "The class [" + fitnesseRunnerClass
651 + "] could not be found, check your maven-fitnesse-plugin configuration and the plugin documentation." );
652 }
653 catch ( SecurityException e )
654 {
655 throw new MojoExecutionException( "The class [" + fitnesseRunnerClass
656 + "] doesn't have a \"main\" accessible method.", e );
657 }
658 catch ( NoSuchMethodException e )
659 {
660 throw new MojoExecutionException( "The class [" + fitnesseRunnerClass
661 + "] doesn't have a \"main\" accessible method.", e );
662 }
663 if ( ( !"fitnesse".equals( classPathProvider ) ) && ( !"maven".equals( classPathProvider ) ) )
664 {
665 throw new MojoExecutionException( "classPathProvider accepts only \"fitnesse\" ou \"maven\" values. ["
666 + classPathProvider + "] is not valid." );
667 }
668 }
669
670
671
672
673
674
675
676
677 FCommandline prepareCommandLine( Fitnesse pServer, String pClassPath )
678 {
679 mCmd.clear();
680
681 mCmd.setExecutable( jdk );
682 if ( jdkOpts != null && jdkOpts.length() > 0 )
683 {
684 StringTokenizer tTok = new StringTokenizer( jdkOpts, " " );
685 while ( tTok.hasMoreTokens() )
686 {
687 mCmd.createArgument().setValue( tTok.nextToken() );
688 }
689 }
690 mCmd.createArgument().setValue( "-cp" );
691 mCmd.createArgument().setValue( pClassPath );
692 mCmd.createArgument().setValue( fitnesseRunnerClass );
693 mCmd.createArgument().setValue( "-v" );
694 if ( debug )
695 {
696 mCmd.createArgument().setValue( "-debug" );
697 }
698 mCmd.createArgument().setValue( "-html" );
699 String tFileName = getTmpFileName( pServer );
700 File tFile = new File( tFileName );
701 if ( tFile.exists() )
702 {
703 tFile.delete();
704 }
705 mCmd.createArgument().setValue( tFileName );
706 mCmd.createArgument().setValue( "-nopath" );
707 mCmd.createArgument().setValue( pServer.getHostName() );
708
709 mCmd.createArgument().setValue( "" + pServer.getPort() );
710 mCmd.createArgument().setValue( pServer.getPageName() );
711 mCmd.setWorkingDirectory( workingDir );
712 getLog().info( "Execute =" + mCmd.toString() );
713 getLog().info( "From =" + mCmd.getWorkingDirectory() );
714 return mCmd;
715 }
716
717
718
719
720
721
722 public void setFitnesseRunnerClass( String pFitnesseRunnerClass )
723 {
724 fitnesseRunnerClass = pFitnesseRunnerClass;
725 }
726
727
728
729
730
731
732 public void setJdk( String pJdk )
733 {
734 jdk = pJdk;
735 }
736
737
738
739
740
741
742 public void setWorkingDir( String pWorkingDir )
743 {
744 workingDir = pWorkingDir;
745 }
746
747
748
749
750
751
752 void setDebug( boolean pDebug )
753 {
754 debug = pDebug;
755 }
756
757
758
759
760
761
762 void setCmd( FCommandline pCmd )
763 {
764 this.mCmd = pCmd;
765 }
766
767
768
769
770
771
772 void setJdkOpts( String jdkOpts )
773 {
774 this.jdkOpts = jdkOpts;
775 }
776
777
778
779
780
781
782 public void setPluginArtifacts( List pluginArtifacts )
783 {
784 this.pluginArtifacts = pluginArtifacts;
785 }
786
787
788
789
790
791
792
793 public Artifact resolvePlugin()
794 throws MojoExecutionException
795 {
796 if ( pluginArtifact == null )
797 {
798 Artifact tPluginArtifact =
799 this.artifactFactory.createArtifactWithClassifier( pluginGroupId, pluginArtifactId, pluginVersion,
800 "maven-plugin", "" );
801 try
802 {
803 this.artifactResolver.resolve( tPluginArtifact, new ArrayList(), localRepository );
804 }
805 catch ( ArtifactResolutionException e )
806 {
807 throw new MojoExecutionException( "Unable to resolve artificat", e );
808 }
809 catch ( ArtifactNotFoundException e )
810 {
811 throw new MojoExecutionException( "Unable to resolve artificat", e );
812 }
813 return tPluginArtifact;
814 }
815 else
816 {
817 return pluginArtifact;
818 }
819 }
820
821
822
823
824
825
826
827
828 void setPluginArtifactInfo( String pGroupId, String pArtifactId, String pVersion )
829 {
830 pluginGroupId = pGroupId;
831 pluginArtifactId = pArtifactId;
832 pluginVersion = pVersion;
833 }
834
835
836
837
838 public void setPluginArtifact( Artifact pArtifact )
839 {
840 this.pluginArtifact = pArtifact;
841 }
842
843 public void setClassPathSubstitions( List classPathSubstitions )
844 {
845 this.classPathSubstitutions = classPathSubstitions;
846 }
847
848 public void setClassPathProvider( String classPathProvider )
849 {
850 this.classPathProvider = classPathProvider;
851 }
852
853 public void setDisplayOutput( boolean displayOutput )
854 {
855 this.displayOutput = displayOutput;
856 }
857
858 String getOutputFileName( Fitnesse pServer )
859 {
860 return getResultFileName( pServer, FitnesseAbstractMojo.OUTPUT_EXTENSION, "txt" );
861 }
862
863 String getOutputUrl( Fitnesse pServer )
864 {
865 return FITNESSE_RESULT_PREFIX + "_" + pServer.getHostName() + "_" + pServer.getPageName() + "_output.txt";
866 }
867
868 public void setCopyDependencies( boolean copyDependencies )
869 {
870 this.copyDependencies = copyDependencies;
871 }
872
873 }