View Javadoc

1   package org.apache.maven.reporting.exec;
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.util.ArrayList;
23  import java.util.Arrays;
24  import java.util.Collections;
25  import java.util.HashSet;
26  import java.util.List;
27  import java.util.Set;
28  
29  import org.apache.maven.artifact.repository.RepositoryRequest;
30  import org.apache.maven.execution.MavenSession;
31  import org.apache.maven.lifecycle.LifecycleExecutor;
32  import org.apache.maven.model.Plugin;
33  import org.apache.maven.plugin.MavenPluginManager;
34  import org.apache.maven.plugin.Mojo;
35  import org.apache.maven.plugin.MojoExecution;
36  import org.apache.maven.plugin.MojoExecutionException;
37  import org.apache.maven.plugin.MojoNotFoundException;
38  import org.apache.maven.plugin.PluginConfigurationException;
39  import org.apache.maven.plugin.PluginContainerException;
40  import org.apache.maven.plugin.descriptor.MojoDescriptor;
41  import org.apache.maven.plugin.descriptor.PluginDescriptor;
42  import org.apache.maven.plugin.version.DefaultPluginVersionRequest;
43  import org.apache.maven.plugin.version.PluginVersionRequest;
44  import org.apache.maven.plugin.version.PluginVersionResolutionException;
45  import org.apache.maven.plugin.version.PluginVersionResolver;
46  import org.apache.maven.plugin.version.PluginVersionResult;
47  import org.apache.maven.project.MavenProject;
48  import org.apache.maven.reporting.MavenReport;
49  import org.codehaus.plexus.component.annotations.Component;
50  import org.codehaus.plexus.component.annotations.Requirement;
51  import org.codehaus.plexus.configuration.PlexusConfiguration;
52  import org.codehaus.plexus.logging.Logger;
53  import org.apache.maven.shared.utils.StringUtils;
54  import org.codehaus.plexus.util.xml.Xpp3Dom;
55  import org.codehaus.plexus.util.xml.Xpp3DomUtils;
56  import org.sonatype.aether.RepositorySystemSession;
57  import org.sonatype.aether.graph.DependencyFilter;
58  import org.sonatype.aether.repository.RemoteRepository;
59  
60  /**
61   * <p>
62   *   This component will build some {@link MavenReportExecution} from {@link MavenReportExecutorRequest}.
63   *   If a {@link MavenReport} needs to fork a lifecycle, this fork is executed here. 
64   *   It will ask the core to get some informations in order to correctly setup {@link MavenReport}.
65   * </p>
66   * <p>
67   *   <b>Note</b> if no version is defined in the report plugin, the version will be searched
68   *   with method {@link #resolvePluginVersion(ReportPlugin, MavenReportExecutorRequest)}
69   *   Steps to find a plugin version stop after each step if a non <code>null</code> value has been found:
70   *   <ol>
71   *     <li>use the one defined in the reportPlugin configuration,</li>
72   *     <li>search similar (same groupId and artifactId) mojo in the build/plugins section of the pom,</li>
73   *     <li>search similar (same groupId and artifactId) mojo in the build/pluginManagement section of the pom,</li>
74   *     <li>ask {@link PluginVersionResolver} to get a fallback version and display a warning as it's not a recommended use.</li>  
75   *   </ol>
76   * </p>
77   * <p>
78   *   Following steps are done:
79   *   <ul>
80   *     <li>get {@link PluginDescriptor} from the {@link MavenPluginManager#getPluginDescriptor(Plugin, RepositoryRequest, RepositorySystemSession)}</li>
81   *     <li>setup a {@link ClassLoader} with the Mojo Site plugin {@link ClassLoader} as parent for the report execution. 
82   *       You must note some classes are imported from the current Site Mojo ClassRealm: see {@link #IMPORTS}.
83   *       The artifact resolution excludes the following artifacts, corresponding to imported classes: 
84   *       doxia-site-renderer, doxia-sink-api, maven-reporting-api.
85   *       Done using {@link MavenPluginManager#setupPluginRealm(PluginDescriptor, MavenSession, ClassLoader, List, DependencyFilter)}
86   *     </li>
87   *     <li>
88   *       setup the mojo using {@link MavenPluginManager#getConfiguredMojo(Class, MavenSession, MojoExecution)}
89   *     </li>
90   *     <li>
91   *       verify with {@link LifecycleExecutor#calculateForkedExecutions(MojoExecution, MavenSession)}
92   *       if any forked execution is needed: if yes, executes the forked execution here
93   *     </li>
94   *   </ul>
95   * </p>
96   * @author Olivier Lamy
97   */
98  @Component( role = MavenReportExecutor.class )
99  public class DefaultMavenReportExecutor
100     implements MavenReportExecutor
101 {
102     @Requirement
103     private Logger logger;
104 
105     @Requirement
106     protected MavenPluginManager mavenPluginManager;
107 
108     @Requirement
109     protected MavenPluginManagerHelper mavenPluginManagerHelper;
110 
111     @Requirement
112     protected LifecycleExecutor lifecycleExecutor;
113 
114     @Requirement
115     protected PluginVersionResolver pluginVersionResolver;
116 
117     private static final List<String> IMPORTS = Arrays.asList( "org.apache.maven.reporting.MavenReport",
118                                                                "org.apache.maven.reporting.MavenMultiPageReport",
119                                                                "org.apache.maven.doxia.siterenderer.Renderer",
120                                                                "org.apache.maven.doxia.sink.SinkFactory",
121                                                                "org.codehaus.doxia.sink.Sink",
122                                                                "org.apache.maven.doxia.sink.Sink",
123                                                                "org.apache.maven.doxia.sink.SinkEventAttributes",
124                                                                "org.apache.maven.doxia.logging.LogEnabled",
125                                                                "org.apache.maven.doxia.logging.Log" );
126 
127     private static final List<String> EXCLUDES = Arrays.asList( "doxia-site-renderer", "doxia-sink-api",
128                                                                 "maven-reporting-api" );
129 
130     public List<MavenReportExecution> buildMavenReports( MavenReportExecutorRequest mavenReportExecutorRequest )
131         throws MojoExecutionException
132     {
133         if ( mavenReportExecutorRequest.getReportPlugins() == null )
134         {
135             return Collections.emptyList();
136         }
137         getLog().debug( "DefaultMavenReportExecutor.buildMavenReports()" );
138 
139         Set<String> reportPluginKeys = new HashSet<String>();
140         List<MavenReportExecution> reportExecutions = new ArrayList<MavenReportExecution>();
141 
142         String pluginKey = "";
143         try
144         {
145             for ( ReportPlugin reportPlugin : mavenReportExecutorRequest.getReportPlugins() )
146             {
147                 pluginKey = reportPlugin.getGroupId() + ':' + reportPlugin.getArtifactId();
148 
149                 if ( !reportPluginKeys.add( pluginKey ) )
150                 {
151                     logger.info( "plugin " + pluginKey + " will be executed more than one time" );
152                 }
153 
154                 reportExecutions.addAll( buildReportPlugin( mavenReportExecutorRequest, reportPlugin ) );
155             }
156         }
157         catch ( Exception e )
158         {
159             throw new MojoExecutionException( "failed to get report for " + pluginKey, e );
160         }
161 
162         return reportExecutions;
163     }
164 
165     protected List<MavenReportExecution> buildReportPlugin( MavenReportExecutorRequest mavenReportExecutorRequest,
166                                                             ReportPlugin reportPlugin )
167         throws Exception
168     {
169         Plugin plugin = new Plugin();
170         plugin.setGroupId( reportPlugin.getGroupId() );
171         plugin.setArtifactId( reportPlugin.getArtifactId() );
172         plugin.setVersion( resolvePluginVersion( reportPlugin, mavenReportExecutorRequest ) );
173 
174         mergePluginToReportPlugin( mavenReportExecutorRequest, plugin, reportPlugin );
175 
176         logger.info( "configuring report plugin " + plugin.getId() );
177 
178         MavenSession session = mavenReportExecutorRequest.getMavenSession();
179         List<RemoteRepository> remoteRepositories = session.getCurrentProject().getRemotePluginRepositories();
180 
181         PluginDescriptor pluginDescriptor =
182             mavenPluginManagerHelper.getPluginDescriptor( plugin, remoteRepositories, session );
183 
184         List<GoalWithConf> goalsWithConfiguration = new ArrayList<GoalWithConf>();
185 
186         boolean userDefinedReports = true;
187         if ( reportPlugin.getReportSets().isEmpty() && reportPlugin.getReports().isEmpty() )
188         {
189             // by default, use every goal, which will be filtered later to only keep reporting goals
190             userDefinedReports = false;
191             List<MojoDescriptor> mojoDescriptors = pluginDescriptor.getMojos();
192             for ( MojoDescriptor mojoDescriptor : mojoDescriptors )
193             {
194                 goalsWithConfiguration.add( new GoalWithConf( mojoDescriptor.getGoal(),
195                                                               mojoDescriptor.getConfiguration() ) );
196             }
197         }
198         else
199         {
200             Set<String> goals = new HashSet<String>();
201             for ( String report : reportPlugin.getReports() )
202             {
203                 if ( goals.add( report ) )
204                 {
205                     goalsWithConfiguration.add( new GoalWithConf( report, reportPlugin.getConfiguration() ) );
206                 }
207                 else
208                 {
209                     logger.warn( report + " report is declared twice in default reports" );
210                 }
211             }
212 
213             for ( ReportSet reportSet : reportPlugin.getReportSets() )
214             {
215                 goals = new HashSet<String>();
216                 for ( String report : reportSet.getReports() )
217                 {
218                     if ( goals.add( report ) )
219                     {
220                         goalsWithConfiguration.add( new GoalWithConf( report, reportSet.getConfiguration() ) );
221                     }
222                     else
223                     {
224                         logger.warn( report + " report is declared twice in " + reportSet.getId() + " reportSet" );
225                     }
226                 }
227             }
228         }
229 
230         List<MavenReportExecution> reports = new ArrayList<MavenReportExecution>();
231         for ( GoalWithConf report : goalsWithConfiguration )
232         {
233             MojoDescriptor mojoDescriptor = pluginDescriptor.getMojo( report.getGoal() );
234             if ( mojoDescriptor == null )
235             {
236                 throw new MojoNotFoundException( report.getGoal(), pluginDescriptor );
237             }
238 
239             MojoExecution mojoExecution = new MojoExecution( plugin, report.getGoal(), "report:" + report.getGoal() );
240 
241             mojoExecution.setMojoDescriptor( mojoDescriptor );
242 
243             mavenPluginManagerHelper.setupPluginRealm( pluginDescriptor, mavenReportExecutorRequest.getMavenSession(),
244                                                        Thread.currentThread().getContextClassLoader(), IMPORTS,
245                                                        EXCLUDES );
246 
247             if ( !isMavenReport( mojoExecution, pluginDescriptor ) )
248             {
249                 if ( userDefinedReports )
250                 {
251                     // reports were explicitly written in the POM
252                     logger.warn( "ignoring " + mojoExecution.getPlugin().getId() + ':' + report.getGoal()
253                         + " goal since it is not a report: should be removed from reporting configuration in POM" );
254                 }
255                 continue;
256             }
257 
258             mojoExecution.setConfiguration( mergeConfiguration( mojoDescriptor.getMojoConfiguration(),
259                                                                 reportPlugin.getConfiguration(),
260                                                                 report.getConfiguration(),
261                                                                 mojoDescriptor.getParameterMap().keySet() ) );
262 
263             MavenReport mavenReport =
264                             getConfiguredMavenReport( mojoExecution, pluginDescriptor, mavenReportExecutorRequest );
265 
266             MavenReportExecution mavenReportExecution =
267                 new MavenReportExecution( mojoExecution.getPlugin(), mavenReport, pluginDescriptor.getClassRealm() );
268 
269             lifecycleExecutor.calculateForkedExecutions( mojoExecution, mavenReportExecutorRequest.getMavenSession() );
270 
271             if ( !mojoExecution.getForkedExecutions().isEmpty() )
272             {
273                 lifecycleExecutor.executeForkedExecutions( mojoExecution, mavenReportExecutorRequest.getMavenSession() );
274             }
275 
276             if ( canGenerateReport( mavenReport, mojoExecution ) )
277             {
278                 reports.add( mavenReportExecution );
279             }
280         }
281 
282         return reports;
283     }
284 
285     private boolean canGenerateReport( MavenReport mavenReport, MojoExecution mojoExecution )
286     {
287         ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
288         try
289         {
290             Thread.currentThread().setContextClassLoader( mojoExecution.getMojoDescriptor().getRealm() );
291 
292             return mavenReport.canGenerateReport();
293         }
294         finally
295         {
296             Thread.currentThread().setContextClassLoader( originalClassLoader );
297         } 
298     }
299 
300     private MavenReport getConfiguredMavenReport( MojoExecution mojoExecution, PluginDescriptor pluginDescriptor,
301                                                   MavenReportExecutorRequest mavenReportExecutorRequest )
302         throws PluginContainerException, PluginConfigurationException
303     {
304         try
305         {
306             Mojo mojo = mavenPluginManager.getConfiguredMojo( Mojo.class,
307                                                               mavenReportExecutorRequest.getMavenSession(),
308                                                               mojoExecution );
309 
310             return (MavenReport) mojo;
311         }
312         catch ( ClassCastException e )
313         {
314             getLog().warn( "skip ClassCastException " + e.getMessage() );
315             return null;
316         }
317         catch ( PluginContainerException e )
318         {
319             /**
320              * ignore old plugin which are using removed PluginRegistry
321              * [INFO] Caused by: java.lang.NoClassDefFoundError: org/apache/maven/plugin/registry/PluginRegistry
322              */
323             if ( e.getCause() != null && e.getCause() instanceof NoClassDefFoundError
324                 && e.getMessage().contains( "PluginRegistry" ) )
325             {
326                 getLog().warn( "skip NoClassDefFoundError with PluginRegistry " );
327                 // too noisy, only in debug mode + e.getMessage() );
328                 if ( getLog().isDebugEnabled() )
329                 {
330                     getLog().debug( e.getMessage(), e );
331                 }
332                 return null;
333             }
334             throw e;
335         }
336     }
337 
338     private boolean isMavenReport( MojoExecution mojoExecution, PluginDescriptor pluginDescriptor )
339     {
340         ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
341         Class<?> mojoClass;
342         Thread.currentThread().setContextClassLoader( mojoExecution.getMojoDescriptor().getRealm() );
343         try
344         {
345             mojoClass =
346                 pluginDescriptor.getClassRealm().loadClass( mojoExecution.getMojoDescriptor().getImplementation() );
347         }
348         catch ( ClassNotFoundException e )
349         {
350             getLog().warn( "skip ClassNotFoundException mojoExecution.goal '" + mojoExecution.getGoal() + "': "
351                                + e.getMessage(), e );
352             return false;
353         }
354         finally
355         {
356             Thread.currentThread().setContextClassLoader( originalClassLoader );
357         }       
358 
359         try
360         {
361             Thread.currentThread().setContextClassLoader( mojoExecution.getMojoDescriptor().getRealm() );
362             MojoDescriptor mojoDescriptor = pluginDescriptor.getMojo( mojoExecution.getGoal() );
363 
364             boolean isMavenReport = MavenReport.class.isAssignableFrom( mojoClass );
365 
366             if ( getLog().isDebugEnabled() )
367             {
368                 if ( mojoDescriptor != null && mojoDescriptor.getImplementationClass() != null )
369                 {
370                     getLog().debug( "class " + mojoDescriptor.getImplementationClass().getName() + " isMavenReport: "
371                                         + isMavenReport );
372                 }
373 
374                 if ( !isMavenReport )
375                 {
376                     getLog().debug( "skip non MavenReport " + mojoExecution.getMojoDescriptor().getId() );
377                 }
378             }
379 
380             return isMavenReport;
381         }
382         catch ( LinkageError e )
383         {
384             getLog().warn( "skip LinkageError mojoExecution.goal '" + mojoExecution.getGoal() + "': " + e.getMessage(),
385                            e );
386             return false;
387         }
388         finally
389         {
390             Thread.currentThread().setContextClassLoader( originalClassLoader );
391         }
392     }
393 
394     private Xpp3Dom mergeConfiguration( PlexusConfiguration mojoConf, PlexusConfiguration pluginConf,
395                                         PlexusConfiguration reportSetConf, Set<String> parameters )
396     {
397         Xpp3Dom mojoConfig = ( mojoConf != null ) ? convert( mojoConf ) : new Xpp3Dom( "configuration" );
398 
399         if ( pluginConf != null || reportSetConf != null )
400         {
401             Xpp3Dom reportConfiguration = pluginConf == null ? new Xpp3Dom( "fake" ) : convert( pluginConf );
402 
403             // MSITE-512 configuration from ReportSet must win
404             Xpp3Dom mergedConfigurationWithReportSet =
405                 Xpp3DomUtils.mergeXpp3Dom( convert( reportSetConf ), reportConfiguration );
406 
407             Xpp3Dom mergedConfiguration = Xpp3DomUtils.mergeXpp3Dom( mergedConfigurationWithReportSet, mojoConfig );
408 
409             Xpp3Dom cleanedConfiguration = new Xpp3Dom( "configuration" );
410             if ( mergedConfiguration.getChildren() != null )
411             {
412                 for ( Xpp3Dom parameter : mergedConfiguration.getChildren() )
413                 {
414                     if ( parameters.contains( parameter.getName() ) )
415                     {
416                         cleanedConfiguration.addChild( parameter );
417                     }
418                 }
419             }
420             if ( getLog().isDebugEnabled() )
421             {
422                 getLog().debug( "mojoExecution mergedConfiguration: " + mergedConfiguration );
423                 getLog().debug( "mojoExecution cleanedConfiguration: " + cleanedConfiguration );
424             }
425 
426             mojoConfig = cleanedConfiguration;
427         }
428 
429         return mojoConfig;
430     }
431 
432     private Xpp3Dom convert( PlexusConfiguration config )
433     {
434         if ( config == null )
435         {
436             return null;
437         }
438 
439         Xpp3Dom dom = new Xpp3Dom( config.getName() );
440         dom.setValue( config.getValue( null ) );
441 
442         for ( String attrib : config.getAttributeNames() )
443         {
444             dom.setAttribute( attrib, config.getAttribute( attrib, null ) );
445         }
446 
447         for ( int n = config.getChildCount(), i = 0; i < n; i++ )
448         {
449             dom.addChild( convert( config.getChild( i ) ) );
450         }
451 
452         return dom;
453     }
454 
455     private Logger getLog()
456     {
457         return logger;
458     }
459 
460     /**
461      * Resolve report plugin version. 
462      * Steps to find a plugin version stop after each step if a non <code>null</code> value has been found:
463      * <ol>
464      *   <li>use the one defined in the reportPlugin configuration,</li>
465      *   <li>search similar (same groupId and artifactId) mojo in the build/plugins section of the pom,</li>
466      *   <li>search similar (same groupId and artifactId) mojo in the build/pluginManagement section of the pom,</li>
467      *   <li>ask {@link PluginVersionResolver} to get a fallback version and display a warning as it's not a recommended use.</li>  
468      * </ol>
469      *
470      * @param reportPlugin the report plugin to resolve the version
471      * @param mavenReportExecutorRequest the current report execution context
472      * @return the report plugin version
473      * @throws PluginVersionResolutionException
474      */
475     protected String resolvePluginVersion( ReportPlugin reportPlugin, MavenReportExecutorRequest mavenReportExecutorRequest )
476         throws PluginVersionResolutionException
477     {
478         String reportPluginKey = reportPlugin.getGroupId() + ':' + reportPlugin.getArtifactId();
479         if ( getLog().isDebugEnabled() )
480         {
481             getLog().debug( "resolving version for " + reportPluginKey );
482         }
483 
484         // look for version defined in the reportPlugin configuration
485         if ( reportPlugin.getVersion() != null )
486         {
487             if ( getLog().isDebugEnabled() )
488             {
489                 logger.debug( "resolved " + reportPluginKey + " version from the reporting.plugins section: "
490                     + reportPlugin.getVersion() );
491             }
492             return reportPlugin.getVersion();
493         }
494 
495         MavenProject project = mavenReportExecutorRequest.getProject();
496 
497         // search in the build section
498         if ( project.getBuild() != null )
499         {
500             Plugin plugin = find( reportPlugin, project.getBuild().getPlugins() );
501 
502             if ( plugin != null && plugin.getVersion() != null )
503             {
504                 if ( getLog().isDebugEnabled() )
505                 {
506                     logger.debug( "resolved " + reportPluginKey + " version from the build.plugins section: "
507                         + plugin.getVersion() );
508                 }
509                 return plugin.getVersion();
510             }
511         }
512 
513         // search in pluginManagement section
514         if ( project.getBuild() != null && project.getBuild().getPluginManagement() != null )
515         {
516             Plugin plugin = find( reportPlugin, project.getBuild().getPluginManagement().getPlugins() );
517 
518             if ( plugin != null && plugin.getVersion() != null )
519             {
520                 if ( getLog().isDebugEnabled() )
521                 {
522                     logger.debug( "resolved " + reportPluginKey
523                         + " version from the build.pluginManagement.plugins section: " + plugin.getVersion() );
524                 }
525                 return plugin.getVersion();
526             }
527         }
528 
529 
530         logger.warn( "Report plugin " + reportPluginKey + " has an empty version." );
531         logger.warn( "" );
532         logger.warn( "It is highly recommended to fix these problems"
533             + " because they threaten the stability of your build." );
534         logger.warn( "" );
535         logger.warn( "For this reason, future Maven versions might no"
536             + " longer support building such malformed projects." );
537 
538         Plugin plugin = new Plugin();
539         plugin.setGroupId( reportPlugin.getGroupId() );
540         plugin.setArtifactId( reportPlugin.getArtifactId() );
541         
542         PluginVersionRequest pluginVersionRequest =
543             new DefaultPluginVersionRequest( plugin, mavenReportExecutorRequest.getMavenSession() );
544 
545         PluginVersionResult result = pluginVersionResolver.resolve( pluginVersionRequest );
546         if ( getLog().isDebugEnabled() )
547         {
548             getLog().debug( "resolved " + reportPluginKey + " version from repository: " + result.getVersion() );
549         }
550         return result.getVersion();
551     }
552 
553     /**
554      * Search similar (same groupId and artifactId) mojo as a given report plugin.
555      * 
556      * @param reportPlugin the report plugin to search for a similar mojo
557      * @param plugins the candidate mojos 
558      * @return the first similar mojo
559      */
560     private Plugin find( ReportPlugin reportPlugin, List<Plugin> plugins )
561     {
562         if ( plugins == null )
563         {
564             return null;
565         }
566         for ( Plugin plugin : plugins )
567         {
568             if ( StringUtils.equals( plugin.getArtifactId(), reportPlugin.getArtifactId() )
569                 && StringUtils.equals( plugin.getGroupId(), reportPlugin.getGroupId() ) )
570             {
571                 return plugin;
572             }
573         }
574         return null;
575     }
576 
577     /**
578      * TODO other stuff to merge ?
579      * <p>
580      * this method will "merge" some part of the plugin declaration existing in the build section 
581      * to the fake plugin build for report execution:
582      * <ul>
583      *   <li>dependencies</li>
584      * </ul>
585      * </p>
586      * @param mavenReportExecutorRequest
587      * @param buildPlugin
588      * @param reportPlugin
589      */
590     private void mergePluginToReportPlugin( MavenReportExecutorRequest mavenReportExecutorRequest, Plugin buildPlugin,
591                                             ReportPlugin reportPlugin )
592     {
593         Plugin configuredPlugin = find( reportPlugin, mavenReportExecutorRequest.getProject().getBuild().getPlugins() );
594         if ( configuredPlugin != null )
595         {
596             if ( !configuredPlugin.getDependencies().isEmpty() )
597             {
598                 buildPlugin.getDependencies().addAll( configuredPlugin.getDependencies() );
599             }
600         }
601     }
602 
603     private static class GoalWithConf
604     {
605         private final String goal;
606         private final PlexusConfiguration configuration;
607 
608         public GoalWithConf( String goal, PlexusConfiguration configuration )
609         {
610             this.goal = goal;
611             this.configuration = configuration;
612         }
613 
614         public String getGoal()
615         {
616             return goal;
617         }
618 
619         public PlexusConfiguration getConfiguration()
620         {
621             return configuration;
622         }
623     }
624 }