View Javadoc

1   /*
2    *   Copyright (C) Christian Schulte, 2005-206
3    *   All rights reserved.
4    *
5    *   Redistribution and use in source and binary forms, with or without
6    *   modification, are permitted provided that the following conditions
7    *   are met:
8    *
9    *     o Redistributions of source code must retain the above copyright
10   *       notice, this list of conditions and the following disclaimer.
11   *
12   *     o Redistributions in binary form must reproduce the above copyright
13   *       notice, this list of conditions and the following disclaimer in
14   *       the documentation and/or other materials provided with the
15   *       distribution.
16   *
17   *   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
18   *   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
19   *   AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
20   *   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
21   *   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22   *   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23   *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24   *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25   *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26   *   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27   *
28   *   $JOMC: AbstractJomcMojo.java 4200 2012-01-25 09:46:13Z schulte2005 $
29   *
30   */
31  package org.jomc.mojo;
32  
33  import java.io.BufferedReader;
34  import java.io.File;
35  import java.io.IOException;
36  import java.io.InputStream;
37  import java.io.StringReader;
38  import java.io.StringWriter;
39  import java.net.MalformedURLException;
40  import java.net.SocketTimeoutException;
41  import java.net.URI;
42  import java.net.URISyntaxException;
43  import java.net.URL;
44  import java.net.URLClassLoader;
45  import java.net.URLConnection;
46  import java.util.Collection;
47  import java.util.Date;
48  import java.util.HashSet;
49  import java.util.Iterator;
50  import java.util.List;
51  import java.util.Locale;
52  import java.util.Map;
53  import java.util.Properties;
54  import java.util.Set;
55  import java.util.logging.Level;
56  import javax.xml.bind.JAXBException;
57  import javax.xml.bind.Marshaller;
58  import javax.xml.transform.ErrorListener;
59  import javax.xml.transform.Transformer;
60  import javax.xml.transform.TransformerConfigurationException;
61  import javax.xml.transform.TransformerException;
62  import javax.xml.transform.TransformerFactory;
63  import javax.xml.transform.stream.StreamSource;
64  import org.apache.commons.lang.StringEscapeUtils;
65  import org.apache.commons.lang.StringUtils;
66  import org.apache.maven.artifact.Artifact;
67  import org.apache.maven.artifact.ArtifactUtils;
68  import org.apache.maven.execution.MavenSession;
69  import org.apache.maven.plugin.AbstractMojo;
70  import org.apache.maven.plugin.MojoExecutionException;
71  import org.apache.maven.plugin.MojoFailureException;
72  import org.apache.maven.plugin.descriptor.MojoDescriptor;
73  import org.apache.maven.project.MavenProject;
74  import org.jomc.model.Module;
75  import org.jomc.model.Modules;
76  import org.jomc.model.modlet.DefaultModelProcessor;
77  import org.jomc.model.modlet.DefaultModelProvider;
78  import org.jomc.model.modlet.ModelHelper;
79  import org.jomc.modlet.DefaultModelContext;
80  import org.jomc.modlet.DefaultModletProvider;
81  import org.jomc.modlet.Model;
82  import org.jomc.modlet.ModelContext;
83  import org.jomc.modlet.ModelContextFactory;
84  import org.jomc.modlet.ModelException;
85  import org.jomc.modlet.ModelValidationReport;
86  import org.jomc.tools.ClassFileProcessor;
87  import org.jomc.tools.JomcTool;
88  import org.jomc.tools.ResourceFileProcessor;
89  import org.jomc.tools.SourceFileProcessor;
90  import org.jomc.tools.modlet.ToolsModelProcessor;
91  import org.jomc.tools.modlet.ToolsModelProvider;
92  
93  /**
94   * Base class for executing {@code JomcTool}s.
95   *
96   * @author <a href="mailto:schulte2005@users.sourceforge.net">Christian Schulte</a>
97   * @version $JOMC: AbstractJomcMojo.java 4200 2012-01-25 09:46:13Z schulte2005 $
98   */
99  public abstract class AbstractJomcMojo extends AbstractMojo
100 {
101 
102     /**
103      * The encoding to use for reading and writing files.
104      *
105      * @parameter default-value="${project.build.sourceEncoding}" expression="${jomc.sourceEncoding}"
106      */
107     private String sourceEncoding;
108 
109     /**
110      * The encoding to use for reading templates.
111      *
112      * @parameter expression="${jomc.templateEncoding}"
113      */
114     private String templateEncoding;
115 
116     /**
117      * Location to search for templates in addition to searching the class path of the plugin.
118      * <p>First an attempt is made to parse the location value to an URL. On successful parsing, that URL is used.
119      * Otherwise the location value is interpreted as a directory name relative to the base directory of the project.
120      * If that directory exists, that directory is used. If nothing is found at the given location, a warning message is
121      * logged.</p>
122      *
123      * @parameter expression="${jomc.templateLocation}"
124      * @since 1.2
125      */
126     private String templateLocation;
127 
128     /**
129      * The template profile to use when accessing templates.
130      *
131      * @parameter expression="${jomc.templateProfile}"
132      */
133     private String templateProfile;
134 
135     /**
136      * The default template profile to use when accessing templates.
137      *
138      * @parameter expression="${jomc.defaultTemplateProfile}"
139      */
140     private String defaultTemplateProfile;
141 
142     /**
143      * The location to search for providers.
144      *
145      * @parameter expression="${jomc.providerLocation}"
146      */
147     private String providerLocation;
148 
149     /**
150      * The location to search for platform providers.
151      *
152      * @parameter expression="${jomc.platformProviderLocation}"
153      */
154     private String platformProviderLocation;
155 
156     /**
157      * The identifier of the model to process.
158      *
159      * @parameter default-value="http://jomc.org/model" expression="${jomc.model}"
160      */
161     private String model;
162 
163     /**
164      * The name of the {@code ModelContextFactory} implementation class backing the task.
165      *
166      * @parameter expression="${jomc.modelContextFactoryClassName}"
167      * @since 1.2
168      */
169     private String modelContextFactoryClassName;
170 
171     /**
172      * The location to search for modlets.
173      *
174      * @parameter expression="${jomc.modletLocation}"
175      */
176     private String modletLocation;
177 
178     /**
179      * The {@code http://jomc.org/modlet} namespace schema system id.
180      *
181      * @parameter expression="${jomc.modletSchemaSystemId}"
182      * @since 1.2
183      */
184     private String modletSchemaSystemId;
185 
186     /**
187      * The location to search for modules.
188      *
189      * @parameter expression="${jomc.moduleLocation}"
190      */
191     private String moduleLocation;
192 
193     /**
194      * The location to search for transformers.
195      *
196      * @parameter expression="${jomc.transformerLocation}"
197      */
198     private String transformerLocation;
199 
200     /**
201      * The indentation string ('\t' for tab).
202      *
203      * @parameter expression="${jomc.indentation}"
204      */
205     private String indentation;
206 
207     /**
208      * The line separator ('\r\n' for DOS, '\r' for Mac, '\n' for Unix).
209      *
210      * @parameter expression="${jomc.lineSeparator}"
211      */
212     private String lineSeparator;
213 
214     /**
215      * The locale.
216      * <pre>
217      * &lt;locale>
218      *   &lt;language>Lowercase two-letter ISO-639 code.&lt;/language>
219      *   &lt;country>Uppercase two-letter ISO-3166 code.&lt;/country>
220      *   &lt;variant>Vendor and browser specific code.&lt;/variant>
221      * &lt;/locale>
222      * </pre>
223      *
224      * @parameter
225      * @since 1.2
226      * @see Locale
227      */
228     private LocaleType locale;
229 
230     /**
231      * Controls verbosity of the plugin.
232      *
233      * @parameter expression="${jomc.verbose}" default-value="false"
234      */
235     private boolean verbose;
236 
237     /**
238      * Controls processing of source code files.
239      *
240      * @parameter expression="${jomc.sourceProcessing}" default-value="true"
241      */
242     private boolean sourceProcessingEnabled;
243 
244     /**
245      * Controls processing of resource files.
246      *
247      * @parameter expression="${jomc.resourceProcessing}" default-value="true"
248      */
249     private boolean resourceProcessingEnabled;
250 
251     /**
252      * Controls processing of class files.
253      *
254      * @parameter expression="${jomc.classProcessing}" default-value="true"
255      */
256     private boolean classProcessingEnabled;
257 
258     /**
259      * Controls processing of models.
260      *
261      * @parameter expression="${jomc.modelProcessing}" default-value="true"
262      */
263     private boolean modelProcessingEnabled;
264 
265     /**
266      * Controls model object class path resolution.
267      *
268      * @parameter expression="${jomc.modelObjectClasspathResolution}" default-value="true"
269      */
270     private boolean modelObjectClasspathResolutionEnabled;
271 
272     /**
273      * Name of the module to process.
274      *
275      * @parameter default-value="${project.name}" expression="${jomc.moduleName}"
276      */
277     private String moduleName;
278 
279     /**
280      * Name of the test module to process.
281      *
282      * @parameter default-value="${project.name} Tests" expression="${jomc.testModuleName}"
283      */
284     private String testModuleName;
285 
286     /**
287      * Directory holding the compiled class files of the project.
288      * <p><strong>Deprecated:</strong> As of JOMC 1.1, please use the 'outputDirectory' parameter. This parameter will
289      * be removed in version 2.0.</p>
290      *
291      * @parameter
292      */
293     @Deprecated
294     private String classesDirectory;
295 
296     /**
297      * Directory holding the compiled test class files of the project.
298      * <p><strong>Deprecated:</strong> As of JOMC 1.1, please use the 'testOutputDirectory' parameter. This parameter
299      * will be removed in version 2.0.</p>
300      *
301      * @parameter
302      */
303     @Deprecated
304     private String testClassesDirectory;
305 
306     /**
307      * Output directory of the project.
308      *
309      * @parameter default-value="${project.build.outputDirectory}" expression="${jomc.outputDirectory}"
310      * @since 1.1
311      */
312     private String outputDirectory;
313 
314     /**
315      * Test output directory of the project.
316      *
317      * @parameter default-value="${project.build.testOutputDirectory}" expression="${jomc.testOutputDirectory}"
318      * @since 1.1
319      */
320     private String testOutputDirectory;
321 
322     /**
323      * Directory holding the source files of the project.
324      *
325      * @parameter default-value="${project.build.sourceDirectory}" expression="${jomc.sourceDirectory}"
326      * @since 1.1
327      */
328     private String sourceDirectory;
329 
330     /**
331      * Directory holding the test source files of the project.
332      *
333      * @parameter default-value="${project.build.testSourceDirectory}" expression="${jomc.testSourceDirectory}"
334      * @since 1.1
335      */
336     private String testSourceDirectory;
337 
338     /**
339      * Directory holding the session related files of the project.
340      *
341      * @parameter default-value="${project.build.directory}/jomc-sessions" expression="${jomc.sessionDirectory}"
342      * @since 1.1
343      */
344     private String sessionDirectory;
345 
346     /**
347      * Directory holding the reports of the project.
348      *
349      * @parameter default-value="${project.reporting.outputDirectory}" expression="${jomc.reportOutputDirectory}"
350      * @since 1.1
351      */
352     private String reportOutputDirectory;
353 
354     /**
355      * Velocity runtime properties.
356      * <pre>
357      * &lt;velocityProperties>
358      *   &lt;velocityProperty>
359      *     &lt;key>The name of the property.&lt;/key>
360      *     &lt;value>The value of the property.&lt;/value>
361      *     &lt;type>The name of the class of the properties object.&lt;/type>
362      *   &lt;/velocityProperty>
363      * &lt;/velocityProperties>
364      * </pre>
365      *
366      * @parameter
367      * @since 1.2
368      */
369     private List<VelocityProperty> velocityProperties;
370 
371     /**
372      * Velocity runtime property resources.
373      * <pre>
374      * &lt;velocityPropertyResources>
375      *   &lt;velocityPropertyResource>
376      *     &lt;location>The location of the properties resource.&lt;/location>
377      *     &lt;optional>Flag indicating the properties resource is optional.&lt;/optional>
378      *     &lt;format>The format of the properties resource.&lt;/format>
379      *     &lt;connectTimeout>Timeout value, in milliseconds.&lt;/connectTimeout>
380      *     &lt;readTimeout>Timeout value, in milliseconds.&lt;/readTimeout>
381      *   &lt;/velocityPropertyResource>
382      * &lt;/velocityPropertyResources>
383      * </pre>
384      * <p>The location value is used to first search the class path of the plugin. If a class path resource is found,
385      * that resource is used. If no class path resource is found, an attempt is made to parse the location value to an
386      * URL. On successful parsing, that URL is used. Otherwise the location value is interpreted as a file name relative
387      * to the base directory of the project. If that file exists, that file is used. If nothing is found at the given
388      * location, depending on the optional flag, a warning message is logged or a build failure is produced.</p>
389      * <p>The optional flag is used to flag the resource optional. When an optional resource is not found, a warning
390      * message is logged instead of producing a build failure.<br/><b>Default value is:</b> false</p>
391      * <p>The format value is used to specify the format of the properties resource. Supported values are {@code plain}
392      * and {@code xml}.<br/><b>Default value is:</b> plain</p>
393      * <p>The connectTimeout value is used to specify the timeout, in milliseconds, to be used when opening
394      * communications links to the resource. A timeout of zero is interpreted as an infinite timeout.<br/>
395      * <b>Default value is:</b> 60000</p>
396      * <p>The readTimeout value is used to specify the timeout, in milliseconds, to be used when reading the resource.
397      * A timeout of zero is interpreted as an infinite timeout.<br/>
398      * <b>Default value is:</b> 60000</p>
399      *
400      * @parameter
401      * @since 1.2
402      */
403     private List<VelocityPropertyResource> velocityPropertyResources;
404 
405     /**
406      * Template parameters.
407      * <pre>
408      * &lt;templateParameters>
409      *   &lt;templateParameter>
410      *     &lt;key>The name of the parameter.&lt;/key>
411      *     &lt;value>The value of the parameter.&lt;/value>
412      *     &lt;type>The name of the class of the parameter's object.&lt;/type>
413      *   &lt;/templateParameter>
414      * &lt;/templateParameters>
415      * </pre>
416      *
417      * @parameter
418      * @since 1.2
419      */
420     private List<TemplateParameter> templateParameters;
421 
422     /**
423      * Template parameter resources.
424      * <pre>
425      * &lt;templateParameterResources>
426      *   &lt;templateParameterResource>
427      *     &lt;location>The location of the properties resource.&lt;/location>
428      *     &lt;optional>Flag indicating the properties resource is optional.&lt;/optional>
429      *     &lt;format>The format of the properties resource.&lt;/format>
430      *     &lt;connectTimeout>Timeout value, in milliseconds.&lt;/connectTimeout>
431      *     &lt;readTimeout>Timeout value, in milliseconds.&lt;/readTimeout>
432      *   &lt;/templateParameterResource>
433      * &lt;/templateParameterResources>
434      * </pre>
435      * <p>The location value is used to first search the class path of the plugin. If a class path resource is found,
436      * that resource is used. If no class path resource is found, an attempt is made to parse the location value to an
437      * URL. On successful parsing, that URL is used. Otherwise the location value is interpreted as a file name relative
438      * to the base directory of the project. If that file exists, that file is used. If nothing is found at the given
439      * location, depending on the optional flag, a warning message is logged or a build failure is produced.</p>
440      * <p>The optional flag is used to flag the resource optional. When an optional resource is not found, a warning
441      * message is logged instead of producing a build failure.<br/><b>Default value is:</b> false</p>
442      * <p>The format value is used to specify the format of the properties resource. Supported values are {@code plain}
443      * and {@code xml}.<br/><b>Default value is:</b> plain</p>
444      * <p>The connectTimeout value is used to specify the timeout, in milliseconds, to be used when opening
445      * communications links to the resource. A timeout of zero is interpreted as an infinite timeout.<br/>
446      * <b>Default value is:</b> 60000</p>
447      * <p>The readTimeout value is used to specify the timeout, in milliseconds, to be used when reading the resource.
448      * A timeout of zero is interpreted as an infinite timeout.<br/>
449      * <b>Default value is:</b> 60000</p>
450      *
451      * @parameter
452      * @since 1.2
453      */
454     private List<TemplateParameterResource> templateParameterResources;
455 
456     /**
457      * Global transformation parameters.
458      * <pre>
459      * &lt;transformationParameters>
460      *   &lt;transformationParameter>
461      *     &lt;key>The name of the parameter.&lt;/key>
462      *     &lt;value>The value of the parameter.&lt;/value>
463      *     &lt;type>The name of the class of the parameter's object.&lt;/type>
464      *   &lt;/transformationParameter>
465      * &lt;/transformationParameters>
466      * </pre>
467      *
468      * @parameter
469      * @since 1.2
470      */
471     private List<TransformationParameter> transformationParameters;
472 
473     /**
474      * Global transformation output properties.
475      * <pre>
476      * &lt;transformationOutputProperties>
477      *   &lt;transformationOutputProperty>
478      *     &lt;key>The name of the property.&lt;/key>
479      *     &lt;value>The value of the property.&lt;/value>
480      *     &lt;type>The name of the class of the properties object.&lt;/type>
481      *   &lt;/transformationOutputProperty>
482      * &lt;/transformationOutputProperties>
483      * </pre>
484      *
485      * @parameter
486      * @since 1.2
487      */
488     private List<TransformationOutputProperty> transformationOutputProperties;
489 
490     /**
491      * Global transformation parameter resources.
492      * <pre>
493      * &lt;transformationParameterResources>
494      *   &lt;transformationParameterResource>
495      *     &lt;location>The location of the properties resource.&lt;/location>
496      *     &lt;optional>Flag indicating the properties resource is optional.&lt;/optional>
497      *     &lt;format>The format of the properties resource.&lt;/format>
498      *     &lt;connectTimeout>Timeout value, in milliseconds.&lt;/connectTimeout>
499      *     &lt;readTimeout>Timeout value, in milliseconds.&lt;/readTimeout>
500      *   &lt;/transformationParameterResource>
501      * &lt;/transformationParameterResources>
502      * </pre>
503      * <p>The location value is used to first search the class path of the plugin. If a class path resource is found,
504      * that resource is used. If no class path resource is found, an attempt is made to parse the location value to an
505      * URL. On successful parsing, that URL is used. Otherwise the location value is interpreted as a file name relative
506      * to the base directory of the project. If that file exists, that file is used. If nothing is found at the given
507      * location, depending on the optional flag, a warning message is logged or a build failure is produced.</p>
508      * <p>The optional flag is used to flag the resource optional. When an optional resource is not found, a warning
509      * message is logged instead of producing a build failure.<br/><b>Default value is:</b> false</p>
510      * <p>The format value is used to specify the format of the properties resource. Supported values are {@code plain}
511      * and {@code xml}.<br/><b>Default value is:</b> plain</p>
512      * <p>The connectTimeout value is used to specify the timeout, in milliseconds, to be used when opening
513      * communications links to the resource. A timeout of zero is interpreted as an infinite timeout.<br/>
514      * <b>Default value is:</b> 60000</p>
515      * <p>The readTimeout value is used to specify the timeout, in milliseconds, to be used when reading the resource.
516      * A timeout of zero is interpreted as an infinite timeout.<br/>
517      * <b>Default value is:</b> 60000</p>
518      *
519      * @parameter
520      * @since 1.2
521      */
522     private List<TransformationParameterResource> transformationParameterResources;
523 
524     /**
525      * Class name of the {@code ClassFileProcessor} backing the goal.
526      *
527      * @parameter default-value="org.jomc.tools.ClassFileProcessor" expression="${jomc.classFileProcessorClassName}"
528      * @since 1.2
529      */
530     private String classFileProcessorClassName;
531 
532     /**
533      * Class name of the {@code ResourceFileProcessor} backing the goal.
534      *
535      * @parameter default-value="org.jomc.tools.ResourceFileProcessor"
536      *            expression="${jomc.resourceFileProcessorClassName}"
537      * @since 1.2
538      */
539     private String resourceFileProcessorClassName;
540 
541     /**
542      * Class name of the {@code SourceFileProcessor} backing the goal.
543      *
544      * @parameter default-value="org.jomc.tools.SourceFileProcessor" expression="${jomc.sourceFileProcessorClassName}"
545      * @since 1.2
546      */
547     private String sourceFileProcessorClassName;
548 
549     /**
550      * {@code ModelContext} attributes.
551      * <pre>
552      * &lt;modelContextAttributes>
553      *   &lt;modelContextAttribute>
554      *     &lt;key>The name of the attribute.&lt;/key>
555      *     &lt;value>The value of the attribute.&lt;/value>
556      *     &lt;type>The name of the class of the attributes's object.&lt;/type>
557      *   &lt;/modelContextAttribute>
558      * &lt;/modelContextAttributes>
559      * </pre>
560      *
561      * @parameter
562      * @since 1.2
563      */
564     private List<ModelContextAttribute> modelContextAttributes;
565 
566     /**
567      * Flag controlling JAXP schema validation of model resources.
568      *
569      * @parameter default-value="true" expression="${jomc.modelResourceValidationEnabled}"
570      *
571      * @since 1.2
572      */
573     private boolean modelResourceValidationEnabled;
574 
575     /**
576      * Flag controlling JAXP schema validation of modlet resources.
577      *
578      * @parameter default-value="true" expression="${jomc.modletResourceValidationEnabled}"
579      *
580      * @since 1.2
581      */
582     private boolean modletResourceValidationEnabled;
583 
584     /**
585      * The Maven project of the instance.
586      *
587      * @parameter expression="${project}"
588      * @required
589      * @readonly
590      */
591     private MavenProject mavenProject;
592 
593     /**
594      * List of plugin artifacts.
595      *
596      * @parameter expression="${plugin.artifacts}"
597      * @required
598      * @readonly
599      */
600     private List<Artifact> pluginArtifacts;
601 
602     /**
603      * The Maven session of the instance.
604      *
605      * @parameter expression="${session}"
606      * @required
607      * @readonly
608      * @since 1.1
609      */
610     private MavenSession mavenSession;
611 
612     /** Creates a new {@code AbstractJomcMojo} instance. */
613     public AbstractJomcMojo()
614     {
615         super();
616     }
617 
618     /**
619      * {@inheritDoc}
620      * @see #assertValidParameters()
621      * @see #isExecutionPermitted()
622      * @see #executeTool()
623      */
624     public void execute() throws MojoExecutionException, MojoFailureException
625     {
626         this.assertValidParameters();
627 
628         try
629         {
630             this.logSeparator();
631 
632             if ( this.isLoggable( Level.INFO ) )
633             {
634                 this.log( Level.INFO, Messages.getMessage( "title" ), null );
635             }
636 
637             if ( this.isExecutionPermitted() )
638             {
639                 this.executeTool();
640             }
641             else if ( this.isLoggable( Level.INFO ) )
642             {
643                 this.log( Level.INFO, Messages.getMessage( "executionSuppressed", this.getExecutionStrategy() ), null );
644             }
645         }
646         catch ( final Exception e )
647         {
648             throw new MojoExecutionException( Messages.getMessage( e ), e );
649         }
650         finally
651         {
652             JomcTool.setDefaultTemplateProfile( null );
653             this.logSeparator();
654         }
655     }
656 
657     /**
658      * Validates the parameters of the goal.
659      *
660      * @throws MojoFailureException if illegal parameter values are detected.
661      *
662      * @see #assertValidResources(java.util.Collection)
663      * @since 1.2
664      */
665     protected void assertValidParameters() throws MojoFailureException
666     {
667         this.assertValidResources( this.templateParameterResources );
668         this.assertValidResources( this.transformationParameterResources );
669         this.assertValidResources( this.velocityPropertyResources );
670     }
671 
672     /**
673      * Validates a given resource collection.
674      *
675      * @param resources The resource collection to validate or {@code null}.
676      *
677      * @throws MojoFailureException if a location property of a given resource holds a {@code null} value or a given
678      * {@code PropertiesResourceType} holds an illegal format.
679      *
680      * @see #assertValidParameters()
681      * @see PropertiesResourceType#isFormatSupported(java.lang.String)
682      * @since 1.2
683      */
684     protected final void assertValidResources( final Collection<? extends ResourceType> resources )
685         throws MojoFailureException
686     {
687         if ( resources != null )
688         {
689             for ( ResourceType r : resources )
690             {
691                 if ( r.getLocation() == null )
692                 {
693                     throw new MojoFailureException( Messages.getMessage( "mandatoryParameter", "location" ) );
694                 }
695 
696                 if ( r instanceof PropertiesResourceType )
697                 {
698                     final PropertiesResourceType p = (PropertiesResourceType) r;
699 
700                     if ( !PropertiesResourceType.isFormatSupported( p.getFormat() ) )
701                     {
702                         throw new MojoFailureException( Messages.getMessage(
703                             "illegalPropertiesFormat", p.getFormat(),
704                             StringUtils.join( PropertiesResourceType.getSupportedFormats(), ',' ) ) );
705 
706                     }
707                 }
708             }
709         }
710     }
711 
712     /**
713      * Executes this tool.
714      *
715      * @throws Exception if execution of this tool fails.
716      */
717     protected abstract void executeTool() throws Exception;
718 
719     /**
720      * Gets the goal of the instance.
721      *
722      * @return The goal of the instance.
723      *
724      * @throws MojoExecutionException if getting the goal of the instance fails.
725      * @since 1.1
726      */
727     protected abstract String getGoal() throws MojoExecutionException;
728 
729     /**
730      * Gets the execution strategy of the instance.
731      *
732      * @return The execution strategy of the instance.
733      *
734      * @throws MojoExecutionException if getting the execution strategy of the instance fails.
735      * @since 1.1
736      */
737     protected abstract String getExecutionStrategy() throws MojoExecutionException;
738 
739     /**
740      * Gets a flag indicating the current execution is permitted.
741      *
742      * @return {@code true}, if the current execution is permitted; {@code false}, if the current execution is
743      * suppressed.
744      *
745      * @throws MojoExecutionException if getting the flag fails.
746      *
747      * @since 1.1
748      * @see #getGoal()
749      * @see #getExecutionStrategy()
750      */
751     protected boolean isExecutionPermitted() throws MojoExecutionException
752     {
753         try
754         {
755             boolean permitted = true;
756 
757             if ( MojoDescriptor.SINGLE_PASS_EXEC_STRATEGY.equals( this.getExecutionStrategy() ) )
758             {
759                 final File flagFile =
760                     new File( this.getSessionDirectory(),
761                               ArtifactUtils.versionlessKey( this.getMavenProject().getArtifact() ).hashCode()
762                               + "-" + this.getGoal()
763                               + "-" + this.getMavenSession().getStartTime().getTime() + ".flg" );
764 
765                 if ( !this.getSessionDirectory().exists() && !this.getSessionDirectory().mkdirs() )
766                 {
767                     throw new MojoExecutionException( Messages.getMessage(
768                         "failedCreatingDirectory", this.getSessionDirectory().getAbsolutePath() ) );
769 
770                 }
771 
772                 permitted = flagFile.createNewFile();
773             }
774 
775             return permitted;
776         }
777         catch ( final IOException e )
778         {
779             throw new MojoExecutionException( Messages.getMessage( e ), e );
780         }
781     }
782 
783     /**
784      * Gets the Maven project of the instance.
785      *
786      * @return The Maven project of the instance.
787      *
788      * @throws MojoExecutionException if getting the Maven project of the instance fails.
789      */
790     protected MavenProject getMavenProject() throws MojoExecutionException
791     {
792         return this.mavenProject;
793     }
794 
795     /**
796      * Gets the Maven session of the instance.
797      *
798      * @return The Maven session of the instance.
799      *
800      * @throws MojoExecutionException if getting the Maven session of the instance fails.
801      *
802      * @since 1.1
803      */
804     protected MavenSession getMavenSession() throws MojoExecutionException
805     {
806         return this.mavenSession;
807     }
808 
809     /**
810      * Gets an absolute {@code File} instance for a given name.
811      * <p>This method constructs a new {@code File} instance using the given name. If the resulting file is not
812      * absolute, the value of the {@code basedir} property of the current Maven project is prepended.</p>
813      *
814      * @param name The name to get an absolute {@code File} instance for.
815      *
816      * @return An absolute {@code File} instance constructed from {@code name}.
817      *
818      * @throws MojoExecutionException if getting an absolute {@code File} instance for {@code name} fails.
819      * @throws NullPointerException if {@code name} is {@code null}.
820      *
821      * @since 1.1
822      */
823     protected File getAbsoluteFile( final String name ) throws MojoExecutionException
824     {
825         if ( name == null )
826         {
827             throw new NullPointerException( "name" );
828         }
829 
830         File file = new File( name );
831         if ( !file.isAbsolute() )
832         {
833             file = new File( this.getMavenProject().getBasedir(), name );
834         }
835 
836         return file;
837     }
838 
839     /**
840      * Gets the directory holding the compiled class files of the project.
841      *
842      * @return The directory holding the compiled class files of the project.
843      *
844      * @throws MojoExecutionException if getting the directory fails.
845      *
846      * @since 1.1
847      */
848     protected File getOutputDirectory() throws MojoExecutionException
849     {
850         if ( this.classesDirectory != null )
851         {
852             if ( this.isLoggable( Level.WARNING ) )
853             {
854                 this.log( Level.WARNING, Messages.getMessage(
855                     "deprecationWarning", "classesDirectory", "outputDirectory" ), null );
856 
857             }
858 
859             if ( !this.classesDirectory.equals( this.outputDirectory ) )
860             {
861                 if ( this.isLoggable( Level.WARNING ) )
862                 {
863                     this.log( Level.WARNING, Messages.getMessage( "ignoringParameter", "outputDirectory" ), null );
864                 }
865 
866                 this.outputDirectory = this.classesDirectory;
867             }
868 
869             this.classesDirectory = null;
870         }
871 
872         final File dir = this.getAbsoluteFile( this.outputDirectory );
873         if ( !dir.exists() && !dir.mkdirs() )
874         {
875             throw new MojoExecutionException( Messages.getMessage( "failedCreatingDirectory", dir.getAbsolutePath() ) );
876         }
877 
878         return dir;
879     }
880 
881     /**
882      * Gets the directory holding the compiled test class files of the project.
883      *
884      * @return The directory holding the compiled test class files of the project.
885      *
886      * @throws MojoExecutionException if getting the directory fails.
887      *
888      * @since 1.1
889      */
890     protected File getTestOutputDirectory() throws MojoExecutionException
891     {
892         if ( this.testClassesDirectory != null )
893         {
894             if ( this.isLoggable( Level.WARNING ) )
895             {
896                 this.log( Level.WARNING, Messages.getMessage(
897                     "deprecationWarning", "testClassesDirectory", "testOutputDirectory" ), null );
898 
899             }
900 
901             if ( !this.testClassesDirectory.equals( this.testOutputDirectory ) )
902             {
903                 if ( this.isLoggable( Level.WARNING ) )
904                 {
905                     this.log( Level.WARNING, Messages.getMessage( "ignoringParameter", "testOutputDirectory" ), null );
906                 }
907 
908                 this.testOutputDirectory = this.testClassesDirectory;
909             }
910 
911             this.testClassesDirectory = null;
912         }
913 
914         final File dir = this.getAbsoluteFile( this.testOutputDirectory );
915         if ( !dir.exists() && !dir.mkdirs() )
916         {
917             throw new MojoExecutionException( Messages.getMessage( "failedCreatingDirectory", dir.getAbsolutePath() ) );
918         }
919 
920         return dir;
921     }
922 
923     /**
924      * Gets the directory holding the source files of the project.
925      *
926      * @return The directory holding the source files of the project.
927      *
928      * @throws MojoExecutionException if getting the directory fails.
929      *
930      * @since 1.1
931      */
932     protected File getSourceDirectory() throws MojoExecutionException
933     {
934         return this.getAbsoluteFile( this.sourceDirectory );
935     }
936 
937     /**
938      * Gets the directory holding the test source files of the project.
939      *
940      * @return The directory holding the test source files of the project.
941      *
942      * @throws MojoExecutionException if getting the directory fails.
943      *
944      * @since 1.1
945      */
946     protected File getTestSourceDirectory() throws MojoExecutionException
947     {
948         return this.getAbsoluteFile( this.testSourceDirectory );
949     }
950 
951     /**
952      * Gets the directory holding the session related files of the project.
953      *
954      * @return The directory holding the session related files of the project.
955      *
956      * @throws MojoExecutionException if getting the directory fails.
957      *
958      * @since 1.1
959      */
960     protected File getSessionDirectory() throws MojoExecutionException
961     {
962         return this.getAbsoluteFile( this.sessionDirectory );
963     }
964 
965     /**
966      * Gets the directory holding the reports of the project.
967      *
968      * @return The directory holding the reports of the project.
969      *
970      * @throws MojoExecutionException if getting the directory fails.
971      *
972      * @since 1.1
973      */
974     protected File getReportOutputDirectory() throws MojoExecutionException
975     {
976         return this.getAbsoluteFile( this.reportOutputDirectory );
977     }
978 
979     /**
980      * Gets the project's runtime class loader of the instance.
981      *
982      * @return The project's runtime class loader of the instance.
983      *
984      * @throws MojoExecutionException if getting the class loader fails.
985      */
986     protected ClassLoader getMainClassLoader() throws MojoExecutionException
987     {
988         try
989         {
990             final Set<String> mainClasspathElements = this.getMainClasspathElements();
991             final Set<URI> uris = new HashSet<URI>( mainClasspathElements.size() );
992 
993             for ( String element : mainClasspathElements )
994             {
995                 final URI uri = new File( element ).toURI();
996                 if ( !uris.contains( uri ) )
997                 {
998                     uris.add( uri );
999                 }
1000             }
1001 
1002             if ( this.isLoggable( Level.FINEST ) )
1003             {
1004                 this.log( Level.FINEST, Messages.getMessage( "mainClasspathInfo" ), null );
1005             }
1006 
1007             int i = 0;
1008             final URL[] urls = new URL[ uris.size() ];
1009             for ( URI uri : uris )
1010             {
1011                 urls[i++] = uri.toURL();
1012 
1013                 if ( this.isLoggable( Level.FINEST ) )
1014                 {
1015                     this.log( Level.FINEST, "\t" + urls[i - 1].toExternalForm(), null );
1016                 }
1017             }
1018 
1019             return new URLClassLoader( urls, Thread.currentThread().getContextClassLoader() );
1020         }
1021         catch ( final IOException e )
1022         {
1023             throw new MojoExecutionException( Messages.getMessage( e ), e );
1024         }
1025     }
1026 
1027     /**
1028      * Gets the project's test class loader of the instance.
1029      *
1030      * @return The project's test class loader of the instance.
1031      *
1032      * @throws MojoExecutionException if getting the class loader fails.
1033      */
1034     protected ClassLoader getTestClassLoader() throws MojoExecutionException
1035     {
1036         try
1037         {
1038             final Set<String> testClasspathElements = this.getTestClasspathElements();
1039             final Set<URI> uris = new HashSet<URI>( testClasspathElements.size() );
1040 
1041             for ( String element : testClasspathElements )
1042             {
1043                 final URI uri = new File( element ).toURI();
1044                 if ( !uris.contains( uri ) )
1045                 {
1046                     uris.add( uri );
1047                 }
1048             }
1049 
1050             if ( this.isLoggable( Level.FINEST ) )
1051             {
1052                 this.log( Level.FINEST, Messages.getMessage( "testClasspathInfo" ), null );
1053             }
1054 
1055             int i = 0;
1056             final URL[] urls = new URL[ uris.size() ];
1057             for ( URI uri : uris )
1058             {
1059                 urls[i++] = uri.toURL();
1060 
1061                 if ( this.isLoggable( Level.FINEST ) )
1062                 {
1063                     this.log( Level.FINEST, "\t" + urls[i - 1].toExternalForm(), null );
1064                 }
1065             }
1066 
1067             return new URLClassLoader( urls, Thread.currentThread().getContextClassLoader() );
1068         }
1069         catch ( final IOException e )
1070         {
1071             throw new MojoExecutionException( Messages.getMessage( e ), e );
1072         }
1073     }
1074 
1075     /**
1076      * Gets the project's runtime class path elements.
1077      *
1078      * @return A set of class path element strings.
1079      *
1080      * @throws MojoExecutionException if getting the class path elements fails.
1081      */
1082     protected Set<String> getMainClasspathElements() throws MojoExecutionException
1083     {
1084         final List<?> runtimeArtifacts = this.getMavenProject().getRuntimeArtifacts();
1085         final List<?> compileArtifacts = this.getMavenProject().getCompileArtifacts();
1086         final Set<String> elements = new HashSet<String>( runtimeArtifacts.size() + compileArtifacts.size() + 1 );
1087         elements.add( this.getOutputDirectory().getAbsolutePath() );
1088 
1089         for ( final Iterator<?> it = runtimeArtifacts.iterator(); it.hasNext(); )
1090         {
1091             final Artifact a = (Artifact) it.next();
1092             final Artifact pluginArtifact = this.getPluginArtifact( a );
1093 
1094             if ( a.getFile() == null )
1095             {
1096                 if ( this.isLoggable( Level.WARNING ) )
1097                 {
1098                     this.log( Level.WARNING, Messages.getMessage( "ignoringArtifact", a.toString() ), null );
1099                 }
1100 
1101                 continue;
1102             }
1103 
1104             if ( pluginArtifact != null )
1105             {
1106                 if ( this.isLoggable( Level.FINER ) )
1107                 {
1108                     this.log( Level.FINER, Messages.getMessage(
1109                         "ignoringPluginArtifact", a.toString(), pluginArtifact.toString() ), null );
1110 
1111                 }
1112 
1113                 continue;
1114             }
1115 
1116             final String element = a.getFile().getAbsolutePath();
1117             elements.add( element );
1118         }
1119 
1120         for ( final Iterator<?> it = compileArtifacts.iterator(); it.hasNext(); )
1121         {
1122             final Artifact a = (Artifact) it.next();
1123             final Artifact pluginArtifact = this.getPluginArtifact( a );
1124 
1125             if ( a.getFile() == null )
1126             {
1127                 if ( this.isLoggable( Level.WARNING ) )
1128                 {
1129                     this.log( Level.WARNING, Messages.getMessage( "ignoringArtifact", a.toString() ), null );
1130                 }
1131 
1132                 continue;
1133             }
1134 
1135             if ( pluginArtifact != null )
1136             {
1137                 if ( this.isLoggable( Level.FINER ) )
1138                 {
1139                     this.log( Level.FINER, Messages.getMessage(
1140                         "ignoringPluginArtifact", a.toString(), pluginArtifact.toString() ), null );
1141 
1142                 }
1143 
1144                 continue;
1145             }
1146 
1147             final String element = a.getFile().getAbsolutePath();
1148             elements.add( element );
1149         }
1150 
1151         return elements;
1152     }
1153 
1154     /**
1155      * Gets the project's test class path elements.
1156      *
1157      * @return A set of class path element strings.
1158      *
1159      * @throws MojoExecutionException if getting the class path elements fails.
1160      */
1161     protected Set<String> getTestClasspathElements() throws MojoExecutionException
1162     {
1163         final List<?> testArtifacts = this.getMavenProject().getTestArtifacts();
1164         final Set<String> elements = new HashSet<String>( testArtifacts.size() + 2 );
1165         elements.add( this.getOutputDirectory().getAbsolutePath() );
1166         elements.add( this.getTestOutputDirectory().getAbsolutePath() );
1167 
1168         for ( final Iterator<?> it = testArtifacts.iterator(); it.hasNext(); )
1169         {
1170             final Artifact a = (Artifact) it.next();
1171             final Artifact pluginArtifact = this.getPluginArtifact( a );
1172 
1173             if ( a.getFile() == null )
1174             {
1175                 if ( this.isLoggable( Level.WARNING ) )
1176                 {
1177                     this.log( Level.WARNING, Messages.getMessage( "ignoringArtifact", a.toString() ), null );
1178                 }
1179 
1180                 continue;
1181             }
1182 
1183             if ( pluginArtifact != null )
1184             {
1185                 if ( this.isLoggable( Level.FINER ) )
1186                 {
1187                     this.log( Level.FINER, Messages.getMessage(
1188                         "ignoringPluginArtifact", a.toString(), pluginArtifact.toString() ), null );
1189 
1190                 }
1191 
1192                 continue;
1193             }
1194 
1195             final String element = a.getFile().getAbsolutePath();
1196             elements.add( element );
1197         }
1198 
1199         return elements;
1200     }
1201 
1202     /**
1203      * Gets a flag indicating verbose output is enabled.
1204      *
1205      * @return {@code true}, if verbose output is enabled; {@code false}, if information messages are suppressed.
1206      *
1207      * @throws MojoExecutionException if getting the flag fails.
1208      *
1209      * @since 1.1
1210      */
1211     protected final boolean isVerbose() throws MojoExecutionException
1212     {
1213         return this.verbose;
1214     }
1215 
1216     /**
1217      * Sets the flag indicating verbose output is enabled.
1218      *
1219      * @param value {@code true}, to enable verbose output; {@code false}, to suppress information messages.
1220      *
1221      * @throws MojoExecutionException if setting the flag fails.
1222      *
1223      * @since 1.1
1224      */
1225     protected final void setVerbose( final boolean value ) throws MojoExecutionException
1226     {
1227         this.verbose = value;
1228     }
1229 
1230     /**
1231      * Gets a flag indicating the processing of sources is enabled.
1232      *
1233      * @return {@code true}, if processing of sources is enabled; {@code false}, else.
1234      *
1235      * @throws MojoExecutionException if getting the flag fails.
1236      */
1237     protected final boolean isSourceProcessingEnabled() throws MojoExecutionException
1238     {
1239         return this.sourceProcessingEnabled;
1240     }
1241 
1242     /**
1243      * Sets the flag indicating the processing of sources is enabled.
1244      *
1245      * @param value {@code true}, to enable processing of sources; {@code false}, to disable processing of sources.
1246      *
1247      * @throws MojoExecutionException if setting the flag fails.
1248      *
1249      * @since 1.1
1250      */
1251     protected final void setSourceProcessingEnabled( final boolean value ) throws MojoExecutionException
1252     {
1253         this.sourceProcessingEnabled = value;
1254     }
1255 
1256     /**
1257      * Gets a flag indicating the processing of resources is enabled.
1258      *
1259      * @return {@code true}, if processing of resources is enabled; {@code false}, else.
1260      *
1261      * @throws MojoExecutionException if getting the flag fails.
1262      */
1263     protected final boolean isResourceProcessingEnabled() throws MojoExecutionException
1264     {
1265         return this.resourceProcessingEnabled;
1266     }
1267 
1268     /**
1269      * Sets the flag indicating the processing of resources is enabled.
1270      *
1271      * @param value {@code true}, to enable processing of resources; {@code false}, to disable processing of resources.
1272      *
1273      * @throws MojoExecutionException if setting the flag fails.
1274      *
1275      * @since 1.1
1276      */
1277     protected final void setResourceProcessingEnabled( final boolean value ) throws MojoExecutionException
1278     {
1279         this.resourceProcessingEnabled = value;
1280     }
1281 
1282     /**
1283      * Gets a flag indicating the processing of classes is enabled.
1284      *
1285      * @return {@code true}, if processing of classes is enabled; {@code false}, else.
1286      *
1287      * @throws MojoExecutionException if getting the flag fails.
1288      */
1289     protected final boolean isClassProcessingEnabled() throws MojoExecutionException
1290     {
1291         return this.classProcessingEnabled;
1292     }
1293 
1294     /**
1295      * Sets the flag indicating the processing of classes is enabled.
1296      *
1297      * @param value {@code true}, to enable processing of classes; {@code false}, to disable processing of classes.
1298      *
1299      * @throws MojoExecutionException if setting the flag fails.
1300      *
1301      * @since 1.1
1302      */
1303     protected final void setClassProcessingEnabled( final boolean value ) throws MojoExecutionException
1304     {
1305         this.classProcessingEnabled = value;
1306     }
1307 
1308     /**
1309      * Gets a flag indicating the processing of models is enabled.
1310      *
1311      * @return {@code true}, if processing of models is enabled; {@code false}, else.
1312      *
1313      * @throws MojoExecutionException if getting the flag fails.
1314      */
1315     protected final boolean isModelProcessingEnabled() throws MojoExecutionException
1316     {
1317         return this.modelProcessingEnabled;
1318     }
1319 
1320     /**
1321      * Sets the flag indicating the processing of models is enabled.
1322      *
1323      * @param value {@code true}, to enable processing of models; {@code false}, to disable processing of models.
1324      *
1325      * @throws MojoExecutionException if setting the flag fails.
1326      *
1327      * @since 1.1
1328      */
1329     protected final void setModelProcessingEnabled( final boolean value ) throws MojoExecutionException
1330     {
1331         this.modelProcessingEnabled = value;
1332     }
1333 
1334     /**
1335      * Gets a flag indicating model object class path resolution is enabled.
1336      *
1337      * @return {@code true}, if model object class path resolution is enabled; {@code false}, else.
1338      *
1339      * @throws MojoExecutionException if getting the flag fails.
1340      */
1341     protected final boolean isModelObjectClasspathResolutionEnabled() throws MojoExecutionException
1342     {
1343         return this.modelObjectClasspathResolutionEnabled;
1344     }
1345 
1346     /**
1347      * Sets the flag indicating model object class path resolution is enabled.
1348      *
1349      * @param value {@code true}, to enable model object class path resolution; {@code false}, to disable model object
1350      * class path resolution.
1351      *
1352      * @throws MojoExecutionException if setting the flag fails.
1353      *
1354      * @since 1.1
1355      */
1356     protected final void setModelObjectClasspathResolutionEnabled( final boolean value ) throws MojoExecutionException
1357     {
1358         this.modelObjectClasspathResolutionEnabled = value;
1359     }
1360 
1361     /**
1362      * Gets the identifier of the model to process.
1363      *
1364      * @return The identifier of the model to process.
1365      *
1366      * @throws MojoExecutionException if getting the identifier fails.
1367      */
1368     protected String getModel() throws MojoExecutionException
1369     {
1370         return this.model;
1371     }
1372 
1373     /**
1374      * Gets the name of the module to process.
1375      *
1376      * @return The name of the module to process.
1377      *
1378      * @throws MojoExecutionException if getting the name of the module fails.
1379      */
1380     protected String getModuleName() throws MojoExecutionException
1381     {
1382         return this.moduleName;
1383     }
1384 
1385     /**
1386      * Gets the name of the test module to process.
1387      *
1388      * @return The name of the test module to process.
1389      *
1390      * @throws MojoExecutionException if getting the name of the test module fails.
1391      */
1392     protected String getTestModuleName() throws MojoExecutionException
1393     {
1394         return this.testModuleName;
1395     }
1396 
1397     /**
1398      * Gets the model to process.
1399      *
1400      * @param context The model context to get the model to process with.
1401      *
1402      * @return The model to process.
1403      *
1404      * @throws NullPointerException if {@code context} is {@code null}.
1405      * @throws MojoExecutionException if getting the model fails.
1406      */
1407     protected Model getModel( final ModelContext context ) throws MojoExecutionException
1408     {
1409         if ( context == null )
1410         {
1411             throw new NullPointerException( "context" );
1412         }
1413 
1414         try
1415         {
1416             Model m = context.findModel( this.getModel() );
1417             final Modules modules = ModelHelper.getModules( m );
1418 
1419             if ( modules != null && this.isModelObjectClasspathResolutionEnabled() )
1420             {
1421                 final Module classpathModule =
1422                     modules.getClasspathModule( Modules.getDefaultClasspathModuleName(), context.getClassLoader() );
1423 
1424                 if ( classpathModule != null )
1425                 {
1426                     modules.getModule().add( classpathModule );
1427                 }
1428             }
1429 
1430             if ( this.isModelProcessingEnabled() )
1431             {
1432                 m = context.processModel( m );
1433             }
1434 
1435             return m;
1436         }
1437         catch ( final ModelException e )
1438         {
1439             throw new MojoExecutionException( Messages.getMessage( e ), e );
1440         }
1441     }
1442 
1443     /**
1444      * Creates a new model context instance for a given class loader.
1445      *
1446      * @param classLoader The class loader to use for creating the context.
1447      *
1448      * @return A new model context instance for {@code classLoader}.
1449      *
1450      * @throws MojoExecutionException if creating the model context fails.
1451      *
1452      * @see #setupModelContext(org.jomc.modlet.ModelContext)
1453      */
1454     protected ModelContext createModelContext( final ClassLoader classLoader ) throws MojoExecutionException
1455     {
1456         final ModelContextFactory modelContextFactory;
1457         if ( this.modelContextFactoryClassName != null )
1458         {
1459             modelContextFactory = ModelContextFactory.newInstance( this.modelContextFactoryClassName );
1460         }
1461         else
1462         {
1463             modelContextFactory = ModelContextFactory.newInstance();
1464         }
1465 
1466         final ModelContext context = modelContextFactory.newModelContext( classLoader );
1467         this.setupModelContext( context );
1468 
1469         return context;
1470     }
1471 
1472     /**
1473      * Creates a new tool instance for processing source files.
1474      *
1475      * @param context The context of the tool.
1476      *
1477      * @return A new tool instance for processing source files.
1478      *
1479      * @throws NullPointerException if {@code context} is {@code null}.
1480      * @throws MojoExecutionException if creating a new tool instance fails.
1481      *
1482      * @see #createJomcTool(org.jomc.modlet.ModelContext, java.lang.String, java.lang.Class)
1483      */
1484     protected SourceFileProcessor createSourceFileProcessor( final ModelContext context ) throws MojoExecutionException
1485     {
1486         if ( context == null )
1487         {
1488             throw new NullPointerException( "context" );
1489         }
1490 
1491         return this.createJomcTool( context, this.sourceFileProcessorClassName, SourceFileProcessor.class );
1492     }
1493 
1494     /**
1495      * Creates a new tool instance for processing resource files.
1496      *
1497      * @param context The context of the tool.
1498      *
1499      * @return A new tool instance for processing resource files.
1500      *
1501      * @throws NullPointerException if {@code context} is {@code null}.
1502      * @throws MojoExecutionException if creating a new tool instance fails.
1503      *
1504      * @see #createJomcTool(org.jomc.modlet.ModelContext, java.lang.String, java.lang.Class)
1505      */
1506     protected ResourceFileProcessor createResourceFileProcessor( final ModelContext context )
1507         throws MojoExecutionException
1508     {
1509         if ( context == null )
1510         {
1511             throw new NullPointerException( "context" );
1512         }
1513 
1514         return this.createJomcTool( context, this.resourceFileProcessorClassName, ResourceFileProcessor.class );
1515     }
1516 
1517     /**
1518      * Creates a new tool instance for processing class files.
1519      *
1520      * @param context The context of the tool.
1521      *
1522      * @return A new tool instance for processing class files.
1523      *
1524      * @throws NullPointerException if {@code context} is {@code null}.
1525      * @throws MojoExecutionException if creating a new tool instance fails.
1526      *
1527      * @see #createJomcTool(org.jomc.modlet.ModelContext, java.lang.String, java.lang.Class)
1528      */
1529     protected ClassFileProcessor createClassFileProcessor( final ModelContext context ) throws MojoExecutionException
1530     {
1531         if ( context == null )
1532         {
1533             throw new NullPointerException( "context" );
1534         }
1535 
1536         return this.createJomcTool( context, this.classFileProcessorClassName, ClassFileProcessor.class );
1537     }
1538 
1539     /**
1540      * Creates a new {@code JomcTool} object for a given class name and type.
1541      *
1542      * @param context The context of the tool.
1543      * @param className The name of the class to create an object of.
1544      * @param type The class of the type of object to create.
1545      * @param <T> The type of the object to create.
1546      *
1547      * @return A new instance of the class with name {@code className}.
1548      *
1549      * @throws NullPointerException if {@code context}, {@code className} or {@code type} is {@code null}.
1550      * @throws MojoExecutionException if creating a new {@code JomcTool} object fails.
1551      *
1552      * @see #createObject(java.lang.String, java.lang.Class)
1553      * @see #setupJomcTool(org.jomc.modlet.ModelContext, org.jomc.tools.JomcTool)
1554      *
1555      * @since 1.2
1556      */
1557     protected <T extends JomcTool> T createJomcTool( final ModelContext context, final String className,
1558                                                      final Class<T> type ) throws MojoExecutionException
1559     {
1560         if ( context == null )
1561         {
1562             throw new NullPointerException( "context" );
1563         }
1564         if ( className == null )
1565         {
1566             throw new NullPointerException( "className" );
1567         }
1568         if ( type == null )
1569         {
1570             throw new NullPointerException( "type" );
1571         }
1572 
1573         final T tool = this.createObject( className, type );
1574         this.setupJomcTool( context, tool );
1575         return tool;
1576     }
1577 
1578     /**
1579      * Creates a new object for a given class name and type.
1580      *
1581      * @param className The name of the class to create an object of.
1582      * @param type The class of the type of object to create.
1583      * @param <T> The type of the object to create.
1584      *
1585      * @return A new instance of the class with name {@code className}.
1586      *
1587      * @throws NullPointerException if {@code className} or {@code type} is {@code null}.
1588      * @throws MojoExecutionException if creating a new object fails.
1589      *
1590      * @since 1.2
1591      */
1592     protected <T> T createObject( final String className, final Class<T> type ) throws MojoExecutionException
1593     {
1594         if ( className == null )
1595         {
1596             throw new NullPointerException( "className" );
1597         }
1598         if ( type == null )
1599         {
1600             throw new NullPointerException( "type" );
1601         }
1602 
1603         try
1604         {
1605             return Class.forName( className ).asSubclass( type ).newInstance();
1606         }
1607         catch ( final InstantiationException e )
1608         {
1609             throw new MojoExecutionException( Messages.getMessage( "failedCreatingObject", className ), e );
1610         }
1611         catch ( final IllegalAccessException e )
1612         {
1613             throw new MojoExecutionException( Messages.getMessage( "failedCreatingObject", className ), e );
1614         }
1615         catch ( final ClassNotFoundException e )
1616         {
1617             throw new MojoExecutionException( Messages.getMessage( "failedCreatingObject", className ), e );
1618         }
1619         catch ( final ClassCastException e )
1620         {
1621             throw new MojoExecutionException( Messages.getMessage( "failedCreatingObject", className ), e );
1622         }
1623     }
1624 
1625     /**
1626      * Creates an {@code URL} for a given resource location.
1627      * <p>This method first searches the class path of the plugin for a single resource matching {@code location}. If
1628      * such a resource is found, the URL of that resource is returned. If no such resource is found, an attempt is made
1629      * to parse the given location to an URL. On successful parsing, that URL is returned. Failing that, the given
1630      * location is interpreted as a file name relative to the project's base directory. If that file is found, the URL
1631      * of that file is returned. Otherwise {@code null} is returned.</p>
1632      *
1633      * @param location The location to create an {@code URL} from.
1634      *
1635      * @return An {@code URL} for {@code location} or {@code null}, if parsing {@code location} to an URL fails and
1636      * {@code location} points to a non-existent resource.
1637      *
1638      * @throws NullPointerException if {@code location} is {@code null}.
1639      * @throws MojoExecutionException if creating an URL fails.
1640      *
1641      * @since 1.2
1642      */
1643     protected URL getResource( final String location ) throws MojoExecutionException
1644     {
1645         if ( location == null )
1646         {
1647             throw new NullPointerException( "location" );
1648         }
1649 
1650         try
1651         {
1652             String absolute = location;
1653             if ( !absolute.startsWith( "/" ) )
1654             {
1655                 absolute = "/" + location;
1656             }
1657 
1658             URL resource = this.getClass().getResource( absolute );
1659             if ( resource == null )
1660             {
1661                 try
1662                 {
1663                     resource = new URL( location );
1664                 }
1665                 catch ( final MalformedURLException e )
1666                 {
1667                     if ( this.isLoggable( Level.FINEST ) )
1668                     {
1669                         this.log( Level.FINEST, Messages.getMessage( e ), e );
1670                     }
1671 
1672                     resource = null;
1673                 }
1674             }
1675 
1676             if ( resource == null )
1677             {
1678                 final File f = this.getAbsoluteFile( location );
1679 
1680                 if ( f.isFile() )
1681                 {
1682                     resource = f.toURI().toURL();
1683                 }
1684             }
1685 
1686             return resource;
1687         }
1688         catch ( final MalformedURLException e )
1689         {
1690             String m = Messages.getMessage( e );
1691             m = m == null ? "" : " " + m;
1692 
1693             throw new MojoExecutionException( Messages.getMessage( "malformedLocation", location, m ), e );
1694         }
1695     }
1696 
1697     /**
1698      * Creates an {@code URL} for a given directory location.
1699      * <p>This method first attempts to parse the given location to an URL. On successful parsing, that URL is returned.
1700      * Failing that, the given location is interpreted as a directory name relative to the project's base directory.
1701      * If that directory is found, the URL of that directory is returned. Otherwise {@code null} is returned.</p>
1702      *
1703      * @param location The directory location to create an {@code URL} from.
1704      *
1705      * @return An {@code URL} for {@code location} or {@code null}, if parsing {@code location} to an URL fails and
1706      * {@code location} points to a non-existent directory.
1707      *
1708      * @throws NullPointerException if {@code location} is {@code null}.
1709      * @throws MojoExecutionException if creating an URL fails.
1710      *
1711      * @since 1.2
1712      */
1713     protected URL getDirectory( final String location ) throws MojoExecutionException
1714     {
1715         if ( location == null )
1716         {
1717             throw new NullPointerException( "location" );
1718         }
1719 
1720         try
1721         {
1722             URL resource = null;
1723 
1724             try
1725             {
1726                 resource = new URL( location );
1727             }
1728             catch ( final MalformedURLException e )
1729             {
1730                 if ( this.isLoggable( Level.FINEST ) )
1731                 {
1732                     this.log( Level.FINEST, Messages.getMessage( e ), e );
1733                 }
1734 
1735                 resource = null;
1736             }
1737 
1738             if ( resource == null )
1739             {
1740                 final File f = this.getAbsoluteFile( location );
1741 
1742                 if ( f.isDirectory() )
1743                 {
1744                     resource = f.toURI().toURL();
1745                 }
1746             }
1747 
1748             return resource;
1749         }
1750         catch ( final MalformedURLException e )
1751         {
1752             String m = Messages.getMessage( e );
1753             m = m == null ? "" : " " + m;
1754 
1755             throw new MojoExecutionException( Messages.getMessage( "malformedLocation", location, m ), e );
1756         }
1757     }
1758 
1759     /**
1760      * Creates a new {@code Transformer} from a given {@code TransformerResourceType}.
1761      *
1762      * @param resource The resource to initialize the transformer with.
1763      *
1764      * @return A {@code Transformer} for {@code resource} or {@code null}, if {@code resource} is not found and flagged
1765      * optional.
1766      *
1767      * @throws NullPointerException if {@code resource} is {@code null}.
1768      * @throws MojoExecutionException if creating a transformer fails.
1769      *
1770      * @see #getResource(java.lang.String)
1771      * @since 1.2
1772      */
1773     protected Transformer getTransformer( final TransformerResourceType resource ) throws MojoExecutionException
1774     {
1775         if ( resource == null )
1776         {
1777             throw new NullPointerException( "resource" );
1778         }
1779 
1780         InputStream in = null;
1781         boolean suppressExceptionOnClose = true;
1782         final URL url = this.getResource( resource.getLocation() );
1783         final ErrorListener errorListener = new ErrorListener()
1784         {
1785 
1786             public void warning( final TransformerException exception ) throws TransformerException
1787             {
1788                 try
1789                 {
1790                     log( Level.WARNING, Messages.getMessage( exception ), exception );
1791                 }
1792                 catch ( final MojoExecutionException e )
1793                 {
1794                     getLog().warn( exception );
1795                     getLog().error( e );
1796                 }
1797             }
1798 
1799             public void error( final TransformerException exception ) throws TransformerException
1800             {
1801                 try
1802                 {
1803                     log( Level.SEVERE, Messages.getMessage( exception ), exception );
1804                 }
1805                 catch ( final MojoExecutionException e )
1806                 {
1807                     getLog().error( exception );
1808                     getLog().error( e );
1809                 }
1810 
1811                 throw exception;
1812             }
1813 
1814             public void fatalError( final TransformerException exception ) throws TransformerException
1815             {
1816                 try
1817                 {
1818                     log( Level.SEVERE, Messages.getMessage( exception ), exception );
1819                 }
1820                 catch ( final MojoExecutionException e )
1821                 {
1822                     getLog().error( exception );
1823                     getLog().error( e );
1824                 }
1825 
1826                 throw exception;
1827             }
1828 
1829         };
1830 
1831         try
1832         {
1833             if ( url != null )
1834             {
1835                 if ( this.isLoggable( Level.FINER ) )
1836                 {
1837                     this.log( Level.FINER, Messages.getMessage( "loadingTransformer", url.toExternalForm() ), null );
1838                 }
1839 
1840                 final URLConnection con = url.openConnection();
1841                 con.setConnectTimeout( resource.getConnectTimeout() );
1842                 con.setReadTimeout( resource.getReadTimeout() );
1843                 con.connect();
1844                 in = con.getInputStream();
1845 
1846                 final TransformerFactory transformerFactory = TransformerFactory.newInstance();
1847                 transformerFactory.setErrorListener( errorListener );
1848                 final Transformer transformer =
1849                     transformerFactory.newTransformer( new StreamSource( in, url.toURI().toASCIIString() ) );
1850 
1851                 transformer.setErrorListener( errorListener );
1852 
1853                 for ( Map.Entry<Object, Object> e : System.getProperties().entrySet() )
1854                 {
1855                     transformer.setParameter( e.getKey().toString(), e.getValue() );
1856                 }
1857 
1858                 if ( this.getMavenProject().getProperties() != null )
1859                 {
1860                     for ( Map.Entry<Object, Object> e : this.getMavenProject().getProperties().entrySet() )
1861                     {
1862                         transformer.setParameter( e.getKey().toString(), e.getValue() );
1863                     }
1864                 }
1865 
1866                 if ( this.transformationParameterResources != null )
1867                 {
1868                     for ( int i = 0, s0 = this.transformationParameterResources.size(); i < s0; i++ )
1869                     {
1870                         for ( Map.Entry<Object, Object> e :
1871                               this.getProperties( this.transformationParameterResources.get( i ) ).entrySet() )
1872                         {
1873                             transformer.setParameter( e.getKey().toString(), e.getValue() );
1874                         }
1875                     }
1876                 }
1877 
1878                 if ( this.transformationParameters != null )
1879                 {
1880                     for ( TransformationParameter e : this.transformationParameters )
1881                     {
1882                         transformer.setParameter( e.getKey(), e.getObject() );
1883                     }
1884                 }
1885 
1886                 if ( this.transformationOutputProperties != null )
1887                 {
1888                     for ( TransformationOutputProperty e : this.transformationOutputProperties )
1889                     {
1890                         transformer.setOutputProperty( e.getKey(), e.getValue() );
1891                     }
1892                 }
1893 
1894                 for ( int i = 0, s0 = resource.getTransformationParameterResources().size(); i < s0; i++ )
1895                 {
1896                     for ( Map.Entry<Object, Object> e :
1897                           this.getProperties( resource.getTransformationParameterResources().get( i ) ).entrySet() )
1898                     {
1899                         transformer.setParameter( e.getKey().toString(), e.getValue() );
1900                     }
1901                 }
1902 
1903                 for ( TransformationParameter e : resource.getTransformationParameters() )
1904                 {
1905                     transformer.setParameter( e.getKey(), e.getObject() );
1906                 }
1907 
1908                 for ( TransformationOutputProperty e : resource.getTransformationOutputProperties() )
1909                 {
1910                     transformer.setOutputProperty( e.getKey(), e.getValue() );
1911                 }
1912 
1913                 suppressExceptionOnClose = false;
1914                 return transformer;
1915             }
1916             else if ( resource.isOptional() )
1917             {
1918                 if ( this.isLoggable( Level.WARNING ) )
1919                 {
1920                     this.log( Level.WARNING, Messages.getMessage(
1921                         "transformerNotFound", resource.getLocation() ), null );
1922 
1923                 }
1924             }
1925             else
1926             {
1927                 throw new MojoExecutionException( Messages.getMessage(
1928                     "transformerNotFound", resource.getLocation() ) );
1929 
1930             }
1931         }
1932         catch ( final InstantiationException e )
1933         {
1934             throw new MojoExecutionException( Messages.getMessage( e ), e );
1935         }
1936         catch ( final URISyntaxException e )
1937         {
1938             throw new MojoExecutionException( Messages.getMessage( e ), e );
1939         }
1940         catch ( final TransformerConfigurationException e )
1941         {
1942             String m = Messages.getMessage( e );
1943             if ( m == null )
1944             {
1945                 m = Messages.getMessage( e.getException() );
1946             }
1947 
1948             m = m == null ? "" : " " + m;
1949 
1950             throw new MojoExecutionException( Messages.getMessage(
1951                 "failedCreatingTransformer", resource.getLocation(), m ), e );
1952 
1953         }
1954         catch ( final SocketTimeoutException e )
1955         {
1956             String m = Messages.getMessage( e );
1957             m = m == null ? "" : " " + m;
1958 
1959             if ( resource.isOptional() )
1960             {
1961                 if ( this.isLoggable( Level.WARNING ) )
1962                 {
1963                     this.log( Level.WARNING, Messages.getMessage(
1964                         "failedLoadingTransformer", url.toExternalForm(), m ), e );
1965 
1966                 }
1967             }
1968             else
1969             {
1970                 throw new MojoExecutionException( Messages.getMessage(
1971                     "failedLoadingTransformer", url.toExternalForm(), m ), e );
1972 
1973             }
1974         }
1975         catch ( final IOException e )
1976         {
1977             String m = Messages.getMessage( e );
1978             m = m == null ? "" : " " + m;
1979 
1980             if ( resource.isOptional() )
1981             {
1982                 if ( this.isLoggable( Level.WARNING ) )
1983                 {
1984                     this.log( Level.WARNING, Messages.getMessage(
1985                         "failedLoadingTransformer", url.toExternalForm(), m ), e );
1986 
1987                 }
1988             }
1989             else
1990             {
1991                 throw new MojoExecutionException( Messages.getMessage(
1992                     "failedLoadingTransformer", url.toExternalForm(), m ), e );
1993 
1994             }
1995         }
1996         finally
1997         {
1998             try
1999             {
2000                 if ( in != null )
2001                 {
2002                     in.close();
2003                 }
2004             }
2005             catch ( final IOException e )
2006             {
2007                 if ( suppressExceptionOnClose )
2008                 {
2009                     this.getLog().error( e );
2010                 }
2011                 else
2012                 {
2013                     throw new MojoExecutionException( Messages.getMessage( e ), e );
2014                 }
2015             }
2016         }
2017 
2018         return null;
2019     }
2020 
2021     /**
2022      * Creates a new {@code Properties} instance from a {@code PropertiesResourceType}.
2023      *
2024      * @param propertiesResourceType The {@code PropertiesResourceType} specifying the properties to create.
2025      *
2026      * @return The properties for {@code propertiesResourceType}.
2027      *
2028      * @throws NullPointerException if {@code propertiesResourceType} is {@code null}.
2029      * @throws MojoExecutionException if loading properties fails.
2030      *
2031      * @see #getResource(java.lang.String)
2032      * @since 1.2
2033      */
2034     protected Properties getProperties( final PropertiesResourceType propertiesResourceType )
2035         throws MojoExecutionException
2036     {
2037         if ( propertiesResourceType == null )
2038         {
2039             throw new NullPointerException( "propertiesResourceType" );
2040         }
2041 
2042         InputStream in = null;
2043         boolean suppressExceptionOnClose = true;
2044         final URL url = this.getResource( propertiesResourceType.getLocation() );
2045         final Properties properties = new Properties();
2046 
2047         try
2048         {
2049             if ( url != null )
2050             {
2051                 if ( this.isLoggable( Level.FINER ) )
2052                 {
2053                     this.log( Level.FINER, Messages.getMessage( "loadingProperties", url.toExternalForm() ), null );
2054                 }
2055 
2056                 final URLConnection con = url.openConnection();
2057                 con.setConnectTimeout( propertiesResourceType.getConnectTimeout() );
2058                 con.setReadTimeout( propertiesResourceType.getReadTimeout() );
2059                 con.connect();
2060 
2061                 in = con.getInputStream();
2062 
2063                 if ( PropertiesResourceType.PLAIN_FORMAT.equalsIgnoreCase( propertiesResourceType.getFormat() ) )
2064                 {
2065                     properties.load( in );
2066                 }
2067                 else if ( PropertiesResourceType.XML_FORMAT.equalsIgnoreCase( propertiesResourceType.getFormat() ) )
2068                 {
2069                     properties.loadFromXML( in );
2070                 }
2071             }
2072             else if ( propertiesResourceType.isOptional() )
2073             {
2074                 if ( this.isLoggable( Level.WARNING ) )
2075                 {
2076                     this.log( Level.WARNING, Messages.getMessage(
2077                         "propertiesNotFound", propertiesResourceType.getLocation() ), null );
2078 
2079                 }
2080             }
2081             else
2082             {
2083                 throw new MojoExecutionException( Messages.getMessage(
2084                     "propertiesNotFound", propertiesResourceType.getLocation() ) );
2085 
2086             }
2087 
2088             suppressExceptionOnClose = false;
2089         }
2090         catch ( final SocketTimeoutException e )
2091         {
2092             String m = Messages.getMessage( e );
2093             m = m == null ? "" : " " + m;
2094 
2095             if ( propertiesResourceType.isOptional() )
2096             {
2097                 if ( this.isLoggable( Level.WARNING ) )
2098                 {
2099                     this.log( Level.WARNING, Messages.getMessage(
2100                         "failedLoadingProperties", url.toExternalForm(), m ), e );
2101 
2102                 }
2103             }
2104             else
2105             {
2106                 throw new MojoExecutionException( Messages.getMessage(
2107                     "failedLoadingProperties", url.toExternalForm(), m ), e );
2108 
2109             }
2110         }
2111         catch ( final IOException e )
2112         {
2113             String m = Messages.getMessage( e );
2114             m = m == null ? "" : " " + m;
2115 
2116             if ( propertiesResourceType.isOptional() )
2117             {
2118                 if ( this.isLoggable( Level.WARNING ) )
2119                 {
2120                     this.log( Level.WARNING, Messages.getMessage(
2121                         "failedLoadingProperties", url.toExternalForm(), m ), e );
2122 
2123                 }
2124             }
2125             else
2126             {
2127                 throw new MojoExecutionException( Messages.getMessage(
2128                     "failedLoadingProperties", url.toExternalForm(), m ), e );
2129 
2130             }
2131         }
2132         finally
2133         {
2134             try
2135             {
2136                 if ( in != null )
2137                 {
2138                     in.close();
2139                 }
2140             }
2141             catch ( final IOException e )
2142             {
2143                 if ( suppressExceptionOnClose )
2144                 {
2145                     this.getLog().error( e );
2146                 }
2147                 else
2148                 {
2149                     throw new MojoExecutionException( Messages.getMessage( e ), e );
2150                 }
2151             }
2152         }
2153 
2154         return properties;
2155     }
2156 
2157     /**
2158      * Tests if messages at a given level are logged.
2159      *
2160      * @param level The level to test.
2161      *
2162      * @return {@code true}, if messages at {@code level} are logged; {@code false}, if messages at {@code level} are
2163      * suppressed.
2164      *
2165      * @throws NullPointerException if {@code level} is {@code null}.
2166      * @throws MojoExecutionException if testing the level fails.
2167      *
2168      * @see #isVerbose()
2169      * @since 1.2
2170      */
2171     protected boolean isLoggable( final Level level ) throws MojoExecutionException
2172     {
2173         if ( level == null )
2174         {
2175             throw new NullPointerException( "level" );
2176         }
2177 
2178         boolean loggable = false;
2179 
2180         if ( level.intValue() <= Level.CONFIG.intValue() )
2181         {
2182             loggable = this.getLog().isDebugEnabled();
2183         }
2184         else if ( level.intValue() <= Level.INFO.intValue() )
2185         {
2186             loggable = this.getLog().isInfoEnabled() && this.isVerbose();
2187         }
2188         else if ( level.intValue() <= Level.WARNING.intValue() )
2189         {
2190             loggable = this.getLog().isWarnEnabled();
2191         }
2192         else if ( level.intValue() <= Level.SEVERE.intValue() )
2193         {
2194             loggable = this.getLog().isErrorEnabled();
2195         }
2196 
2197         return loggable;
2198     }
2199 
2200     /**
2201      * Logs a separator at a given level.
2202      *
2203      * @param level The level to log a separator at.
2204      *
2205      * @throws MojoExecutionException if logging fails.
2206      *
2207      * @deprecated As of JOMC 1.1, please use method {@link #logSeparator()}. This method will be removed in version
2208      * 2.0.
2209      */
2210     @Deprecated
2211     protected void logSeparator( final Level level ) throws MojoExecutionException
2212     {
2213         this.logSeparator();
2214     }
2215 
2216     /**
2217      * Logs a separator.
2218      *
2219      * @throws MojoExecutionException if logging fails.
2220      *
2221      * @since 1.1
2222      */
2223     protected void logSeparator() throws MojoExecutionException
2224     {
2225         if ( this.isLoggable( Level.INFO ) )
2226         {
2227             this.log( Level.INFO, Messages.getMessage( "separator" ), null );
2228         }
2229     }
2230 
2231     /**
2232      * Logs a message stating a tool is starting to process a module.
2233      *
2234      * @param toolName The tool starting execution.
2235      * @param module The module getting processed.
2236      *
2237      * @throws MojoExecutionException if logging fails.
2238      */
2239     protected void logProcessingModule( final String toolName, final String module ) throws MojoExecutionException
2240     {
2241         if ( this.isLoggable( Level.INFO ) )
2242         {
2243             this.log( Level.INFO, Messages.getMessage( "processingModule", toolName, module ), null );
2244         }
2245     }
2246 
2247     /**
2248      * Logs a message stating a tool is starting to process a model.
2249      *
2250      * @param toolName The tool starting execution.
2251      * @param model The model getting processed.
2252      *
2253      * @throws MojoExecutionException if logging fails.
2254      *
2255      * @since 1.1
2256      */
2257     protected void logProcessingModel( final String toolName, final String model ) throws MojoExecutionException
2258     {
2259         if ( this.isLoggable( Level.INFO ) )
2260         {
2261             this.log( Level.INFO, Messages.getMessage( "processingModel", toolName, model ), null );
2262         }
2263     }
2264 
2265     /**
2266      * Logs a message stating that a module has not been found.
2267      *
2268      * @param module The module not having been found.
2269      *
2270      * @throws MojoExecutionException if logging fails.
2271      */
2272     protected void logMissingModule( final String module ) throws MojoExecutionException
2273     {
2274         if ( this.isLoggable( Level.WARNING ) )
2275         {
2276             this.log( Level.WARNING, Messages.getMessage( "missingModule", module ), null );
2277         }
2278     }
2279 
2280     /**
2281      * Logs a message stating that a tool successfully completed execution.
2282      *
2283      * @param toolName The name of the tool.
2284      *
2285      * @throws MojoExecutionException if logging fails.
2286      */
2287     protected void logToolSuccess( final String toolName ) throws MojoExecutionException
2288     {
2289         if ( this.isLoggable( Level.INFO ) )
2290         {
2291             this.log( Level.INFO, Messages.getMessage( "toolSuccess", toolName ), null );
2292         }
2293     }
2294 
2295     /**
2296      * Logs a {@code ModelValidationReport}.
2297      *
2298      * @param context The context to use when marshalling detail elements of the report.
2299      * @param level The level to log at.
2300      * @param report The report to log.
2301      *
2302      * @throws MojoExecutionException if logging {@code report} fails.
2303      */
2304     protected void log( final ModelContext context, final Level level, final ModelValidationReport report )
2305         throws MojoExecutionException
2306     {
2307         try
2308         {
2309             if ( !report.getDetails().isEmpty() )
2310             {
2311                 this.logSeparator();
2312                 Marshaller marshaller = null;
2313 
2314                 for ( ModelValidationReport.Detail detail : report.getDetails() )
2315                 {
2316                     this.log( detail.getLevel(), "o " + detail.getMessage(), null );
2317 
2318                     if ( detail.getElement() != null && this.isLoggable( Level.FINEST ) )
2319                     {
2320                         if ( marshaller == null )
2321                         {
2322                             marshaller = context.createMarshaller( this.getModel() );
2323                             marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE );
2324                         }
2325 
2326                         final StringWriter stringWriter = new StringWriter();
2327                         marshaller.marshal( detail.getElement(), stringWriter );
2328                         this.log( Level.FINEST, stringWriter.toString(), null );
2329                     }
2330                 }
2331             }
2332         }
2333         catch ( final ModelException e )
2334         {
2335             throw new MojoExecutionException( Messages.getMessage( e ), e );
2336         }
2337         catch ( final JAXBException e )
2338         {
2339             String message = Messages.getMessage( e );
2340             if ( message == null && e.getLinkedException() != null )
2341             {
2342                 message = Messages.getMessage( e.getLinkedException() );
2343             }
2344 
2345             throw new MojoExecutionException( message, e );
2346         }
2347     }
2348 
2349     /**
2350      * Logs a message and throwable at a given level.
2351      *
2352      * @param level The level to log at.
2353      * @param message The message to log or {@code null}.
2354      * @param throwable The throwable to log or {@code null}.
2355      *
2356      * @throws MojoExecutionException if logging fails.
2357      */
2358     protected void log( final Level level, final String message, final Throwable throwable )
2359         throws MojoExecutionException
2360     {
2361         BufferedReader reader = null;
2362         boolean suppressExceptionOnClose = true;
2363 
2364         try
2365         {
2366             if ( this.isLoggable( level ) )
2367             {
2368                 String line;
2369                 reader = new BufferedReader( new StringReader( message == null ? "" : message ) );
2370                 boolean throwableLogged = false;
2371 
2372                 while ( ( line = reader.readLine() ) != null )
2373                 {
2374                     final String mojoMessage =
2375                         Messages.getMessage( this.getLog().isDebugEnabled() ? "debugMessage" : "logMessage", line,
2376                                              Thread.currentThread().getName(), new Date( System.currentTimeMillis() ) );
2377 
2378                     if ( level.intValue() <= Level.CONFIG.intValue() )
2379                     {
2380                         this.getLog().debug( mojoMessage, throwableLogged ? null : throwable );
2381                     }
2382                     else if ( level.intValue() <= Level.INFO.intValue() )
2383                     {
2384                         this.getLog().info( mojoMessage, throwableLogged ? null : throwable );
2385                     }
2386                     else if ( level.intValue() <= Level.WARNING.intValue() )
2387                     {
2388                         this.getLog().warn( mojoMessage, throwableLogged ? null : throwable );
2389                     }
2390                     else if ( level.intValue() <= Level.SEVERE.intValue() )
2391                     {
2392                         this.getLog().error( mojoMessage, throwableLogged ? null : throwable );
2393                     }
2394 
2395                     throwableLogged = true;
2396                 }
2397             }
2398 
2399             suppressExceptionOnClose = false;
2400         }
2401         catch ( final IOException e )
2402         {
2403             this.getLog().error( e );
2404             throw new AssertionError( e );
2405         }
2406         finally
2407         {
2408             try
2409             {
2410                 if ( reader != null )
2411                 {
2412                     reader.close();
2413                 }
2414             }
2415             catch ( final IOException e )
2416             {
2417                 if ( !suppressExceptionOnClose )
2418                 {
2419                     throw new AssertionError( e );
2420                 }
2421             }
2422         }
2423     }
2424 
2425     /**
2426      * Configures a {@code ModelContext} instance.
2427      *
2428      * @param context The model context to configure.
2429      *
2430      * @throws NullPointerException if {@code context} is {@code null}.
2431      * @throws MojoExecutionException if configuring {@code context} fails.
2432      */
2433     protected void setupModelContext( final ModelContext context ) throws MojoExecutionException
2434     {
2435         if ( context == null )
2436         {
2437             throw new NullPointerException( "context" );
2438         }
2439 
2440         if ( this.isVerbose() || this.getLog().isDebugEnabled() )
2441         {
2442             context.setLogLevel( this.getLog().isDebugEnabled() ? Level.ALL : Level.INFO );
2443         }
2444 
2445         try
2446         {
2447             context.setModletSchemaSystemId( this.modletSchemaSystemId );
2448             context.getListeners().add( new ModelContext.Listener()
2449             {
2450 
2451                 @Override
2452                 public void onLog( final Level level, final String message, final Throwable t )
2453                 {
2454                     super.onLog( level, message, t );
2455 
2456                     try
2457                     {
2458                         log( level, message, t );
2459                     }
2460                     catch ( final MojoExecutionException e )
2461                     {
2462                         getLog().error( e );
2463                     }
2464                 }
2465 
2466             } );
2467 
2468             if ( this.providerLocation != null )
2469             {
2470                 context.setAttribute( DefaultModelContext.PROVIDER_LOCATION_ATTRIBUTE_NAME, this.providerLocation );
2471             }
2472 
2473             if ( this.platformProviderLocation != null )
2474             {
2475                 context.setAttribute( DefaultModelContext.PLATFORM_PROVIDER_LOCATION_ATTRIBUTE_NAME,
2476                                       this.platformProviderLocation );
2477 
2478             }
2479 
2480             if ( this.modletLocation != null )
2481             {
2482                 context.setAttribute( DefaultModletProvider.MODLET_LOCATION_ATTRIBUTE_NAME, this.modletLocation );
2483             }
2484 
2485             if ( this.transformerLocation != null )
2486             {
2487                 context.setAttribute( DefaultModelProcessor.TRANSFORMER_LOCATION_ATTRIBUTE_NAME,
2488                                       this.transformerLocation );
2489             }
2490 
2491             if ( this.moduleLocation != null )
2492             {
2493                 context.setAttribute( DefaultModelProvider.MODULE_LOCATION_ATTRIBUTE_NAME, this.moduleLocation );
2494             }
2495 
2496             context.setAttribute( ToolsModelProvider.MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME,
2497                                   this.modelObjectClasspathResolutionEnabled );
2498 
2499             context.setAttribute( ToolsModelProcessor.MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME,
2500                                   this.modelObjectClasspathResolutionEnabled );
2501 
2502             context.setAttribute( DefaultModletProvider.VALIDATING_ATTRIBUTE_NAME, this.modletResourceValidationEnabled );
2503             context.setAttribute( DefaultModelProvider.VALIDATING_ATTRIBUTE_NAME, this.modelResourceValidationEnabled );
2504 
2505             if ( this.modelContextAttributes != null )
2506             {
2507                 for ( ModelContextAttribute e : this.modelContextAttributes )
2508                 {
2509                     final Object object = e.getObject();
2510 
2511                     if ( object != null )
2512                     {
2513                         context.setAttribute( e.getKey(), object );
2514                     }
2515                     else
2516                     {
2517                         context.clearAttribute( e.getKey() );
2518                     }
2519                 }
2520             }
2521         }
2522         catch ( final InstantiationException e )
2523         {
2524             throw new MojoExecutionException( Messages.getMessage( e ), e );
2525         }
2526     }
2527 
2528     /**
2529      * Configures a {@code JomcTool} instance.
2530      *
2531      * @param context The model context to use for configuring {@code tool}.
2532      * @param tool The tool to configure.
2533      *
2534      * @throws NullPointerException if {@code context} of {@code tool} is {@code null}.
2535      * @throws MojoExecutionException if configuring {@code tool} fails.
2536      */
2537     protected void setupJomcTool( final ModelContext context, final JomcTool tool ) throws MojoExecutionException
2538     {
2539         if ( context == null )
2540         {
2541             throw new NullPointerException( "context" );
2542         }
2543         if ( tool == null )
2544         {
2545             throw new NullPointerException( "tool" );
2546         }
2547 
2548         try
2549         {
2550             if ( this.isVerbose() || this.getLog().isDebugEnabled() )
2551             {
2552                 tool.setLogLevel( this.getLog().isDebugEnabled() ? Level.ALL : Level.INFO );
2553             }
2554 
2555             tool.getListeners().add( new JomcTool.Listener()
2556             {
2557 
2558                 @Override
2559                 public void onLog( final Level level, final String message, final Throwable t )
2560                 {
2561                     super.onLog( level, message, t );
2562 
2563                     try
2564                     {
2565                         log( level, message, t );
2566                     }
2567                     catch ( final MojoExecutionException e )
2568                     {
2569                         getLog().error( e );
2570                     }
2571                 }
2572 
2573             } );
2574 
2575             tool.setTemplateEncoding( this.templateEncoding );
2576             tool.setInputEncoding( this.sourceEncoding );
2577             tool.setOutputEncoding( this.sourceEncoding );
2578             tool.setDefaultTemplateProfile( this.defaultTemplateProfile );
2579             tool.setTemplateProfile( this.templateProfile );
2580             tool.setModel( this.getModel( context ) );
2581 
2582             if ( this.indentation != null )
2583             {
2584                 tool.setIndentation( StringEscapeUtils.unescapeJava( this.indentation ) );
2585             }
2586 
2587             if ( this.lineSeparator != null )
2588             {
2589                 tool.setLineSeparator( StringEscapeUtils.unescapeJava( this.lineSeparator ) );
2590             }
2591 
2592             if ( this.locale != null )
2593             {
2594                 tool.setLocale( new Locale( StringUtils.defaultString( this.locale.getLanguage() ),
2595                                             StringUtils.defaultString( this.locale.getCountry() ),
2596                                             StringUtils.defaultString( this.locale.getVariant() ) ) );
2597 
2598             }
2599 
2600             if ( this.velocityPropertyResources != null )
2601             {
2602                 for ( int i = 0, s0 = this.velocityPropertyResources.size(); i < s0; i++ )
2603                 {
2604                     for ( Map.Entry<Object, Object> e :
2605                           this.getProperties( this.velocityPropertyResources.get( i ) ).entrySet() )
2606                     {
2607                         if ( e.getValue() != null )
2608                         {
2609                             tool.getVelocityEngine().setProperty( e.getKey().toString(), e );
2610                         }
2611                         else
2612                         {
2613                             tool.getVelocityEngine().clearProperty( e.getKey().toString() );
2614                         }
2615                     }
2616                 }
2617             }
2618 
2619             if ( this.velocityProperties != null )
2620             {
2621                 for ( VelocityProperty e : this.velocityProperties )
2622                 {
2623                     final Object object = e.getObject();
2624 
2625                     if ( object != null )
2626                     {
2627                         tool.getVelocityEngine().setProperty( e.getKey(), object );
2628                     }
2629                     else
2630                     {
2631                         tool.getVelocityEngine().clearProperty( e.getKey() );
2632                     }
2633                 }
2634             }
2635 
2636             for ( Map.Entry<Object, Object> e : System.getProperties().entrySet() )
2637             {
2638                 tool.getTemplateParameters().put( e.getKey().toString(), e.getValue() );
2639             }
2640 
2641             if ( this.getMavenProject().getProperties() != null )
2642             {
2643                 for ( Map.Entry<Object, Object> e : System.getProperties().entrySet() )
2644                 {
2645                     tool.getTemplateParameters().put( e.getKey().toString(), e.getValue() );
2646                 }
2647             }
2648 
2649             if ( this.templateParameterResources != null )
2650             {
2651                 for ( int i = 0, s0 = this.templateParameterResources.size(); i < s0; i++ )
2652                 {
2653                     for ( Map.Entry<Object, Object> e :
2654                           this.getProperties( this.templateParameterResources.get( i ) ).entrySet() )
2655                     {
2656                         if ( e.getValue() != null )
2657                         {
2658                             tool.getTemplateParameters().put( e.getKey().toString(), e.getValue() );
2659                         }
2660                         else
2661                         {
2662                             tool.getTemplateParameters().remove( e.getKey().toString() );
2663                         }
2664                     }
2665                 }
2666             }
2667 
2668             if ( this.templateParameters != null )
2669             {
2670                 for ( TemplateParameter e : this.templateParameters )
2671                 {
2672                     final Object object = e.getObject();
2673 
2674                     if ( object != null )
2675                     {
2676                         tool.getTemplateParameters().put( e.getKey(), object );
2677                     }
2678                     else
2679                     {
2680                         tool.getTemplateParameters().remove( e.getKey() );
2681                     }
2682                 }
2683             }
2684 
2685             if ( this.templateLocation != null )
2686             {
2687                 final URL url = this.getDirectory( this.templateLocation );
2688                 tool.setTemplateLocation( url );
2689 
2690                 if ( url == null && this.isLoggable( Level.WARNING ) )
2691                 {
2692                     this.log( Level.WARNING, Messages.getMessage( "locationNotFound", this.templateLocation ), null );
2693                 }
2694             }
2695         }
2696         catch ( final InstantiationException e )
2697         {
2698             throw new MojoExecutionException( Messages.getMessage( e ), e );
2699         }
2700         catch ( final IOException e )
2701         {
2702             throw new MojoExecutionException( Messages.getMessage( e ), e );
2703         }
2704     }
2705 
2706     private Artifact getPluginArtifact( final Artifact a )
2707     {
2708         for ( int i = 0, s0 = this.pluginArtifacts.size(); i < s0; i++ )
2709         {
2710             final Artifact pluginArtifact = this.pluginArtifacts.get( i );
2711 
2712             if ( pluginArtifact.getGroupId().equals( a.getGroupId() )
2713                  && pluginArtifact.getArtifactId().equals( a.getArtifactId() )
2714                  && ( pluginArtifact.hasClassifier()
2715                       ? pluginArtifact.getClassifier().equals( a.getClassifier() )
2716                       : !a.hasClassifier() ) )
2717             {
2718                 return pluginArtifact;
2719             }
2720         }
2721 
2722         return null;
2723     }
2724 
2725 }