View Javadoc
1   /*
2    *   Copyright (C) 2005 Christian Schulte <cs@schulte.it>
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 5328 2016-09-01 01:05:43Z schulte $
29   *
30   */
31  package org.jomc.mojo;
32  
33  import java.io.BufferedReader;
34  import java.io.File;
35  import java.io.FileInputStream;
36  import java.io.FileOutputStream;
37  import java.io.IOException;
38  import java.io.InputStream;
39  import java.io.OutputStream;
40  import java.io.StringReader;
41  import java.io.StringWriter;
42  import java.net.HttpURLConnection;
43  import java.net.MalformedURLException;
44  import java.net.SocketTimeoutException;
45  import java.net.URI;
46  import java.net.URISyntaxException;
47  import java.net.URL;
48  import java.net.URLClassLoader;
49  import java.net.URLConnection;
50  import java.util.Collection;
51  import java.util.Date;
52  import java.util.HashSet;
53  import java.util.Iterator;
54  import java.util.List;
55  import java.util.Locale;
56  import java.util.Map;
57  import java.util.Properties;
58  import java.util.Set;
59  import java.util.concurrent.ExecutorService;
60  import java.util.concurrent.Executors;
61  import java.util.concurrent.ThreadFactory;
62  import java.util.concurrent.atomic.AtomicInteger;
63  import java.util.logging.Level;
64  import javax.xml.bind.JAXBException;
65  import javax.xml.bind.Marshaller;
66  import javax.xml.transform.ErrorListener;
67  import javax.xml.transform.Transformer;
68  import javax.xml.transform.TransformerConfigurationException;
69  import javax.xml.transform.TransformerException;
70  import javax.xml.transform.TransformerFactory;
71  import javax.xml.transform.stream.StreamSource;
72  import org.apache.commons.lang.StringEscapeUtils;
73  import org.apache.commons.lang.StringUtils;
74  import org.apache.maven.artifact.Artifact;
75  import org.apache.maven.artifact.ArtifactUtils;
76  import org.apache.maven.execution.MavenSession;
77  import org.apache.maven.plugin.AbstractMojo;
78  import org.apache.maven.plugin.MojoExecutionException;
79  import org.apache.maven.plugin.MojoFailureException;
80  import org.apache.maven.plugin.descriptor.MojoDescriptor;
81  import org.apache.maven.plugins.annotations.Parameter;
82  import org.apache.maven.project.MavenProject;
83  import org.jomc.model.Module;
84  import org.jomc.model.Modules;
85  import org.jomc.model.modlet.DefaultModelProcessor;
86  import org.jomc.model.modlet.DefaultModelProvider;
87  import org.jomc.model.modlet.DefaultModelValidator;
88  import org.jomc.model.modlet.ModelHelper;
89  import org.jomc.modlet.DefaultModelContext;
90  import org.jomc.modlet.DefaultModletProvider;
91  import org.jomc.modlet.Model;
92  import org.jomc.modlet.ModelContext;
93  import org.jomc.modlet.ModelContextFactory;
94  import org.jomc.modlet.ModelException;
95  import org.jomc.modlet.ModelValidationReport;
96  import org.jomc.modlet.Modlet;
97  import org.jomc.modlet.Modlets;
98  import org.jomc.tools.ClassFileProcessor;
99  import org.jomc.tools.JomcTool;
100 import org.jomc.tools.ResourceFileProcessor;
101 import org.jomc.tools.SourceFileProcessor;
102 import org.jomc.tools.modlet.ToolsModelProcessor;
103 import org.jomc.tools.modlet.ToolsModelProvider;
104 
105 /**
106  * Base class for executing {@code JomcTool}s.
107  *
108  * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
109  * @version $JOMC: AbstractJomcMojo.java 5328 2016-09-01 01:05:43Z schulte $
110  */
111 public abstract class AbstractJomcMojo extends AbstractMojo
112 {
113 
114     /**
115      * The encoding to use for reading and writing files.
116      */
117     @Parameter( name = "sourceEncoding",
118                 property = "jomc.sourceEncoding",
119                 defaultValue = "${project.build.sourceEncoding}" )
120     private String sourceEncoding;
121 
122     /**
123      * The encoding to use for reading templates.
124      * <p>
125      * <strong>Deprecated:</strong> As of JOMC 1.3, please use the 'defaultTemplateEncoding' parameter. This
126      * parameter will be removed in version 2.0.
127      * </p>
128      */
129     @Deprecated
130     @Parameter( name = "templateEncoding",
131                 property = "jomc.templateEncoding" )
132     private String templateEncoding;
133 
134     /**
135      * The encoding to use for reading templates.
136      *
137      * @since 1.3
138      */
139     @Parameter( name = "defaultTemplateEncoding",
140                 property = "jomc.defaultTemplateEncoding" )
141     private String defaultTemplateEncoding;
142 
143     /**
144      * Location to search for templates in addition to searching the class path of the plugin.
145      * <p>
146      * First an attempt is made to parse the location value to an URL. On successful parsing, that URL is used.
147      * Otherwise the location value is interpreted as a directory name relative to the base directory of the project.
148      * If that directory exists, that directory is used. If nothing is found at the given location, a warning message is
149      * logged.
150      * </p>
151      *
152      * @since 1.2
153      */
154     @Parameter( name = "templateLocation",
155                 property = "jomc.templateLocation" )
156     private String templateLocation;
157 
158     /**
159      * The template profile to use when accessing templates.
160      */
161     @Parameter( name = "templateProfile",
162                 property = "jomc.templateProfile" )
163     private String templateProfile;
164 
165     /**
166      * The default template profile to use when accessing templates.
167      */
168     @Parameter( name = "defaultTemplateProfile",
169                 property = "jomc.defaultTemplateProfile" )
170     private String defaultTemplateProfile;
171 
172     /**
173      * The location to search for providers.
174      */
175     @Parameter( name = "providerLocation",
176                 property = "jomc.providerLocation" )
177     private String providerLocation;
178 
179     /**
180      * The location to search for platform providers.
181      */
182     @Parameter( name = "platformProviderLocation",
183                 property = "jomc.platformProviderLocation" )
184     private String platformProviderLocation;
185 
186     /**
187      * The identifier of the model to process.
188      */
189     @Parameter( name = "model",
190                 property = "jomc.model",
191                 defaultValue = "http://jomc.org/model" )
192     private String model;
193 
194     /**
195      * The name of the {@code ModelContextFactory} implementation class backing the task.
196      *
197      * @since 1.2
198      */
199     @Parameter( name = "modelContextFactoryClassName",
200                 property = "jomc.modelContextFactoryClassName" )
201     private String modelContextFactoryClassName;
202 
203     /**
204      * The location to search for modlets.
205      */
206     @Parameter( name = "modletLocation",
207                 property = "jomc.modletLocation" )
208     private String modletLocation;
209 
210     /**
211      * The {@code http://jomc.org/modlet} namespace schema system id.
212      *
213      * @since 1.2
214      */
215     @Parameter( name = "modletSchemaSystemId",
216                 property = "jomc.modletSchemaSystemId" )
217     private String modletSchemaSystemId;
218 
219     /**
220      * The location to search for modules.
221      */
222     @Parameter( name = "moduleLocation",
223                 property = "jomc.moduleLocation" )
224     private String moduleLocation;
225 
226     /**
227      * The location to search for transformers.
228      */
229     @Parameter( name = "transformerLocation",
230                 property = "jomc.transformerLocation" )
231     private String transformerLocation;
232 
233     /**
234      * The indentation string ('\t' for tab).
235      */
236     @Parameter( name = "indentation",
237                 property = "jomc.indentation" )
238     private String indentation;
239 
240     /**
241      * The line separator ('\r\n' for DOS, '\r' for Mac, '\n' for Unix).
242      */
243     @Parameter( name = "lineSeparator",
244                 property = "jomc.lineSeparator" )
245     private String lineSeparator;
246 
247     /**
248      * The locale.
249      * <pre>
250      * &lt;locale>
251      *   &lt;language>Lowercase two-letter ISO-639 code.&lt;/language>
252      *   &lt;country>Uppercase two-letter ISO-3166 code.&lt;/country>
253      *   &lt;variant>Vendor and browser specific code.&lt;/variant>
254      * &lt;/locale>
255      * </pre>
256      *
257      * @since 1.2
258      * @see Locale
259      */
260     @Parameter( name = "locale" )
261     private LocaleType locale;
262 
263     /**
264      * Controls verbosity of the plugin.
265      */
266     @Parameter( name = "verbose",
267                 property = "jomc.verbose",
268                 defaultValue = "false" )
269     private boolean verbose;
270 
271     /**
272      * Controls processing of source code files.
273      */
274     @Parameter( name = "sourceProcessingEnabled",
275                 property = "jomc.sourceProcessing",
276                 defaultValue = "true" )
277     private boolean sourceProcessingEnabled;
278 
279     /**
280      * Controls processing of resource files.
281      */
282     @Parameter( name = "resourceProcessingEnabled",
283                 property = "jomc.resourceProcessing",
284                 defaultValue = "true" )
285     private boolean resourceProcessingEnabled;
286 
287     /**
288      * Controls processing of class files.
289      */
290     @Parameter( name = "classProcessingEnabled",
291                 property = "jomc.classProcessing",
292                 defaultValue = "true" )
293     private boolean classProcessingEnabled;
294 
295     /**
296      * Controls processing of models.
297      */
298     @Parameter( name = "modelProcessingEnabled",
299                 property = "jomc.modelProcessing",
300                 defaultValue = "true" )
301     private boolean modelProcessingEnabled;
302 
303     /**
304      * Controls model object class path resolution.
305      */
306     @Parameter( name = "modelObjectClasspathResolutionEnabled",
307                 property = "jomc.modelObjectClasspathResolution",
308                 defaultValue = "true" )
309     private boolean modelObjectClasspathResolutionEnabled;
310 
311     /**
312      * Name of the module to process.
313      */
314     @Parameter( name = "moduleName",
315                 property = "jomc.moduleName",
316                 defaultValue = "${project.name}" )
317     private String moduleName;
318 
319     /**
320      * Name of the test module to process.
321      */
322     @Parameter( name = "testModuleName",
323                 property = "jomc.testModuleName",
324                 defaultValue = "${project.name} Tests" )
325     private String testModuleName;
326 
327     /**
328      * Directory holding the compiled class files of the project.
329      * <p>
330      * <strong>Deprecated:</strong> As of JOMC 1.1, please use the 'outputDirectory' parameter. This parameter will
331      * be removed in version 2.0.
332      * </p>
333      */
334     @Deprecated
335     @Parameter( name = "classesDirectory" )
336     private String classesDirectory;
337 
338     /**
339      * Directory holding the compiled test class files of the project.
340      * <p>
341      * <strong>Deprecated:</strong> As of JOMC 1.1, please use the 'testOutputDirectory' parameter. This parameter
342      * will be removed in version 2.0.
343      * </p>
344      */
345     @Deprecated
346     @Parameter( name = "testClassesDirectory" )
347     private String testClassesDirectory;
348 
349     /**
350      * Output directory of the project.
351      *
352      * @since 1.1
353      */
354     @Parameter( name = "outputDirectory",
355                 property = "jomc.outputDirectory",
356                 defaultValue = "${project.build.outputDirectory}" )
357     private String outputDirectory;
358 
359     /**
360      * Test output directory of the project.
361      *
362      * @since 1.1
363      */
364     @Parameter( name = "testOutputDirectory",
365                 property = "jomc.testOutputDirectory",
366                 defaultValue = "${project.build.testOutputDirectory}" )
367     private String testOutputDirectory;
368 
369     /**
370      * Directory holding the source files of the project.
371      *
372      * @since 1.1
373      */
374     @Parameter( name = "sourceDirectory",
375                 property = "jomc.sourceDirectory",
376                 defaultValue = "${project.build.sourceDirectory}" )
377     private String sourceDirectory;
378 
379     /**
380      * Directory holding the test source files of the project.
381      *
382      * @since 1.1
383      */
384     @Parameter( name = "testSourceDirectory",
385                 property = "jomc.testSourceDirectory",
386                 defaultValue = "${project.build.testSourceDirectory}" )
387     private String testSourceDirectory;
388 
389     /**
390      * Directory holding the session related files of the project.
391      *
392      * @since 1.1
393      */
394     @Parameter( name = "sessionDirectory",
395                 property = "jomc.sessionDirectory",
396                 defaultValue = "${project.build.directory}/jomc-sessions" )
397     private String sessionDirectory;
398 
399     /**
400      * Directory holding the reports of the project.
401      *
402      * @since 1.1
403      */
404     @Parameter( name = "reportOutputDirectory",
405                 property = "jomc.reportOutputDirectory",
406                 defaultValue = "${project.reporting.outputDirectory}" )
407     private String reportOutputDirectory;
408 
409     /**
410      * Velocity runtime properties.
411      * <pre>
412      * &lt;velocityProperties>
413      *   &lt;velocityProperty>
414      *     &lt;key>The name of the property.&lt;/key>
415      *     &lt;value>The value of the property.&lt;/value>
416      *     &lt;type>The name of the class of the properties object.&lt;/type>
417      *   &lt;/velocityProperty>
418      * &lt;/velocityProperties>
419      * </pre>
420      *
421      * @since 1.2
422      */
423     @Parameter( name = "velocityProperties" )
424     private List<VelocityProperty> velocityProperties;
425 
426     /**
427      * Velocity runtime property resources.
428      * <pre>
429      * &lt;velocityPropertyResources>
430      *   &lt;velocityPropertyResource>
431      *     &lt;location>The location of the properties resource.&lt;/location>
432      *     &lt;optional>Flag indicating the properties resource is optional.&lt;/optional>
433      *     &lt;format>The format of the properties resource.&lt;/format>
434      *     &lt;connectTimeout>Timeout value, in milliseconds.&lt;/connectTimeout>
435      *     &lt;readTimeout>Timeout value, in milliseconds.&lt;/readTimeout>
436      *   &lt;/velocityPropertyResource>
437      * &lt;/velocityPropertyResources>
438      * </pre>
439      * <p>
440      * The location value is used to first search the class path of the plugin and the project's main or test class
441      * path. If a class path resource is found, that resource is used. If no class path resource is found, an attempt is
442      * made to parse the location value to an URL. On successful parsing, that URL is used. Otherwise the location value
443      * is interpreted as a file name relative to the base directory of the project. If that file exists, that file is
444      * used. If nothing is found at the given location, depending on the optional flag, a warning message is logged or a
445      * build failure is produced.
446      * </p>
447      * <p>
448      * The optional flag is used to flag the resource optional. When an optional resource is not found, a warning
449      * message is logged instead of producing a build failure.<br/><b>Default value is:</b> false
450      * </p>
451      * <p>
452      * The format value is used to specify the format of the properties resource. Supported values are {@code plain}
453      * and {@code xml}.<br/><b>Default value is:</b> plain
454      * </p>
455      * <p>
456      * The connectTimeout value is used to specify the timeout, in milliseconds, to be used when opening
457      * communications links to the resource. A timeout of zero is interpreted as an infinite timeout.<br/>
458      * <b>Default value is:</b> 60000
459      * </p>
460      * <p>
461      * The readTimeout value is used to specify the timeout, in milliseconds, to be used when reading the resource.
462      * A timeout of zero is interpreted as an infinite timeout.<br/>
463      * <b>Default value is:</b> 60000
464      * </p>
465      *
466      * @since 1.2
467      */
468     @Parameter( name = "velocityPropertyResources" )
469     private List<VelocityPropertyResource> velocityPropertyResources;
470 
471     /**
472      * Template parameters.
473      * <pre>
474      * &lt;templateParameters>
475      *   &lt;templateParameter>
476      *     &lt;key>The name of the parameter.&lt;/key>
477      *     &lt;value>The value of the parameter.&lt;/value>
478      *     &lt;type>The name of the class of the parameter's object.&lt;/type>
479      *   &lt;/templateParameter>
480      * &lt;/templateParameters>
481      * </pre>
482      *
483      * @since 1.2
484      */
485     @Parameter( name = "templateParameters" )
486     private List<TemplateParameter> templateParameters;
487 
488     /**
489      * Template parameter resources.
490      * <pre>
491      * &lt;templateParameterResources>
492      *   &lt;templateParameterResource>
493      *     &lt;location>The location of the properties resource.&lt;/location>
494      *     &lt;optional>Flag indicating the properties resource is optional.&lt;/optional>
495      *     &lt;format>The format of the properties resource.&lt;/format>
496      *     &lt;connectTimeout>Timeout value, in milliseconds.&lt;/connectTimeout>
497      *     &lt;readTimeout>Timeout value, in milliseconds.&lt;/readTimeout>
498      *   &lt;/templateParameterResource>
499      * &lt;/templateParameterResources>
500      * </pre>
501      * <p>
502      * The location value is used to first search the class path of the plugin and the project's main or test class
503      * path. If a class path resource is found, that resource is used. If no class path resource is found, an attempt is
504      * made to parse the location value to an URL. On successful parsing, that URL is used. Otherwise the location value
505      * is interpreted as a file name relative to the base directory of the project. If that file exists, that file is
506      * used. If nothing is found at the given location, depending on the optional flag, a warning message is logged or a
507      * build failure is produced.
508      * </p>
509      * <p>
510      * The optional flag is used to flag the resource optional. When an optional resource is not found, a warning
511      * message is logged instead of producing a build failure.<br/><b>Default value is:</b> false
512      * </p>
513      * <p>
514      * The format value is used to specify the format of the properties resource. Supported values are {@code plain}
515      * and {@code xml}.<br/><b>Default value is:</b> plain
516      * </p>
517      * <p>
518      * The connectTimeout value is used to specify the timeout, in milliseconds, to be used when opening
519      * communications links to the resource. A timeout of zero is interpreted as an infinite timeout.<br/>
520      * <b>Default value is:</b> 60000
521      * </p>
522      * <p>
523      * The readTimeout value is used to specify the timeout, in milliseconds, to be used when reading the resource.
524      * A timeout of zero is interpreted as an infinite timeout.<br/>
525      * <b>Default value is:</b> 60000
526      * </p>
527      *
528      * @since 1.2
529      */
530     @Parameter( name = "templateParameterResources" )
531     private List<TemplateParameterResource> templateParameterResources;
532 
533     /**
534      * Global transformation parameters.
535      * <pre>
536      * &lt;transformationParameters>
537      *   &lt;transformationParameter>
538      *     &lt;key>The name of the parameter.&lt;/key>
539      *     &lt;value>The value of the parameter.&lt;/value>
540      *     &lt;type>The name of the class of the parameter's object.&lt;/type>
541      *   &lt;/transformationParameter>
542      * &lt;/transformationParameters>
543      * </pre>
544      *
545      * @since 1.2
546      */
547     @Parameter( name = "transformationParameters" )
548     private List<TransformationParameter> transformationParameters;
549 
550     /**
551      * Global transformation output properties.
552      * <pre>
553      * &lt;transformationOutputProperties>
554      *   &lt;transformationOutputProperty>
555      *     &lt;key>The name of the property.&lt;/key>
556      *     &lt;value>The value of the property.&lt;/value>
557      *     &lt;type>The name of the class of the properties object.&lt;/type>
558      *   &lt;/transformationOutputProperty>
559      * &lt;/transformationOutputProperties>
560      * </pre>
561      *
562      * @since 1.2
563      */
564     @Parameter( name = "transformationOutputProperties" )
565     private List<TransformationOutputProperty> transformationOutputProperties;
566 
567     /**
568      * Global transformation parameter resources.
569      * <pre>
570      * &lt;transformationParameterResources>
571      *   &lt;transformationParameterResource>
572      *     &lt;location>The location of the properties resource.&lt;/location>
573      *     &lt;optional>Flag indicating the properties resource is optional.&lt;/optional>
574      *     &lt;format>The format of the properties resource.&lt;/format>
575      *     &lt;connectTimeout>Timeout value, in milliseconds.&lt;/connectTimeout>
576      *     &lt;readTimeout>Timeout value, in milliseconds.&lt;/readTimeout>
577      *   &lt;/transformationParameterResource>
578      * &lt;/transformationParameterResources>
579      * </pre>
580      * <p>
581      * The location value is used to first search the class path of the plugin and the project's main or test class
582      * path. If a class path resource is found, that resource is used. If no class path resource is found, an attempt is
583      * made to parse the location value to an URL. On successful parsing, that URL is used. Otherwise the location value
584      * is interpreted as a file name relative to the base directory of the project. If that file exists, that file is
585      * used. If nothing is found at the given location, depending on the optional flag, a warning message is logged or a
586      * build failure is produced.
587      * </p>
588      * <p>
589      * The optional flag is used to flag the resource optional. When an optional resource is not found, a warning
590      * message is logged instead of producing a build failure.<br/><b>Default value is:</b> false
591      * </p>
592      * <p>
593      * The format value is used to specify the format of the properties resource. Supported values are {@code plain}
594      * and {@code xml}.<br/><b>Default value is:</b> plain
595      * </p>
596      * <p>
597      * The connectTimeout value is used to specify the timeout, in milliseconds, to be used when opening
598      * communications links to the resource. A timeout of zero is interpreted as an infinite timeout.<br/>
599      * <b>Default value is:</b> 60000
600      * </p>
601      * <p>
602      * The readTimeout value is used to specify the timeout, in milliseconds, to be used when reading the resource.
603      * A timeout of zero is interpreted as an infinite timeout.<br/>
604      * <b>Default value is:</b> 60000
605      * </p>
606      *
607      * @since 1.2
608      */
609     @Parameter( name = "transformationParameterResources" )
610     private List<TransformationParameterResource> transformationParameterResources;
611 
612     /**
613      * Class name of the {@code ClassFileProcessor} backing the goal.
614      *
615      * @since 1.2
616      */
617     @Parameter( name = "classFileProcessorClassName",
618                 property = "jomc.classFileProcessorClassName",
619                 defaultValue = "org.jomc.tools.ClassFileProcessor" )
620     private String classFileProcessorClassName;
621 
622     /**
623      * Class name of the {@code ResourceFileProcessor} backing the goal.
624      *
625      * @since 1.2
626      */
627     @Parameter( name = "resourceFileProcessorClassName",
628                 property = "jomc.resourceFileProcessorClassName",
629                 defaultValue = "org.jomc.tools.ResourceFileProcessor" )
630     private String resourceFileProcessorClassName;
631 
632     /**
633      * Class name of the {@code SourceFileProcessor} backing the goal.
634      *
635      * @since 1.2
636      */
637     @Parameter( name = "sourceFileProcessorClassName",
638                 property = "jomc.sourceFileProcessorClassName",
639                 defaultValue = "org.jomc.tools.SourceFileProcessor" )
640     private String sourceFileProcessorClassName;
641 
642     /**
643      * {@code ModelContext} attributes.
644      * <pre>
645      * &lt;modelContextAttributes>
646      *   &lt;modelContextAttribute>
647      *     &lt;key>The name of the attribute.&lt;/key>
648      *     &lt;value>The value of the attribute.&lt;/value>
649      *     &lt;type>The name of the class of the attributes's object.&lt;/type>
650      *   &lt;/modelContextAttribute>
651      * &lt;/modelContextAttributes>
652      * </pre>
653      *
654      * @since 1.2
655      */
656     @Parameter( name = "modelContextAttributes" )
657     private List<ModelContextAttribute> modelContextAttributes;
658 
659     /**
660      * Flag controlling JAXP schema validation of model resources.
661      *
662      * @since 1.2
663      */
664     @Parameter( name = "modelResourceValidationEnabled",
665                 property = "jomc.modelResourceValidationEnabled",
666                 defaultValue = "true" )
667     private boolean modelResourceValidationEnabled;
668 
669     /**
670      * Flag controlling JAXP schema validation of modlet resources.
671      *
672      * @since 1.2
673      */
674     @Parameter( name = "modletResourceValidationEnabled",
675                 property = "jomc.modletResourceValidationEnabled",
676                 defaultValue = "true" )
677     private boolean modletResourceValidationEnabled;
678 
679     /**
680      * Flag controlling Java validation.
681      *
682      * @since 1.4
683      */
684     @Parameter( name = "javaValidationEnabled",
685                 property = "jomc.javaValidationEnabled",
686                 defaultValue = "true" )
687     private boolean javaValidationEnabled;
688 
689     /**
690      * Names of modlets to exclude.
691      *
692      * @since 1.6
693      */
694     @Parameter( name = "modletExcludes",
695                 property = "jomc.modletExcludes" )
696     private List<String> modletExcludes;
697 
698     /**
699      * Names of modlets to include.
700      *
701      * @since 1.6
702      */
703     @Parameter( name = "modletIncludes",
704                 property = "jomc.modletIncludes" )
705     private List<String> modletIncludes;
706 
707     /**
708      * A formula used to calculate the maximum number of threads to create for running tasks in parallel. If the
709      * formular contains the character {@code C}, the number of threads will be calculated by multiplying the value by
710      * the number of available processors. The default number of threads is the number of available processors (1.0C).
711      *
712      * @since 1.10
713      */
714     @Parameter( name = "threads",
715                 property = "jomc.threads",
716                 defaultValue = "1.0C" )
717     private String threads;
718 
719     /**
720      * The Maven project of the instance.
721      */
722     @Parameter( name = "mavenProject",
723                 defaultValue = "${project}",
724                 readonly = true,
725                 required = true )
726     private MavenProject mavenProject;
727 
728     /**
729      * List of plugin artifacts.
730      */
731     @Parameter( name = "pluginArtifacts",
732                 defaultValue = "${plugin.artifacts}",
733                 readonly = true,
734                 required = true )
735     private List<Artifact> pluginArtifacts;
736 
737     /**
738      * The Maven session of the instance.
739      *
740      * @since 1.1
741      */
742     @Parameter( name = "mavenSession",
743                 defaultValue = "${session}",
744                 readonly = true,
745                 required = true )
746     private MavenSession mavenSession;
747 
748     /**
749      * The executor service, if using threads.
750      *
751      * @since 1.10
752      */
753     private ExecutorService executorService;
754 
755     /**
756      * Creates a new {@code AbstractJomcMojo} instance.
757      */
758     public AbstractJomcMojo()
759     {
760         super();
761     }
762 
763     /**
764      * {@inheritDoc}
765      *
766      * @see #assertValidParameters()
767      * @see #isExecutionPermitted()
768      * @see #executeTool()
769      */
770     @SuppressWarnings( "deprecation" )
771     public void execute() throws MojoExecutionException, MojoFailureException
772     {
773         this.assertValidParameters();
774 
775         try
776         {
777             this.logSeparator();
778 
779             if ( this.isLoggable( Level.INFO ) )
780             {
781                 this.log( Level.INFO, Messages.getMessage( "title" ), null );
782             }
783 
784             if ( this.isExecutionPermitted() )
785             {
786                 this.executeTool();
787             }
788             else if ( this.isLoggable( Level.INFO ) )
789             {
790                 this.log( Level.INFO, Messages.getMessage( "executionSuppressed", this.getExecutionStrategy() ), null );
791             }
792         }
793         catch ( final Exception e )
794         {
795             throw new MojoExecutionException( Messages.getMessage( e ), e );
796         }
797         finally
798         {
799             try
800             {
801                 JomcTool.setDefaultTemplateProfile( null );
802                 this.logSeparator();
803             }
804             finally
805             {
806                 if ( this.executorService != null )
807                 {
808                     this.executorService.shutdown();
809                     this.executorService = null;
810                 }
811             }
812         }
813     }
814 
815     /**
816      * Validates the parameters of the goal.
817      *
818      * @throws MojoFailureException if illegal parameter values are detected.
819      *
820      * @see #assertValidResources(java.util.Collection)
821      * @since 1.2
822      */
823     protected void assertValidParameters() throws MojoFailureException
824     {
825         this.assertValidResources( this.templateParameterResources );
826         this.assertValidResources( this.transformationParameterResources );
827         this.assertValidResources( this.velocityPropertyResources );
828     }
829 
830     /**
831      * Validates a given resource collection.
832      *
833      * @param resources The resource collection to validate or {@code null}.
834      *
835      * @throws MojoFailureException if a location property of a given resource holds a {@code null} value or a given
836      * {@code PropertiesResourceType} holds an illegal format.
837      *
838      * @see #assertValidParameters()
839      * @see PropertiesResourceType#isFormatSupported(java.lang.String)
840      * @since 1.2
841      */
842     protected final void assertValidResources( final Collection<? extends ResourceType> resources )
843         throws MojoFailureException
844     {
845         if ( resources != null )
846         {
847             for ( final ResourceType r : resources )
848             {
849                 if ( r.getLocation() == null )
850                 {
851                     throw new MojoFailureException( Messages.getMessage( "mandatoryParameter", "location" ) );
852                 }
853 
854                 if ( r instanceof PropertiesResourceType )
855                 {
856                     final PropertiesResourceType p = (PropertiesResourceType) r;
857 
858                     if ( !PropertiesResourceType.isFormatSupported( p.getFormat() ) )
859                     {
860                         throw new MojoFailureException( Messages.getMessage(
861                             "illegalPropertiesFormat", p.getFormat(),
862                             StringUtils.join( PropertiesResourceType.getSupportedFormats(), ',' ) ) );
863 
864                     }
865                 }
866             }
867         }
868     }
869 
870     /**
871      * Executes this tool.
872      *
873      * @throws Exception if execution of this tool fails.
874      */
875     protected abstract void executeTool() throws Exception;
876 
877     /**
878      * Gets the goal of the instance.
879      *
880      * @return The goal of the instance.
881      *
882      * @throws MojoExecutionException if getting the goal of the instance fails.
883      * @since 1.1
884      */
885     protected abstract String getGoal() throws MojoExecutionException;
886 
887     /**
888      * Gets the execution strategy of the instance.
889      *
890      * @return The execution strategy of the instance.
891      *
892      * @throws MojoExecutionException if getting the execution strategy of the instance fails.
893      * @since 1.1
894      */
895     protected abstract String getExecutionStrategy() throws MojoExecutionException;
896 
897     /**
898      * Gets a flag indicating the current execution is permitted.
899      *
900      * @return {@code true}, if the current execution is permitted; {@code false}, if the current execution is
901      * suppressed.
902      *
903      * @throws MojoExecutionException if getting the flag fails.
904      *
905      * @since 1.1
906      * @see #getGoal()
907      * @see #getExecutionStrategy()
908      */
909     protected boolean isExecutionPermitted() throws MojoExecutionException
910     {
911         try
912         {
913             boolean permitted = true;
914 
915             if ( MojoDescriptor.SINGLE_PASS_EXEC_STRATEGY.equals( this.getExecutionStrategy() ) )
916             {
917                 final File flagFile =
918                     new File( this.getSessionDirectory(),
919                               ArtifactUtils.versionlessKey( this.getMavenProject().getArtifact() ).hashCode()
920                                   + "-" + this.getGoal()
921                                   + "-" + this.getMavenSession().getStartTime().getTime() + ".flg" );
922 
923                 if ( !this.getSessionDirectory().exists() && !this.getSessionDirectory().mkdirs() )
924                 {
925                     throw new MojoExecutionException( Messages.getMessage(
926                         "failedCreatingDirectory", this.getSessionDirectory().getAbsolutePath() ) );
927 
928                 }
929 
930                 permitted = flagFile.createNewFile();
931             }
932 
933             return permitted;
934         }
935         catch ( final IOException e )
936         {
937             throw new MojoExecutionException( Messages.getMessage( e ), e );
938         }
939     }
940 
941     /**
942      * Gets the {@code ExecutorService} used to run tasks in parallel.
943      *
944      * @return The {@code ExecutorService} used to run tasks in parallel or {@code null}.
945      *
946      * @since 1.10
947      */
948     protected final ExecutorService getExecutorService()
949     {
950         if ( this.executorService == null )
951         {
952             final Double parallelism =
953                 this.threads != null
954                     ? this.threads.toLowerCase( new Locale( "" ) ).contains( "c" )
955                           ? Double.valueOf( this.threads.toLowerCase( new Locale( "" ) ).replace( "c", "" ) )
956                                 * Runtime.getRuntime().availableProcessors()
957                           : Double.valueOf( this.threads )
958                     : 0.0D;
959 
960             if ( parallelism.intValue() > 1 )
961             {
962                 this.executorService = Executors.newFixedThreadPool(
963                     parallelism.intValue(), new ThreadFactory()
964                 {
965 
966                     private final ThreadGroup group;
967 
968                     private final AtomicInteger threadNumber = new AtomicInteger( 1 );
969 
970 
971                     {
972                         final SecurityManager s = System.getSecurityManager();
973                         this.group = s != null
974                                          ? s.getThreadGroup()
975                                          : Thread.currentThread().getThreadGroup();
976 
977                     }
978 
979                     @Override
980                     public Thread newThread( final Runnable r )
981                     {
982                         final Thread t =
983                             new Thread( this.group, r, "maven-jomc-plugin-" + this.threadNumber.getAndIncrement(), 0 );
984 
985                         if ( t.isDaemon() )
986                         {
987                             t.setDaemon( false );
988                         }
989                         if ( t.getPriority() != Thread.NORM_PRIORITY )
990                         {
991                             t.setPriority( Thread.NORM_PRIORITY );
992                         }
993 
994                         return t;
995                     }
996 
997                 } );
998             }
999         }
1000 
1001         return this.executorService;
1002     }
1003 
1004     /**
1005      * Gets the Maven project of the instance.
1006      *
1007      * @return The Maven project of the instance.
1008      *
1009      * @throws MojoExecutionException if getting the Maven project of the instance fails.
1010      */
1011     protected MavenProject getMavenProject() throws MojoExecutionException
1012     {
1013         return this.mavenProject;
1014     }
1015 
1016     /**
1017      * Gets the Maven session of the instance.
1018      *
1019      * @return The Maven session of the instance.
1020      *
1021      * @throws MojoExecutionException if getting the Maven session of the instance fails.
1022      *
1023      * @since 1.1
1024      */
1025     protected MavenSession getMavenSession() throws MojoExecutionException
1026     {
1027         return this.mavenSession;
1028     }
1029 
1030     /**
1031      * Gets an absolute {@code File} instance for a given name.
1032      * <p>
1033      * This method constructs a new {@code File} instance using the given name. If the resulting file is not
1034      * absolute, the value of the {@code basedir} property of the current Maven project is prepended.
1035      * </p>
1036      *
1037      * @param name The name to get an absolute {@code File} instance for.
1038      *
1039      * @return An absolute {@code File} instance constructed from {@code name}.
1040      *
1041      * @throws MojoExecutionException if getting an absolute {@code File} instance for {@code name} fails.
1042      * @throws NullPointerException if {@code name} is {@code null}.
1043      *
1044      * @since 1.1
1045      */
1046     protected File getAbsoluteFile( final String name ) throws MojoExecutionException
1047     {
1048         if ( name == null )
1049         {
1050             throw new NullPointerException( "name" );
1051         }
1052 
1053         File file = new File( name );
1054         if ( !file.isAbsolute() )
1055         {
1056             file = new File( this.getMavenProject().getBasedir(), name );
1057         }
1058 
1059         return file;
1060     }
1061 
1062     /**
1063      * Gets the directory holding the compiled class files of the project.
1064      *
1065      * @return The directory holding the compiled class files of the project.
1066      *
1067      * @throws MojoExecutionException if getting the directory fails.
1068      *
1069      * @since 1.1
1070      */
1071     protected File getOutputDirectory() throws MojoExecutionException
1072     {
1073         if ( this.classesDirectory != null )
1074         {
1075             if ( this.isLoggable( Level.WARNING ) )
1076             {
1077                 this.log( Level.WARNING, Messages.getMessage(
1078                           "deprecationWarning", "classesDirectory", "outputDirectory" ), null );
1079 
1080             }
1081 
1082             if ( !this.classesDirectory.equals( this.outputDirectory ) )
1083             {
1084                 if ( this.isLoggable( Level.WARNING ) )
1085                 {
1086                     this.log( Level.WARNING, Messages.getMessage( "ignoringParameter", "outputDirectory" ), null );
1087                 }
1088 
1089                 this.outputDirectory = this.classesDirectory;
1090             }
1091 
1092             this.classesDirectory = null;
1093         }
1094 
1095         final File dir = this.getAbsoluteFile( this.outputDirectory );
1096         if ( !dir.exists() && !dir.mkdirs() )
1097         {
1098             throw new MojoExecutionException( Messages.getMessage( "failedCreatingDirectory", dir.getAbsolutePath() ) );
1099         }
1100 
1101         return dir;
1102     }
1103 
1104     /**
1105      * Gets the directory holding the compiled test class files of the project.
1106      *
1107      * @return The directory holding the compiled test class files of the project.
1108      *
1109      * @throws MojoExecutionException if getting the directory fails.
1110      *
1111      * @since 1.1
1112      */
1113     protected File getTestOutputDirectory() throws MojoExecutionException
1114     {
1115         if ( this.testClassesDirectory != null )
1116         {
1117             if ( this.isLoggable( Level.WARNING ) )
1118             {
1119                 this.log( Level.WARNING, Messages.getMessage(
1120                           "deprecationWarning", "testClassesDirectory", "testOutputDirectory" ), null );
1121 
1122             }
1123 
1124             if ( !this.testClassesDirectory.equals( this.testOutputDirectory ) )
1125             {
1126                 if ( this.isLoggable( Level.WARNING ) )
1127                 {
1128                     this.log( Level.WARNING, Messages.getMessage( "ignoringParameter", "testOutputDirectory" ), null );
1129                 }
1130 
1131                 this.testOutputDirectory = this.testClassesDirectory;
1132             }
1133 
1134             this.testClassesDirectory = null;
1135         }
1136 
1137         final File dir = this.getAbsoluteFile( this.testOutputDirectory );
1138         if ( !dir.exists() && !dir.mkdirs() )
1139         {
1140             throw new MojoExecutionException( Messages.getMessage( "failedCreatingDirectory", dir.getAbsolutePath() ) );
1141         }
1142 
1143         return dir;
1144     }
1145 
1146     /**
1147      * Gets the directory holding the source files of the project.
1148      *
1149      * @return The directory holding the source files of the project.
1150      *
1151      * @throws MojoExecutionException if getting the directory fails.
1152      *
1153      * @since 1.1
1154      */
1155     protected File getSourceDirectory() throws MojoExecutionException
1156     {
1157         return this.getAbsoluteFile( this.sourceDirectory );
1158     }
1159 
1160     /**
1161      * Gets the directory holding the test source files of the project.
1162      *
1163      * @return The directory holding the test source files of the project.
1164      *
1165      * @throws MojoExecutionException if getting the directory fails.
1166      *
1167      * @since 1.1
1168      */
1169     protected File getTestSourceDirectory() throws MojoExecutionException
1170     {
1171         return this.getAbsoluteFile( this.testSourceDirectory );
1172     }
1173 
1174     /**
1175      * Gets the directory holding the session related files of the project.
1176      *
1177      * @return The directory holding the session related files of the project.
1178      *
1179      * @throws MojoExecutionException if getting the directory fails.
1180      *
1181      * @since 1.1
1182      */
1183     protected File getSessionDirectory() throws MojoExecutionException
1184     {
1185         return this.getAbsoluteFile( this.sessionDirectory );
1186     }
1187 
1188     /**
1189      * Gets the directory holding the reports of the project.
1190      *
1191      * @return The directory holding the reports of the project.
1192      *
1193      * @throws MojoExecutionException if getting the directory fails.
1194      *
1195      * @since 1.1
1196      */
1197     protected File getReportOutputDirectory() throws MojoExecutionException
1198     {
1199         return this.getAbsoluteFile( this.reportOutputDirectory );
1200     }
1201 
1202     /**
1203      * Gets the project's runtime class loader of the instance.
1204      *
1205      * @return The project's runtime class loader of the instance.
1206      *
1207      * @throws MojoExecutionException if getting the class loader fails.
1208      */
1209     protected ClassLoader getMainClassLoader() throws MojoExecutionException
1210     {
1211         try
1212         {
1213             final Set<String> mainClasspathElements = this.getMainClasspathElements();
1214             final Set<URI> uris = new HashSet<URI>( mainClasspathElements.size() );
1215 
1216             for ( final String element : mainClasspathElements )
1217             {
1218                 final URI uri = new File( element ).toURI();
1219                 if ( !uris.contains( uri ) )
1220                 {
1221                     uris.add( uri );
1222                 }
1223             }
1224 
1225             if ( this.isLoggable( Level.FINEST ) )
1226             {
1227                 this.log( Level.FINEST, Messages.getMessage( "mainClasspathInfo" ), null );
1228             }
1229 
1230             int i = 0;
1231             final URL[] urls = new URL[ uris.size() ];
1232             for ( final URI uri : uris )
1233             {
1234                 urls[i++] = uri.toURL();
1235 
1236                 if ( this.isLoggable( Level.FINEST ) )
1237                 {
1238                     this.log( Level.FINEST, "\t" + urls[i - 1].toExternalForm(), null );
1239                 }
1240             }
1241 
1242             return new URLClassLoader( urls, Thread.currentThread().getContextClassLoader() );
1243         }
1244         catch ( final MalformedURLException e )
1245         {
1246             throw new MojoExecutionException( Messages.getMessage( e ), e );
1247         }
1248     }
1249 
1250     /**
1251      * Gets the project's test class loader of the instance.
1252      *
1253      * @return The project's test class loader of the instance.
1254      *
1255      * @throws MojoExecutionException if getting the class loader fails.
1256      */
1257     protected ClassLoader getTestClassLoader() throws MojoExecutionException
1258     {
1259         try
1260         {
1261             final Set<String> testClasspathElements = this.getTestClasspathElements();
1262             final Set<URI> uris = new HashSet<URI>( testClasspathElements.size() );
1263 
1264             for ( final String element : testClasspathElements )
1265             {
1266                 final URI uri = new File( element ).toURI();
1267                 if ( !uris.contains( uri ) )
1268                 {
1269                     uris.add( uri );
1270                 }
1271             }
1272 
1273             if ( this.isLoggable( Level.FINEST ) )
1274             {
1275                 this.log( Level.FINEST, Messages.getMessage( "testClasspathInfo" ), null );
1276             }
1277 
1278             int i = 0;
1279             final URL[] urls = new URL[ uris.size() ];
1280             for ( final URI uri : uris )
1281             {
1282                 urls[i++] = uri.toURL();
1283 
1284                 if ( this.isLoggable( Level.FINEST ) )
1285                 {
1286                     this.log( Level.FINEST, "\t" + urls[i - 1].toExternalForm(), null );
1287                 }
1288             }
1289 
1290             return new URLClassLoader( urls, Thread.currentThread().getContextClassLoader() );
1291         }
1292         catch ( final MalformedURLException e )
1293         {
1294             throw new MojoExecutionException( Messages.getMessage( e ), e );
1295         }
1296     }
1297 
1298     /**
1299      * Gets the project's runtime class path elements.
1300      *
1301      * @return A set of class path element strings.
1302      *
1303      * @throws MojoExecutionException if getting the class path elements fails.
1304      */
1305     protected Set<String> getMainClasspathElements() throws MojoExecutionException
1306     {
1307         final List<?> runtimeArtifacts = this.getMavenProject().getRuntimeArtifacts();
1308         final List<?> compileArtifacts = this.getMavenProject().getCompileArtifacts();
1309         final Set<String> elements = new HashSet<String>( runtimeArtifacts.size() + compileArtifacts.size() + 1 );
1310         elements.add( this.getOutputDirectory().getAbsolutePath() );
1311 
1312         for ( final Iterator<?> it = runtimeArtifacts.iterator(); it.hasNext(); )
1313         {
1314             final Artifact a = (Artifact) it.next();
1315             final Artifact pluginArtifact = this.getPluginArtifact( a );
1316 
1317             if ( a.getFile() == null )
1318             {
1319                 if ( this.isLoggable( Level.WARNING ) )
1320                 {
1321                     this.log( Level.WARNING, Messages.getMessage( "ignoringArtifact", a.toString() ), null );
1322                 }
1323 
1324                 continue;
1325             }
1326 
1327             if ( pluginArtifact != null )
1328             {
1329                 if ( this.isLoggable( Level.FINER ) )
1330                 {
1331                     this.log( Level.FINER, Messages.getMessage(
1332                               "ignoringPluginArtifact", a.toString(), pluginArtifact.toString() ), null );
1333 
1334                 }
1335 
1336                 continue;
1337             }
1338 
1339             final String element = a.getFile().getAbsolutePath();
1340             elements.add( element );
1341         }
1342 
1343         for ( final Iterator<?> it = compileArtifacts.iterator(); it.hasNext(); )
1344         {
1345             final Artifact a = (Artifact) it.next();
1346             final Artifact pluginArtifact = this.getPluginArtifact( a );
1347 
1348             if ( a.getFile() == null )
1349             {
1350                 if ( this.isLoggable( Level.WARNING ) )
1351                 {
1352                     this.log( Level.WARNING, Messages.getMessage( "ignoringArtifact", a.toString() ), null );
1353                 }
1354 
1355                 continue;
1356             }
1357 
1358             if ( pluginArtifact != null )
1359             {
1360                 if ( this.isLoggable( Level.FINER ) )
1361                 {
1362                     this.log( Level.FINER, Messages.getMessage(
1363                               "ignoringPluginArtifact", a.toString(), pluginArtifact.toString() ), null );
1364 
1365                 }
1366 
1367                 continue;
1368             }
1369 
1370             final String element = a.getFile().getAbsolutePath();
1371             elements.add( element );
1372         }
1373 
1374         return elements;
1375     }
1376 
1377     /**
1378      * Gets the project's test class path elements.
1379      *
1380      * @return A set of class path element strings.
1381      *
1382      * @throws MojoExecutionException if getting the class path elements fails.
1383      */
1384     protected Set<String> getTestClasspathElements() throws MojoExecutionException
1385     {
1386         final List<?> testArtifacts = this.getMavenProject().getTestArtifacts();
1387         final Set<String> elements = new HashSet<String>( testArtifacts.size() + 2 );
1388         elements.add( this.getOutputDirectory().getAbsolutePath() );
1389         elements.add( this.getTestOutputDirectory().getAbsolutePath() );
1390 
1391         for ( final Iterator<?> it = testArtifacts.iterator(); it.hasNext(); )
1392         {
1393             final Artifact a = (Artifact) it.next();
1394             final Artifact pluginArtifact = this.getPluginArtifact( a );
1395 
1396             if ( a.getFile() == null )
1397             {
1398                 if ( this.isLoggable( Level.WARNING ) )
1399                 {
1400                     this.log( Level.WARNING, Messages.getMessage( "ignoringArtifact", a.toString() ), null );
1401                 }
1402 
1403                 continue;
1404             }
1405 
1406             if ( pluginArtifact != null )
1407             {
1408                 if ( this.isLoggable( Level.FINER ) )
1409                 {
1410                     this.log( Level.FINER, Messages.getMessage(
1411                               "ignoringPluginArtifact", a.toString(), pluginArtifact.toString() ), null );
1412 
1413                 }
1414 
1415                 continue;
1416             }
1417 
1418             final String element = a.getFile().getAbsolutePath();
1419             elements.add( element );
1420         }
1421 
1422         return elements;
1423     }
1424 
1425     /**
1426      * Gets a flag indicating verbose output is enabled.
1427      *
1428      * @return {@code true}, if verbose output is enabled; {@code false}, if information messages are suppressed.
1429      *
1430      * @throws MojoExecutionException if getting the flag fails.
1431      *
1432      * @since 1.1
1433      */
1434     protected final boolean isVerbose() throws MojoExecutionException
1435     {
1436         return this.verbose;
1437     }
1438 
1439     /**
1440      * Sets the flag indicating verbose output is enabled.
1441      *
1442      * @param value {@code true}, to enable verbose output; {@code false}, to suppress information messages.
1443      *
1444      * @throws MojoExecutionException if setting the flag fails.
1445      *
1446      * @since 1.1
1447      */
1448     protected final void setVerbose( final boolean value ) throws MojoExecutionException
1449     {
1450         this.verbose = value;
1451     }
1452 
1453     /**
1454      * Gets a flag indicating the processing of sources is enabled.
1455      *
1456      * @return {@code true}, if processing of sources is enabled; {@code false}, else.
1457      *
1458      * @throws MojoExecutionException if getting the flag fails.
1459      */
1460     protected final boolean isSourceProcessingEnabled() throws MojoExecutionException
1461     {
1462         return this.sourceProcessingEnabled;
1463     }
1464 
1465     /**
1466      * Sets the flag indicating the processing of sources is enabled.
1467      *
1468      * @param value {@code true}, to enable processing of sources; {@code false}, to disable processing of sources.
1469      *
1470      * @throws MojoExecutionException if setting the flag fails.
1471      *
1472      * @since 1.1
1473      */
1474     protected final void setSourceProcessingEnabled( final boolean value ) throws MojoExecutionException
1475     {
1476         this.sourceProcessingEnabled = value;
1477     }
1478 
1479     /**
1480      * Gets a flag indicating the processing of resources is enabled.
1481      *
1482      * @return {@code true}, if processing of resources is enabled; {@code false}, else.
1483      *
1484      * @throws MojoExecutionException if getting the flag fails.
1485      */
1486     protected final boolean isResourceProcessingEnabled() throws MojoExecutionException
1487     {
1488         return this.resourceProcessingEnabled;
1489     }
1490 
1491     /**
1492      * Sets the flag indicating the processing of resources is enabled.
1493      *
1494      * @param value {@code true}, to enable processing of resources; {@code false}, to disable processing of resources.
1495      *
1496      * @throws MojoExecutionException if setting the flag fails.
1497      *
1498      * @since 1.1
1499      */
1500     protected final void setResourceProcessingEnabled( final boolean value ) throws MojoExecutionException
1501     {
1502         this.resourceProcessingEnabled = value;
1503     }
1504 
1505     /**
1506      * Gets a flag indicating the processing of classes is enabled.
1507      *
1508      * @return {@code true}, if processing of classes is enabled; {@code false}, else.
1509      *
1510      * @throws MojoExecutionException if getting the flag fails.
1511      */
1512     protected final boolean isClassProcessingEnabled() throws MojoExecutionException
1513     {
1514         return this.classProcessingEnabled;
1515     }
1516 
1517     /**
1518      * Sets the flag indicating the processing of classes is enabled.
1519      *
1520      * @param value {@code true}, to enable processing of classes; {@code false}, to disable processing of classes.
1521      *
1522      * @throws MojoExecutionException if setting the flag fails.
1523      *
1524      * @since 1.1
1525      */
1526     protected final void setClassProcessingEnabled( final boolean value ) throws MojoExecutionException
1527     {
1528         this.classProcessingEnabled = value;
1529     }
1530 
1531     /**
1532      * Gets a flag indicating the processing of models is enabled.
1533      *
1534      * @return {@code true}, if processing of models is enabled; {@code false}, else.
1535      *
1536      * @throws MojoExecutionException if getting the flag fails.
1537      */
1538     protected final boolean isModelProcessingEnabled() throws MojoExecutionException
1539     {
1540         return this.modelProcessingEnabled;
1541     }
1542 
1543     /**
1544      * Sets the flag indicating the processing of models is enabled.
1545      *
1546      * @param value {@code true}, to enable processing of models; {@code false}, to disable processing of models.
1547      *
1548      * @throws MojoExecutionException if setting the flag fails.
1549      *
1550      * @since 1.1
1551      */
1552     protected final void setModelProcessingEnabled( final boolean value ) throws MojoExecutionException
1553     {
1554         this.modelProcessingEnabled = value;
1555     }
1556 
1557     /**
1558      * Gets a flag indicating model object class path resolution is enabled.
1559      *
1560      * @return {@code true}, if model object class path resolution is enabled; {@code false}, else.
1561      *
1562      * @throws MojoExecutionException if getting the flag fails.
1563      */
1564     protected final boolean isModelObjectClasspathResolutionEnabled() throws MojoExecutionException
1565     {
1566         return this.modelObjectClasspathResolutionEnabled;
1567     }
1568 
1569     /**
1570      * Sets the flag indicating model object class path resolution is enabled.
1571      *
1572      * @param value {@code true}, to enable model object class path resolution; {@code false}, to disable model object
1573      * class path resolution.
1574      *
1575      * @throws MojoExecutionException if setting the flag fails.
1576      *
1577      * @since 1.1
1578      */
1579     protected final void setModelObjectClasspathResolutionEnabled( final boolean value ) throws MojoExecutionException
1580     {
1581         this.modelObjectClasspathResolutionEnabled = value;
1582     }
1583 
1584     /**
1585      * Gets the identifier of the model to process.
1586      *
1587      * @return The identifier of the model to process.
1588      *
1589      * @throws MojoExecutionException if getting the identifier fails.
1590      */
1591     protected String getModel() throws MojoExecutionException
1592     {
1593         return this.model;
1594     }
1595 
1596     /**
1597      * Gets the name of the module to process.
1598      *
1599      * @return The name of the module to process.
1600      *
1601      * @throws MojoExecutionException if getting the name of the module fails.
1602      */
1603     protected String getModuleName() throws MojoExecutionException
1604     {
1605         return this.moduleName;
1606     }
1607 
1608     /**
1609      * Gets the name of the test module to process.
1610      *
1611      * @return The name of the test module to process.
1612      *
1613      * @throws MojoExecutionException if getting the name of the test module fails.
1614      */
1615     protected String getTestModuleName() throws MojoExecutionException
1616     {
1617         return this.testModuleName;
1618     }
1619 
1620     /**
1621      * Gets the model to process.
1622      *
1623      * @param context The model context to get the model to process with.
1624      *
1625      * @return The model to process.
1626      *
1627      * @throws NullPointerException if {@code context} is {@code null}.
1628      * @throws MojoExecutionException if getting the model fails.
1629      */
1630     protected Model getModel( final ModelContext context ) throws MojoExecutionException
1631     {
1632         if ( context == null )
1633         {
1634             throw new NullPointerException( "context" );
1635         }
1636 
1637         try
1638         {
1639             Model m = context.findModel( this.getModel() );
1640             final Modules modules = ModelHelper.getModules( m );
1641 
1642             if ( modules != null && this.isModelObjectClasspathResolutionEnabled() )
1643             {
1644                 final Module classpathModule =
1645                     modules.getClasspathModule( Modules.getDefaultClasspathModuleName(), context.getClassLoader() );
1646 
1647                 if ( classpathModule != null )
1648                 {
1649                     modules.getModule().add( classpathModule );
1650                 }
1651             }
1652 
1653             if ( this.isModelProcessingEnabled() )
1654             {
1655                 m = context.processModel( m );
1656             }
1657 
1658             return m;
1659         }
1660         catch ( final ModelException e )
1661         {
1662             throw new MojoExecutionException( Messages.getMessage( e ), e );
1663         }
1664     }
1665 
1666     /**
1667      * Creates a new model context instance for a given class loader.
1668      *
1669      * @param classLoader The class loader to use for creating the context.
1670      *
1671      * @return A new model context instance for {@code classLoader}.
1672      *
1673      * @throws MojoExecutionException if creating the model context fails.
1674      *
1675      * @see #setupModelContext(org.jomc.modlet.ModelContext)
1676      */
1677     protected ModelContext createModelContext( final ClassLoader classLoader ) throws MojoExecutionException
1678     {
1679         final ModelContextFactory modelContextFactory;
1680         if ( this.modelContextFactoryClassName != null )
1681         {
1682             modelContextFactory = ModelContextFactory.newInstance( this.modelContextFactoryClassName );
1683         }
1684         else
1685         {
1686             modelContextFactory = ModelContextFactory.newInstance();
1687         }
1688 
1689         final ModelContext context = modelContextFactory.newModelContext( classLoader );
1690         this.setupModelContext( context );
1691 
1692         return context;
1693     }
1694 
1695     /**
1696      * Creates a new tool instance for processing source files.
1697      *
1698      * @param context The context of the tool.
1699      *
1700      * @return A new tool instance for processing source files.
1701      *
1702      * @throws NullPointerException if {@code context} is {@code null}.
1703      * @throws MojoExecutionException if creating a new tool instance fails.
1704      *
1705      * @see #createJomcTool(org.jomc.modlet.ModelContext, java.lang.String, java.lang.Class)
1706      */
1707     protected SourceFileProcessor createSourceFileProcessor( final ModelContext context ) throws MojoExecutionException
1708     {
1709         if ( context == null )
1710         {
1711             throw new NullPointerException( "context" );
1712         }
1713 
1714         return this.createJomcTool( context, this.sourceFileProcessorClassName, SourceFileProcessor.class );
1715     }
1716 
1717     /**
1718      * Creates a new tool instance for processing resource files.
1719      *
1720      * @param context The context of the tool.
1721      *
1722      * @return A new tool instance for processing resource files.
1723      *
1724      * @throws NullPointerException if {@code context} is {@code null}.
1725      * @throws MojoExecutionException if creating a new tool instance fails.
1726      *
1727      * @see #createJomcTool(org.jomc.modlet.ModelContext, java.lang.String, java.lang.Class)
1728      */
1729     protected ResourceFileProcessor createResourceFileProcessor( final ModelContext context )
1730         throws MojoExecutionException
1731     {
1732         if ( context == null )
1733         {
1734             throw new NullPointerException( "context" );
1735         }
1736 
1737         return this.createJomcTool( context, this.resourceFileProcessorClassName, ResourceFileProcessor.class );
1738     }
1739 
1740     /**
1741      * Creates a new tool instance for processing class files.
1742      *
1743      * @param context The context of the tool.
1744      *
1745      * @return A new tool instance for processing class files.
1746      *
1747      * @throws NullPointerException if {@code context} is {@code null}.
1748      * @throws MojoExecutionException if creating a new tool instance fails.
1749      *
1750      * @see #createJomcTool(org.jomc.modlet.ModelContext, java.lang.String, java.lang.Class)
1751      */
1752     protected ClassFileProcessor createClassFileProcessor( final ModelContext context ) throws MojoExecutionException
1753     {
1754         if ( context == null )
1755         {
1756             throw new NullPointerException( "context" );
1757         }
1758 
1759         return this.createJomcTool( context, this.classFileProcessorClassName, ClassFileProcessor.class );
1760     }
1761 
1762     /**
1763      * Creates a new {@code JomcTool} object for a given class name and type.
1764      *
1765      * @param context The context of the tool.
1766      * @param className The name of the class to create an object of.
1767      * @param type The class of the type of object to create.
1768      * @param <T> The type of the object to create.
1769      *
1770      * @return A new instance of the class with name {@code className}.
1771      *
1772      * @throws NullPointerException if {@code context}, {@code className} or {@code type} is {@code null}.
1773      * @throws MojoExecutionException if creating a new {@code JomcTool} object fails.
1774      *
1775      * @see #createObject(org.jomc.modlet.ModelContext, java.lang.String, java.lang.Class)
1776      * @see #setupJomcTool(org.jomc.modlet.ModelContext, org.jomc.tools.JomcTool)
1777      *
1778      * @since 1.2
1779      */
1780     protected <T extends JomcTool> T createJomcTool( final ModelContext context, final String className,
1781                                                      final Class<T> type ) throws MojoExecutionException
1782     {
1783         if ( context == null )
1784         {
1785             throw new NullPointerException( "context" );
1786         }
1787         if ( className == null )
1788         {
1789             throw new NullPointerException( "className" );
1790         }
1791         if ( type == null )
1792         {
1793             throw new NullPointerException( "type" );
1794         }
1795 
1796         final T tool = this.createObject( context, className, type );
1797         this.setupJomcTool( context, tool );
1798         return tool;
1799     }
1800 
1801     /**
1802      * Creates a new object for a given class name and type.
1803      *
1804      * @param className The name of the class to create an object of.
1805      * @param type The class of the type of object to create.
1806      * @param <T> The type of the object to create.
1807      *
1808      * @return A new instance of the class with name {@code className}.
1809      *
1810      * @throws NullPointerException if {@code className} or {@code type} is {@code null}.
1811      * @throws MojoExecutionException if creating a new object fails.
1812      *
1813      * @since 1.2
1814      * @deprecated As of JOMC 1.8, replaced by method {@link #createObject(org.jomc.modlet.ModelContext, java.lang.String, java.lang.Class)}.
1815      * This method will be removed in JOMC 2.0.
1816      */
1817     @Deprecated
1818     @SuppressWarnings( "deprecation" )
1819     protected <T> T createObject( final String className, final Class<T> type ) throws MojoExecutionException
1820     {
1821         if ( className == null )
1822         {
1823             throw new NullPointerException( "className" );
1824         }
1825         if ( type == null )
1826         {
1827             throw new NullPointerException( "type" );
1828         }
1829 
1830         try
1831         {
1832             return Class.forName( className ).asSubclass( type ).newInstance();
1833         }
1834         catch ( final InstantiationException e )
1835         {
1836             throw new MojoExecutionException( Messages.getMessage( "failedCreatingObject", className ), e );
1837         }
1838         catch ( final IllegalAccessException e )
1839         {
1840             throw new MojoExecutionException( Messages.getMessage( "failedCreatingObject", className ), e );
1841         }
1842         catch ( final ClassNotFoundException e )
1843         {
1844             throw new MojoExecutionException( Messages.getMessage( "failedCreatingObject", className ), e );
1845         }
1846         catch ( final ClassCastException e )
1847         {
1848             throw new MojoExecutionException( Messages.getMessage( "failedCreatingObject", className ), e );
1849         }
1850     }
1851 
1852     /**
1853      * Creates a new object for a given class name and type.
1854      *
1855      * @param modelContext The model context to search.
1856      * @param className The name of the class to create an object of.
1857      * @param type The class of the type of object to create.
1858      * @param <T> The type of the object to create.
1859      *
1860      * @return A new instance of the class with name {@code className}.
1861      *
1862      * @throws NullPointerException if {@code modelContext}, {@code className} or {@code type} is {@code null}.
1863      * @throws MojoExecutionException if creating a new object fails.
1864      *
1865      * @since 1.8
1866      */
1867     protected <T> T createObject( final ModelContext modelContext, final String className, final Class<T> type )
1868         throws MojoExecutionException
1869     {
1870         if ( modelContext == null )
1871         {
1872             throw new NullPointerException( "modelContext" );
1873         }
1874         if ( className == null )
1875         {
1876             throw new NullPointerException( "className" );
1877         }
1878         if ( type == null )
1879         {
1880             throw new NullPointerException( "type" );
1881         }
1882 
1883         try
1884         {
1885             final Class<?> javaClass = modelContext.findClass( className );
1886 
1887             if ( javaClass == null )
1888             {
1889                 throw new MojoExecutionException( Messages.getMessage( "classNotFound", className ) );
1890             }
1891 
1892             return javaClass.asSubclass( type ).newInstance();
1893         }
1894         catch ( final ModelException e )
1895         {
1896             String m = Messages.getMessage( e );
1897             m = m == null ? "" : " " + m;
1898 
1899             throw new MojoExecutionException( Messages.getMessage( "failedSearchingClass", className, m ), e );
1900         }
1901         catch ( final InstantiationException e )
1902         {
1903             throw new MojoExecutionException( Messages.getMessage( "failedCreatingObject", className ), e );
1904         }
1905         catch ( final IllegalAccessException e )
1906         {
1907             throw new MojoExecutionException( Messages.getMessage( "failedCreatingObject", className ), e );
1908         }
1909         catch ( final ClassCastException e )
1910         {
1911             throw new MojoExecutionException( Messages.getMessage( "failedCreatingObject", className ), e );
1912         }
1913     }
1914 
1915     /**
1916      * Creates an {@code URL} for a given resource location.
1917      * <p>
1918      * This method first searches the class path of the plugin for a single resource matching {@code location}. If
1919      * such a resource is found, the URL of that resource is returned. If no such resource is found, an attempt is made
1920      * to parse the given location to an URL. On successful parsing, that URL is returned. Failing that, the given
1921      * location is interpreted as a file name relative to the project's base directory. If that file is found, the URL
1922      * of that file is returned. Otherwise {@code null} is returned.
1923      * </p>
1924      *
1925      * @param location The location to create an {@code URL} from.
1926      *
1927      * @return An {@code URL} for {@code location} or {@code null}, if parsing {@code location} to an URL fails and
1928      * {@code location} points to a non-existent resource.
1929      *
1930      * @throws NullPointerException if {@code location} is {@code null}.
1931      * @throws MojoExecutionException if creating an URL fails.
1932      *
1933      * @since 1.2
1934      * @deprecated As of JOMC 1.8, replaced by method {@link #getResource(org.jomc.modlet.ModelContext, java.lang.String)}.
1935      * This method will be removed in JOMC 2.0.
1936      */
1937     @Deprecated
1938     @SuppressWarnings( "deprecation" )
1939     protected URL getResource( final String location ) throws MojoExecutionException
1940     {
1941         if ( location == null )
1942         {
1943             throw new NullPointerException( "location" );
1944         }
1945 
1946         try
1947         {
1948             String absolute = location;
1949             if ( !absolute.startsWith( "/" ) )
1950             {
1951                 absolute = "/" + location;
1952             }
1953 
1954             URL resource = this.getClass().getResource( absolute );
1955             if ( resource == null )
1956             {
1957                 try
1958                 {
1959                     resource = new URL( location );
1960                 }
1961                 catch ( final MalformedURLException e )
1962                 {
1963                     if ( this.isLoggable( Level.FINEST ) )
1964                     {
1965                         this.log( Level.FINEST, Messages.getMessage( e ), e );
1966                     }
1967 
1968                     resource = null;
1969                 }
1970             }
1971 
1972             if ( resource == null )
1973             {
1974                 final File f = this.getAbsoluteFile( location );
1975 
1976                 if ( f.isFile() )
1977                 {
1978                     resource = f.toURI().toURL();
1979                 }
1980             }
1981 
1982             return resource;
1983         }
1984         catch ( final MalformedURLException e )
1985         {
1986             String m = Messages.getMessage( e );
1987             m = m == null ? "" : " " + m;
1988 
1989             throw new MojoExecutionException( Messages.getMessage( "malformedLocation", location, m ), e );
1990         }
1991     }
1992 
1993     /**
1994      * Creates an {@code URL} for a given resource location.
1995      * <p>
1996      * This method first searches the given model context for a single resource matching {@code location}. If such a
1997      * resource is found, the URL of that resource is returned. If no such resource is found, an attempt is made to
1998      * parse the given location to an URL. On successful parsing, that URL is returned. Failing that, the given location
1999      * is interpreted as a file name relative to the project's base directory. If that file is found, the URL of that
2000      * file is returned. Otherwise {@code null} is returned.
2001      * </p>
2002      *
2003      * @param modelContext The model conext to search.
2004      * @param location The location to create an {@code URL} from.
2005      *
2006      * @return An {@code URL} for {@code location} or {@code null}, if parsing {@code location} to an URL fails and
2007      * {@code location} points to a non-existent resource.
2008      *
2009      * @throws NullPointerException if {@code modelContext} or {@code location} is {@code null}.
2010      * @throws MojoExecutionException if creating an URL fails.
2011      *
2012      * @since 1.8
2013      */
2014     protected URL getResource( final ModelContext modelContext, final String location ) throws MojoExecutionException
2015     {
2016         if ( modelContext == null )
2017         {
2018             throw new NullPointerException( "modelContext" );
2019         }
2020         if ( location == null )
2021         {
2022             throw new NullPointerException( "location" );
2023         }
2024 
2025         try
2026         {
2027             String absolute = location;
2028             if ( !absolute.startsWith( "/" ) )
2029             {
2030                 absolute = "/" + location;
2031             }
2032 
2033             URL resource = modelContext.findResource( absolute );
2034 
2035             if ( resource == null )
2036             {
2037                 try
2038                 {
2039                     resource = new URL( location );
2040                 }
2041                 catch ( final MalformedURLException e )
2042                 {
2043                     if ( this.isLoggable( Level.FINEST ) )
2044                     {
2045                         this.log( Level.FINEST, Messages.getMessage( e ), e );
2046                     }
2047 
2048                     resource = null;
2049                 }
2050             }
2051 
2052             if ( resource == null )
2053             {
2054                 final File f = this.getAbsoluteFile( location );
2055 
2056                 if ( f.isFile() )
2057                 {
2058                     resource = f.toURI().toURL();
2059                 }
2060             }
2061 
2062             return resource;
2063         }
2064         catch ( final ModelException e )
2065         {
2066             String m = Messages.getMessage( e );
2067             m = m == null ? "" : " " + m;
2068 
2069             throw new MojoExecutionException( Messages.getMessage( "failedSearchingResource", location, m ), e );
2070         }
2071         catch ( final MalformedURLException e )
2072         {
2073             String m = Messages.getMessage( e );
2074             m = m == null ? "" : " " + m;
2075 
2076             throw new MojoExecutionException( Messages.getMessage( "malformedLocation", location, m ), e );
2077         }
2078     }
2079 
2080     /**
2081      * Creates an {@code URL} for a given directory location.
2082      * <p>
2083      * This method first attempts to parse the given location to an URL. On successful parsing, that URL is returned.
2084      * Failing that, the given location is interpreted as a directory name relative to the project's base directory.
2085      * If that directory is found, the URL of that directory is returned. Otherwise {@code null} is returned.
2086      * </p>
2087      *
2088      * @param location The directory location to create an {@code URL} from.
2089      *
2090      * @return An {@code URL} for {@code location} or {@code null}, if parsing {@code location} to an URL fails and
2091      * {@code location} points to a non-existent directory.
2092      *
2093      * @throws NullPointerException if {@code location} is {@code null}.
2094      * @throws MojoExecutionException if creating an URL fails.
2095      *
2096      * @since 1.2
2097      */
2098     protected URL getDirectory( final String location ) throws MojoExecutionException
2099     {
2100         if ( location == null )
2101         {
2102             throw new NullPointerException( "location" );
2103         }
2104 
2105         try
2106         {
2107             URL resource;
2108 
2109             try
2110             {
2111                 resource = new URL( location );
2112             }
2113             catch ( final MalformedURLException e )
2114             {
2115                 if ( this.isLoggable( Level.FINEST ) )
2116                 {
2117                     this.log( Level.FINEST, Messages.getMessage( e ), e );
2118                 }
2119 
2120                 resource = null;
2121             }
2122 
2123             if ( resource == null )
2124             {
2125                 final File f = this.getAbsoluteFile( location );
2126 
2127                 if ( f.isDirectory() )
2128                 {
2129                     resource = f.toURI().toURL();
2130                 }
2131             }
2132 
2133             return resource;
2134         }
2135         catch ( final MalformedURLException e )
2136         {
2137             String m = Messages.getMessage( e );
2138             m = m == null ? "" : " " + m;
2139 
2140             throw new MojoExecutionException( Messages.getMessage( "malformedLocation", location, m ), e );
2141         }
2142     }
2143 
2144     /**
2145      * Creates a new {@code Transformer} from a given {@code TransformerResourceType}.
2146      *
2147      * @param resource The resource to initialize the transformer with.
2148      *
2149      * @return A {@code Transformer} for {@code resource} or {@code null}, if {@code resource} is not found and flagged
2150      * optional.
2151      *
2152      * @throws NullPointerException if {@code resource} is {@code null}.
2153      * @throws MojoExecutionException if creating a transformer fails.
2154      *
2155      * @see #getResource(java.lang.String)
2156      * @since 1.2
2157      * @deprecated As of JOMC 1.8, replaced by method {@link #getTransformer(org.jomc.modlet.ModelContext, org.jomc.mojo.TransformerResourceType)}.
2158      * This method will be removed in JOMC 2.0.
2159      */
2160     @Deprecated
2161     @SuppressWarnings( "deprecation" )
2162     protected Transformer getTransformer( final TransformerResourceType resource ) throws MojoExecutionException
2163     {
2164         if ( resource == null )
2165         {
2166             throw new NullPointerException( "resource" );
2167         }
2168 
2169         URLConnection con = null;
2170         InputStream in = null;
2171         final URL url = this.getResource( resource.getLocation() );
2172         final ErrorListener errorListener = new ErrorListener()
2173         {
2174 
2175             public void warning( final TransformerException exception ) throws TransformerException
2176             {
2177                 try
2178                 {
2179                     log( Level.WARNING, Messages.getMessage( exception ), exception );
2180                 }
2181                 catch ( final MojoExecutionException e )
2182                 {
2183                     getLog().warn( exception );
2184                     getLog().error( e );
2185                 }
2186             }
2187 
2188             public void error( final TransformerException exception ) throws TransformerException
2189             {
2190                 try
2191                 {
2192                     log( Level.SEVERE, Messages.getMessage( exception ), exception );
2193                 }
2194                 catch ( final MojoExecutionException e )
2195                 {
2196                     getLog().error( exception );
2197                     getLog().error( e );
2198                 }
2199 
2200                 throw exception;
2201             }
2202 
2203             public void fatalError( final TransformerException exception ) throws TransformerException
2204             {
2205                 try
2206                 {
2207                     log( Level.SEVERE, Messages.getMessage( exception ), exception );
2208                 }
2209                 catch ( final MojoExecutionException e )
2210                 {
2211                     getLog().error( exception );
2212                     getLog().error( e );
2213                 }
2214 
2215                 throw exception;
2216             }
2217 
2218         };
2219 
2220         try
2221         {
2222             if ( url != null )
2223             {
2224                 if ( this.isLoggable( Level.FINER ) )
2225                 {
2226                     this.log( Level.FINER, Messages.getMessage( "loadingTransformer", url.toExternalForm() ), null );
2227                 }
2228 
2229                 con = url.openConnection();
2230                 con.setConnectTimeout( resource.getConnectTimeout() );
2231                 con.setReadTimeout( resource.getReadTimeout() );
2232                 con.connect();
2233                 in = con.getInputStream();
2234 
2235                 final TransformerFactory transformerFactory = TransformerFactory.newInstance();
2236                 transformerFactory.setErrorListener( errorListener );
2237                 final Transformer transformer =
2238                     transformerFactory.newTransformer( new StreamSource( in, url.toURI().toASCIIString() ) );
2239 
2240                 transformer.setErrorListener( errorListener );
2241 
2242                 for ( final Map.Entry<Object, Object> e : System.getProperties().entrySet() )
2243                 {
2244                     transformer.setParameter( e.getKey().toString(), e.getValue() );
2245                 }
2246 
2247                 if ( this.getMavenProject().getProperties() != null )
2248                 {
2249                     for ( final Map.Entry<Object, Object> e : this.getMavenProject().getProperties().entrySet() )
2250                     {
2251                         transformer.setParameter( e.getKey().toString(), e.getValue() );
2252                     }
2253                 }
2254 
2255                 if ( this.transformationParameterResources != null )
2256                 {
2257                     for ( int i = 0, s0 = this.transformationParameterResources.size(); i < s0; i++ )
2258                     {
2259                         for ( final Map.Entry<Object, Object> e : this.getProperties(
2260                             this.transformationParameterResources.get( i ) ).entrySet() )
2261                         {
2262                             transformer.setParameter( e.getKey().toString(), e.getValue() );
2263                         }
2264                     }
2265                 }
2266 
2267                 if ( this.transformationParameters != null )
2268                 {
2269                     for ( final TransformationParameter e : this.transformationParameters )
2270                     {
2271                         transformer.setParameter( e.getKey(), e.getObject() );
2272                     }
2273                 }
2274 
2275                 if ( this.transformationOutputProperties != null )
2276                 {
2277                     for ( final TransformationOutputProperty e : this.transformationOutputProperties )
2278                     {
2279                         transformer.setOutputProperty( e.getKey(), e.getValue() );
2280                     }
2281                 }
2282 
2283                 for ( int i = 0, s0 = resource.getTransformationParameterResources().size(); i < s0; i++ )
2284                 {
2285                     for ( final Map.Entry<Object, Object> e : this.getProperties(
2286                         resource.getTransformationParameterResources().get( i ) ).entrySet() )
2287                     {
2288                         transformer.setParameter( e.getKey().toString(), e.getValue() );
2289                     }
2290                 }
2291 
2292                 for ( final TransformationParameter e : resource.getTransformationParameters() )
2293                 {
2294                     transformer.setParameter( e.getKey(), e.getObject() );
2295                 }
2296 
2297                 for ( final TransformationOutputProperty e : resource.getTransformationOutputProperties() )
2298                 {
2299                     transformer.setOutputProperty( e.getKey(), e.getValue() );
2300                 }
2301 
2302                 in.close();
2303                 in = null;
2304 
2305                 return transformer;
2306             }
2307             else if ( resource.isOptional() )
2308             {
2309                 if ( this.isLoggable( Level.WARNING ) )
2310                 {
2311                     this.log( Level.WARNING, Messages.getMessage(
2312                               "transformerNotFound", resource.getLocation() ), null );
2313 
2314                 }
2315             }
2316             else
2317             {
2318                 throw new MojoExecutionException( Messages.getMessage(
2319                     "transformerNotFound", resource.getLocation() ) );
2320 
2321             }
2322         }
2323         catch ( final InstantiationException e )
2324         {
2325             throw new MojoExecutionException( Messages.getMessage( e ), e );
2326         }
2327         catch ( final URISyntaxException e )
2328         {
2329             throw new MojoExecutionException( Messages.getMessage( e ), e );
2330         }
2331         catch ( final TransformerConfigurationException e )
2332         {
2333             String m = Messages.getMessage( e );
2334             if ( m == null )
2335             {
2336                 m = Messages.getMessage( e.getException() );
2337             }
2338 
2339             m = m == null ? "" : " " + m;
2340 
2341             throw new MojoExecutionException( Messages.getMessage(
2342                 "failedCreatingTransformer", resource.getLocation(), m ), e );
2343 
2344         }
2345         catch ( final SocketTimeoutException e )
2346         {
2347             String m = Messages.getMessage( e );
2348             m = m == null ? "" : " " + m;
2349 
2350             if ( resource.isOptional() )
2351             {
2352                 if ( this.isLoggable( Level.WARNING ) )
2353                 {
2354                     this.log( Level.WARNING, Messages.getMessage(
2355                               "failedLoadingTransformer", url.toExternalForm(), m ), e );
2356 
2357                 }
2358             }
2359             else
2360             {
2361                 throw new MojoExecutionException( Messages.getMessage(
2362                     "failedLoadingTransformer", url.toExternalForm(), m ), e );
2363 
2364             }
2365         }
2366         catch ( final IOException e )
2367         {
2368             String m = Messages.getMessage( e );
2369             m = m == null ? "" : " " + m;
2370 
2371             if ( resource.isOptional() )
2372             {
2373                 if ( this.isLoggable( Level.WARNING ) )
2374                 {
2375                     this.log( Level.WARNING, Messages.getMessage(
2376                               "failedLoadingTransformer", url.toExternalForm(), m ), e );
2377 
2378                 }
2379             }
2380             else
2381             {
2382                 throw new MojoExecutionException( Messages.getMessage(
2383                     "failedLoadingTransformer", url.toExternalForm(), m ), e );
2384 
2385             }
2386         }
2387         finally
2388         {
2389             try
2390             {
2391                 if ( in != null )
2392                 {
2393                     in.close();
2394                 }
2395             }
2396             catch ( final IOException e )
2397             {
2398                 this.getLog().error( e );
2399             }
2400             finally
2401             {
2402                 if ( con instanceof HttpURLConnection )
2403                 {
2404                     ( (HttpURLConnection) con ).disconnect();
2405                 }
2406             }
2407         }
2408 
2409         return null;
2410     }
2411 
2412     /**
2413      * Creates a new {@code Transformer} from a given {@code TransformerResourceType}.
2414      *
2415      * @param modelContext The model context to search.
2416      * @param resource The resource to initialize the transformer with.
2417      *
2418      * @return A {@code Transformer} for {@code resource} or {@code null}, if {@code resource} is not found and flagged
2419      * optional.
2420      *
2421      * @throws NullPointerException if {@code modelContext} or {@code resource} is {@code null}.
2422      * @throws MojoExecutionException if creating a transformer fails.
2423      *
2424      * @see #getResource(org.jomc.modlet.ModelContext, java.lang.String)
2425      * @since 1.8
2426      */
2427     protected Transformer getTransformer( final ModelContext modelContext, final TransformerResourceType resource )
2428         throws MojoExecutionException
2429     {
2430         if ( modelContext == null )
2431         {
2432             throw new NullPointerException( "modelContext" );
2433         }
2434         if ( resource == null )
2435         {
2436             throw new NullPointerException( "resource" );
2437         }
2438 
2439         URLConnection con = null;
2440         InputStream in = null;
2441         final URL url = this.getResource( modelContext, resource.getLocation() );
2442         final ErrorListener errorListener = new ErrorListener()
2443         {
2444 
2445             public void warning( final TransformerException exception ) throws TransformerException
2446             {
2447                 try
2448                 {
2449                     log( Level.WARNING, Messages.getMessage( exception ), exception );
2450                 }
2451                 catch ( final MojoExecutionException e )
2452                 {
2453                     getLog().warn( exception );
2454                     getLog().error( e );
2455                 }
2456             }
2457 
2458             public void error( final TransformerException exception ) throws TransformerException
2459             {
2460                 try
2461                 {
2462                     log( Level.SEVERE, Messages.getMessage( exception ), exception );
2463                 }
2464                 catch ( final MojoExecutionException e )
2465                 {
2466                     getLog().error( exception );
2467                     getLog().error( e );
2468                 }
2469 
2470                 throw exception;
2471             }
2472 
2473             public void fatalError( final TransformerException exception ) throws TransformerException
2474             {
2475                 try
2476                 {
2477                     log( Level.SEVERE, Messages.getMessage( exception ), exception );
2478                 }
2479                 catch ( final MojoExecutionException e )
2480                 {
2481                     getLog().error( exception );
2482                     getLog().error( e );
2483                 }
2484 
2485                 throw exception;
2486             }
2487 
2488         };
2489 
2490         try
2491         {
2492             if ( url != null )
2493             {
2494                 if ( this.isLoggable( Level.FINER ) )
2495                 {
2496                     this.log( Level.FINER, Messages.getMessage( "loadingTransformer", url.toExternalForm() ), null );
2497                 }
2498 
2499                 con = url.openConnection();
2500                 con.setConnectTimeout( resource.getConnectTimeout() );
2501                 con.setReadTimeout( resource.getReadTimeout() );
2502                 con.connect();
2503                 in = con.getInputStream();
2504 
2505                 final TransformerFactory transformerFactory = TransformerFactory.newInstance();
2506                 transformerFactory.setErrorListener( errorListener );
2507                 final Transformer transformer =
2508                     transformerFactory.newTransformer( new StreamSource( in, url.toURI().toASCIIString() ) );
2509 
2510                 transformer.setErrorListener( errorListener );
2511 
2512                 for ( final Map.Entry<Object, Object> e : System.getProperties().entrySet() )
2513                 {
2514                     transformer.setParameter( e.getKey().toString(), e.getValue() );
2515                 }
2516 
2517                 if ( this.getMavenProject().getProperties() != null )
2518                 {
2519                     for ( final Map.Entry<Object, Object> e : this.getMavenProject().getProperties().entrySet() )
2520                     {
2521                         transformer.setParameter( e.getKey().toString(), e.getValue() );
2522                     }
2523                 }
2524 
2525                 if ( this.transformationParameterResources != null )
2526                 {
2527                     for ( int i = 0, s0 = this.transformationParameterResources.size(); i < s0; i++ )
2528                     {
2529                         for ( final Map.Entry<Object, Object> e : this.getProperties(
2530                             modelContext, this.transformationParameterResources.get( i ) ).entrySet() )
2531                         {
2532                             transformer.setParameter( e.getKey().toString(), e.getValue() );
2533                         }
2534                     }
2535                 }
2536 
2537                 if ( this.transformationParameters != null )
2538                 {
2539                     for ( final TransformationParameter e : this.transformationParameters )
2540                     {
2541                         transformer.setParameter( e.getKey(), e.getObject( modelContext ) );
2542                     }
2543                 }
2544 
2545                 if ( this.transformationOutputProperties != null )
2546                 {
2547                     for ( final TransformationOutputProperty e : this.transformationOutputProperties )
2548                     {
2549                         transformer.setOutputProperty( e.getKey(), e.getValue() );
2550                     }
2551                 }
2552 
2553                 for ( int i = 0, s0 = resource.getTransformationParameterResources().size(); i < s0; i++ )
2554                 {
2555                     for ( final Map.Entry<Object, Object> e : this.getProperties(
2556                         modelContext, resource.getTransformationParameterResources().get( i ) ).entrySet() )
2557                     {
2558                         transformer.setParameter( e.getKey().toString(), e.getValue() );
2559                     }
2560                 }
2561 
2562                 for ( final TransformationParameter e : resource.getTransformationParameters() )
2563                 {
2564                     transformer.setParameter( e.getKey(), e.getObject( modelContext ) );
2565                 }
2566 
2567                 for ( final TransformationOutputProperty e : resource.getTransformationOutputProperties() )
2568                 {
2569                     transformer.setOutputProperty( e.getKey(), e.getValue() );
2570                 }
2571 
2572                 in.close();
2573                 in = null;
2574 
2575                 return transformer;
2576             }
2577             else if ( resource.isOptional() )
2578             {
2579                 if ( this.isLoggable( Level.WARNING ) )
2580                 {
2581                     this.log( Level.WARNING, Messages.getMessage(
2582                               "transformerNotFound", resource.getLocation() ), null );
2583 
2584                 }
2585             }
2586             else
2587             {
2588                 throw new MojoExecutionException( Messages.getMessage(
2589                     "transformerNotFound", resource.getLocation() ) );
2590 
2591             }
2592         }
2593         catch ( final InstantiationException e )
2594         {
2595             throw new MojoExecutionException( Messages.getMessage( e ), e );
2596         }
2597         catch ( final URISyntaxException e )
2598         {
2599             throw new MojoExecutionException( Messages.getMessage( e ), e );
2600         }
2601         catch ( final TransformerConfigurationException e )
2602         {
2603             String m = Messages.getMessage( e );
2604             if ( m == null )
2605             {
2606                 m = Messages.getMessage( e.getException() );
2607             }
2608 
2609             m = m == null ? "" : " " + m;
2610 
2611             throw new MojoExecutionException( Messages.getMessage(
2612                 "failedCreatingTransformer", resource.getLocation(), m ), e );
2613 
2614         }
2615         catch ( final SocketTimeoutException e )
2616         {
2617             String m = Messages.getMessage( e );
2618             m = m == null ? "" : " " + m;
2619 
2620             if ( resource.isOptional() )
2621             {
2622                 if ( this.isLoggable( Level.WARNING ) )
2623                 {
2624                     this.log( Level.WARNING, Messages.getMessage(
2625                               "failedLoadingTransformer", url.toExternalForm(), m ), e );
2626 
2627                 }
2628             }
2629             else
2630             {
2631                 throw new MojoExecutionException( Messages.getMessage(
2632                     "failedLoadingTransformer", url.toExternalForm(), m ), e );
2633 
2634             }
2635         }
2636         catch ( final IOException e )
2637         {
2638             String m = Messages.getMessage( e );
2639             m = m == null ? "" : " " + m;
2640 
2641             if ( resource.isOptional() )
2642             {
2643                 if ( this.isLoggable( Level.WARNING ) )
2644                 {
2645                     this.log( Level.WARNING, Messages.getMessage(
2646                               "failedLoadingTransformer", url.toExternalForm(), m ), e );
2647 
2648                 }
2649             }
2650             else
2651             {
2652                 throw new MojoExecutionException( Messages.getMessage(
2653                     "failedLoadingTransformer", url.toExternalForm(), m ), e );
2654 
2655             }
2656         }
2657         finally
2658         {
2659             try
2660             {
2661                 if ( in != null )
2662                 {
2663                     in.close();
2664                 }
2665             }
2666             catch ( final IOException e )
2667             {
2668                 this.getLog().error( e );
2669             }
2670             finally
2671             {
2672                 if ( con instanceof HttpURLConnection )
2673                 {
2674                     ( (HttpURLConnection) con ).disconnect();
2675                 }
2676             }
2677         }
2678 
2679         return null;
2680     }
2681 
2682     /**
2683      * Creates a new {@code Properties} instance from a {@code PropertiesResourceType}.
2684      *
2685      * @param propertiesResourceType The {@code PropertiesResourceType} specifying the properties to create.
2686      *
2687      * @return The properties for {@code propertiesResourceType}.
2688      *
2689      * @throws NullPointerException if {@code propertiesResourceType} is {@code null}.
2690      * @throws MojoExecutionException if loading properties fails.
2691      *
2692      * @see #getResource(java.lang.String)
2693      * @since 1.2
2694      * @deprecated As of JOMC 1.8, replaced by method {@link #getProperties(org.jomc.modlet.ModelContext, org.jomc.mojo.PropertiesResourceType)}.
2695      * This method will be removed in JOMC 2.0.
2696      */
2697     @Deprecated
2698     @SuppressWarnings( "deprecation" )
2699     protected Properties getProperties( final PropertiesResourceType propertiesResourceType )
2700         throws MojoExecutionException
2701     {
2702         if ( propertiesResourceType == null )
2703         {
2704             throw new NullPointerException( "propertiesResourceType" );
2705         }
2706 
2707         URLConnection con = null;
2708         InputStream in = null;
2709         final URL url = this.getResource( propertiesResourceType.getLocation() );
2710         final Properties properties = new Properties();
2711 
2712         try
2713         {
2714             if ( url != null )
2715             {
2716                 if ( this.isLoggable( Level.FINER ) )
2717                 {
2718                     this.log( Level.FINER, Messages.getMessage( "loadingProperties", url.toExternalForm() ), null );
2719                 }
2720 
2721                 con = url.openConnection();
2722                 con.setConnectTimeout( propertiesResourceType.getConnectTimeout() );
2723                 con.setReadTimeout( propertiesResourceType.getReadTimeout() );
2724                 con.connect();
2725                 in = con.getInputStream();
2726 
2727                 if ( PropertiesResourceType.PLAIN_FORMAT.equalsIgnoreCase( propertiesResourceType.getFormat() ) )
2728                 {
2729                     properties.load( in );
2730                 }
2731                 else if ( PropertiesResourceType.XML_FORMAT.equalsIgnoreCase( propertiesResourceType.getFormat() ) )
2732                 {
2733                     properties.loadFromXML( in );
2734                 }
2735 
2736                 in.close();
2737                 in = null;
2738             }
2739             else if ( propertiesResourceType.isOptional() )
2740             {
2741                 if ( this.isLoggable( Level.WARNING ) )
2742                 {
2743                     this.log( Level.WARNING, Messages.getMessage(
2744                               "propertiesNotFound", propertiesResourceType.getLocation() ), null );
2745 
2746                 }
2747             }
2748             else
2749             {
2750                 throw new MojoExecutionException( Messages.getMessage(
2751                     "propertiesNotFound", propertiesResourceType.getLocation() ) );
2752 
2753             }
2754         }
2755         catch ( final SocketTimeoutException e )
2756         {
2757             String m = Messages.getMessage( e );
2758             m = m == null ? "" : " " + m;
2759 
2760             if ( propertiesResourceType.isOptional() )
2761             {
2762                 if ( this.isLoggable( Level.WARNING ) )
2763                 {
2764                     this.log( Level.WARNING, Messages.getMessage(
2765                               "failedLoadingProperties", url.toExternalForm(), m ), e );
2766 
2767                 }
2768             }
2769             else
2770             {
2771                 throw new MojoExecutionException( Messages.getMessage(
2772                     "failedLoadingProperties", url.toExternalForm(), m ), e );
2773 
2774             }
2775         }
2776         catch ( final IOException e )
2777         {
2778             String m = Messages.getMessage( e );
2779             m = m == null ? "" : " " + m;
2780 
2781             if ( propertiesResourceType.isOptional() )
2782             {
2783                 if ( this.isLoggable( Level.WARNING ) )
2784                 {
2785                     this.log( Level.WARNING, Messages.getMessage(
2786                               "failedLoadingProperties", url.toExternalForm(), m ), e );
2787 
2788                 }
2789             }
2790             else
2791             {
2792                 throw new MojoExecutionException( Messages.getMessage(
2793                     "failedLoadingProperties", url.toExternalForm(), m ), e );
2794 
2795             }
2796         }
2797         finally
2798         {
2799             try
2800             {
2801                 if ( in != null )
2802                 {
2803                     in.close();
2804                 }
2805             }
2806             catch ( final IOException e )
2807             {
2808                 this.getLog().error( e );
2809             }
2810             finally
2811             {
2812                 if ( con instanceof HttpURLConnection )
2813                 {
2814                     ( (HttpURLConnection) con ).disconnect();
2815                 }
2816             }
2817         }
2818 
2819         return properties;
2820     }
2821 
2822     /**
2823      * Creates a new {@code Properties} instance from a {@code PropertiesResourceType}.
2824      *
2825      * @param modelContext The model context to search.
2826      * @param propertiesResourceType The {@code PropertiesResourceType} specifying the properties to create.
2827      *
2828      * @return The properties for {@code propertiesResourceType}.
2829      *
2830      * @throws NullPointerException if {@code modelContext} or {@code propertiesResourceType} is {@code null}.
2831      * @throws MojoExecutionException if loading properties fails.
2832      *
2833      * @see #getResource(org.jomc.modlet.ModelContext, java.lang.String)
2834      * @since 1.8
2835      */
2836     protected Properties getProperties( final ModelContext modelContext,
2837                                         final PropertiesResourceType propertiesResourceType )
2838         throws MojoExecutionException
2839     {
2840         if ( modelContext == null )
2841         {
2842             throw new NullPointerException( "modelContext" );
2843         }
2844         if ( propertiesResourceType == null )
2845         {
2846             throw new NullPointerException( "propertiesResourceType" );
2847         }
2848 
2849         URLConnection con = null;
2850         InputStream in = null;
2851         final URL url = this.getResource( modelContext, propertiesResourceType.getLocation() );
2852         final Properties properties = new Properties();
2853 
2854         try
2855         {
2856             if ( url != null )
2857             {
2858                 if ( this.isLoggable( Level.FINER ) )
2859                 {
2860                     this.log( Level.FINER, Messages.getMessage( "loadingProperties", url.toExternalForm() ), null );
2861                 }
2862 
2863                 con = url.openConnection();
2864                 con.setConnectTimeout( propertiesResourceType.getConnectTimeout() );
2865                 con.setReadTimeout( propertiesResourceType.getReadTimeout() );
2866                 con.connect();
2867                 in = con.getInputStream();
2868 
2869                 if ( PropertiesResourceType.PLAIN_FORMAT.equalsIgnoreCase( propertiesResourceType.getFormat() ) )
2870                 {
2871                     properties.load( in );
2872                 }
2873                 else if ( PropertiesResourceType.XML_FORMAT.equalsIgnoreCase( propertiesResourceType.getFormat() ) )
2874                 {
2875                     properties.loadFromXML( in );
2876                 }
2877 
2878                 in.close();
2879                 in = null;
2880             }
2881             else if ( propertiesResourceType.isOptional() )
2882             {
2883                 if ( this.isLoggable( Level.WARNING ) )
2884                 {
2885                     this.log( Level.WARNING, Messages.getMessage(
2886                               "propertiesNotFound", propertiesResourceType.getLocation() ), null );
2887 
2888                 }
2889             }
2890             else
2891             {
2892                 throw new MojoExecutionException( Messages.getMessage(
2893                     "propertiesNotFound", propertiesResourceType.getLocation() ) );
2894 
2895             }
2896         }
2897         catch ( final SocketTimeoutException e )
2898         {
2899             String m = Messages.getMessage( e );
2900             m = m == null ? "" : " " + m;
2901 
2902             if ( propertiesResourceType.isOptional() )
2903             {
2904                 if ( this.isLoggable( Level.WARNING ) )
2905                 {
2906                     this.log( Level.WARNING, Messages.getMessage(
2907                               "failedLoadingProperties", url.toExternalForm(), m ), e );
2908 
2909                 }
2910             }
2911             else
2912             {
2913                 throw new MojoExecutionException( Messages.getMessage(
2914                     "failedLoadingProperties", url.toExternalForm(), m ), e );
2915 
2916             }
2917         }
2918         catch ( final IOException e )
2919         {
2920             String m = Messages.getMessage( e );
2921             m = m == null ? "" : " " + m;
2922 
2923             if ( propertiesResourceType.isOptional() )
2924             {
2925                 if ( this.isLoggable( Level.WARNING ) )
2926                 {
2927                     this.log( Level.WARNING, Messages.getMessage(
2928                               "failedLoadingProperties", url.toExternalForm(), m ), e );
2929 
2930                 }
2931             }
2932             else
2933             {
2934                 throw new MojoExecutionException( Messages.getMessage(
2935                     "failedLoadingProperties", url.toExternalForm(), m ), e );
2936 
2937             }
2938         }
2939         finally
2940         {
2941             try
2942             {
2943                 if ( in != null )
2944                 {
2945                     in.close();
2946                 }
2947             }
2948             catch ( final IOException e )
2949             {
2950                 this.getLog().error( e );
2951             }
2952             finally
2953             {
2954                 if ( con instanceof HttpURLConnection )
2955                 {
2956                     ( (HttpURLConnection) con ).disconnect();
2957                 }
2958             }
2959         }
2960 
2961         return properties;
2962     }
2963 
2964     /**
2965      * Tests if messages at a given level are logged.
2966      *
2967      * @param level The level to test.
2968      *
2969      * @return {@code true}, if messages at {@code level} are logged; {@code false}, if messages at {@code level} are
2970      * suppressed.
2971      *
2972      * @throws NullPointerException if {@code level} is {@code null}.
2973      * @throws MojoExecutionException if testing the level fails.
2974      *
2975      * @see #isVerbose()
2976      * @since 1.2
2977      */
2978     protected boolean isLoggable( final Level level ) throws MojoExecutionException
2979     {
2980         if ( level == null )
2981         {
2982             throw new NullPointerException( "level" );
2983         }
2984 
2985         boolean loggable = false;
2986 
2987         if ( level.intValue() <= Level.CONFIG.intValue() )
2988         {
2989             loggable = this.getLog().isDebugEnabled();
2990         }
2991         else if ( level.intValue() <= Level.INFO.intValue() )
2992         {
2993             loggable = this.getLog().isInfoEnabled() && this.isVerbose();
2994         }
2995         else if ( level.intValue() <= Level.WARNING.intValue() )
2996         {
2997             loggable = this.getLog().isWarnEnabled();
2998         }
2999         else if ( level.intValue() <= Level.SEVERE.intValue() )
3000         {
3001             loggable = this.getLog().isErrorEnabled();
3002         }
3003 
3004         return loggable;
3005     }
3006 
3007     /**
3008      * Logs a separator at a given level.
3009      *
3010      * @param level The level to log a separator at.
3011      *
3012      * @throws MojoExecutionException if logging fails.
3013      *
3014      * @deprecated As of JOMC 1.1, please use method {@link #logSeparator()}. This method will be removed in version
3015      * 2.0.
3016      */
3017     @Deprecated
3018     protected void logSeparator( final Level level ) throws MojoExecutionException
3019     {
3020         this.logSeparator();
3021     }
3022 
3023     /**
3024      * Logs a separator.
3025      *
3026      * @throws MojoExecutionException if logging fails.
3027      *
3028      * @since 1.1
3029      */
3030     protected void logSeparator() throws MojoExecutionException
3031     {
3032         if ( this.isLoggable( Level.INFO ) )
3033         {
3034             this.log( Level.INFO, Messages.getMessage( "separator" ), null );
3035         }
3036     }
3037 
3038     /**
3039      * Logs a message stating a tool is starting to process a module.
3040      *
3041      * @param toolName The tool starting execution.
3042      * @param module The module getting processed.
3043      *
3044      * @throws MojoExecutionException if logging fails.
3045      */
3046     protected void logProcessingModule( final String toolName, final String module ) throws MojoExecutionException
3047     {
3048         if ( this.isLoggable( Level.INFO ) )
3049         {
3050             this.log( Level.INFO, Messages.getMessage( "processingModule", toolName, module ), null );
3051         }
3052     }
3053 
3054     /**
3055      * Logs a message stating a tool is starting to process a model.
3056      *
3057      * @param toolName The tool starting execution.
3058      * @param model The model getting processed.
3059      *
3060      * @throws MojoExecutionException if logging fails.
3061      *
3062      * @since 1.1
3063      */
3064     protected void logProcessingModel( final String toolName, final String model ) throws MojoExecutionException
3065     {
3066         if ( this.isLoggable( Level.INFO ) )
3067         {
3068             this.log( Level.INFO, Messages.getMessage( "processingModel", toolName, model ), null );
3069         }
3070     }
3071 
3072     /**
3073      * Logs a message stating that a module has not been found.
3074      *
3075      * @param module The module not having been found.
3076      *
3077      * @throws MojoExecutionException if logging fails.
3078      */
3079     protected void logMissingModule( final String module ) throws MojoExecutionException
3080     {
3081         if ( this.isLoggable( Level.WARNING ) )
3082         {
3083             this.log( Level.WARNING, Messages.getMessage( "missingModule", module ), null );
3084         }
3085     }
3086 
3087     /**
3088      * Logs a message stating that a tool successfully completed execution.
3089      *
3090      * @param toolName The name of the tool.
3091      *
3092      * @throws MojoExecutionException if logging fails.
3093      */
3094     protected void logToolSuccess( final String toolName ) throws MojoExecutionException
3095     {
3096         if ( this.isLoggable( Level.INFO ) )
3097         {
3098             this.log( Level.INFO, Messages.getMessage( "toolSuccess", toolName ), null );
3099         }
3100     }
3101 
3102     /**
3103      * Logs a {@code ModelValidationReport}.
3104      *
3105      * @param context The context to use when marshalling detail elements of the report.
3106      * @param level The level to log at.
3107      * @param report The report to log.
3108      *
3109      * @throws MojoExecutionException if logging {@code report} fails.
3110      */
3111     protected void log( final ModelContext context, final Level level, final ModelValidationReport report )
3112         throws MojoExecutionException
3113     {
3114         try
3115         {
3116             if ( !report.getDetails().isEmpty() )
3117             {
3118                 this.logSeparator();
3119                 Marshaller marshaller = null;
3120 
3121                 for ( final ModelValidationReport.Detail detail : report.getDetails() )
3122                 {
3123                     this.log( detail.getLevel(), "o " + detail.getMessage(), null );
3124 
3125                     if ( detail.getElement() != null && this.isLoggable( Level.FINEST ) )
3126                     {
3127                         if ( marshaller == null )
3128                         {
3129                             marshaller = context.createMarshaller( this.getModel() );
3130                             marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE );
3131                         }
3132 
3133                         final StringWriter stringWriter = new StringWriter();
3134                         marshaller.marshal( detail.getElement(), stringWriter );
3135                         this.log( Level.FINEST, stringWriter.toString(), null );
3136                     }
3137                 }
3138             }
3139         }
3140         catch ( final ModelException e )
3141         {
3142             throw new MojoExecutionException( Messages.getMessage( e ), e );
3143         }
3144         catch ( final JAXBException e )
3145         {
3146             String message = Messages.getMessage( e );
3147             if ( message == null && e.getLinkedException() != null )
3148             {
3149                 message = Messages.getMessage( e.getLinkedException() );
3150             }
3151 
3152             throw new MojoExecutionException( message, e );
3153         }
3154     }
3155 
3156     /**
3157      * Logs a message and throwable at a given level.
3158      *
3159      * @param level The level to log at.
3160      * @param message The message to log or {@code null}.
3161      * @param throwable The throwable to log or {@code null}.
3162      *
3163      * @throws MojoExecutionException if logging fails.
3164      */
3165     protected void log( final Level level, final String message, final Throwable throwable )
3166         throws MojoExecutionException
3167     {
3168         BufferedReader reader = null;
3169 
3170         try
3171         {
3172             if ( this.isLoggable( level ) )
3173             {
3174                 reader = new BufferedReader( new StringReader( message == null ? "" : message ) );
3175                 boolean throwableLogged = false;
3176 
3177                 for ( String line = reader.readLine(); line != null; line = reader.readLine() )
3178                 {
3179                     final String mojoMessage =
3180                         Messages.getMessage( this.getLog().isDebugEnabled() ? "debugMessage" : "logMessage", line,
3181                                              Thread.currentThread().getName(), new Date( System.currentTimeMillis() ) );
3182 
3183                     if ( level.intValue() <= Level.CONFIG.intValue() )
3184                     {
3185                         this.getLog().debug( mojoMessage, throwableLogged ? null : throwable );
3186                     }
3187                     else if ( level.intValue() <= Level.INFO.intValue() )
3188                     {
3189                         this.getLog().info( mojoMessage, throwableLogged ? null : throwable );
3190                     }
3191                     else if ( level.intValue() <= Level.WARNING.intValue() )
3192                     {
3193                         this.getLog().warn( mojoMessage, throwableLogged ? null : throwable );
3194                     }
3195                     else if ( level.intValue() <= Level.SEVERE.intValue() )
3196                     {
3197                         this.getLog().error( mojoMessage, throwableLogged ? null : throwable );
3198                     }
3199 
3200                     throwableLogged = true;
3201                 }
3202 
3203                 reader.close();
3204                 reader = null;
3205             }
3206         }
3207         catch ( final IOException e )
3208         {
3209             this.getLog().error( e );
3210             throw new AssertionError( e );
3211         }
3212         finally
3213         {
3214             try
3215             {
3216                 if ( reader != null )
3217                 {
3218                     reader.close();
3219                 }
3220             }
3221             catch ( final IOException e )
3222             {
3223                 this.getLog().error( e );
3224             }
3225         }
3226     }
3227 
3228     /**
3229      * Configures a {@code ModelContext} instance.
3230      *
3231      * @param context The model context to configure.
3232      *
3233      * @throws NullPointerException if {@code context} is {@code null}.
3234      * @throws MojoExecutionException if configuring {@code context} fails.
3235      */
3236     protected void setupModelContext( final ModelContext context ) throws MojoExecutionException
3237     {
3238         if ( context == null )
3239         {
3240             throw new NullPointerException( "context" );
3241         }
3242 
3243         if ( this.isVerbose() || this.getLog().isDebugEnabled() )
3244         {
3245             context.setLogLevel( this.getLog().isDebugEnabled() ? Level.ALL : Level.INFO );
3246         }
3247 
3248         try
3249         {
3250             context.setExecutorService( this.getExecutorService() );
3251             context.setModletSchemaSystemId( this.modletSchemaSystemId );
3252             context.getListeners().add( new ModelContext.Listener()
3253             {
3254 
3255                 @Override
3256                 public void onLog( final Level level, final String message, final Throwable t )
3257                 {
3258                     super.onLog( level, message, t );
3259 
3260                     try
3261                     {
3262                         log( level, message, t );
3263                     }
3264                     catch ( final MojoExecutionException e )
3265                     {
3266                         getLog().error( e );
3267                     }
3268                 }
3269 
3270             } );
3271 
3272             if ( this.providerLocation != null )
3273             {
3274                 context.setAttribute( DefaultModelContext.PROVIDER_LOCATION_ATTRIBUTE_NAME, this.providerLocation );
3275             }
3276 
3277             if ( this.platformProviderLocation != null )
3278             {
3279                 context.setAttribute( DefaultModelContext.PLATFORM_PROVIDER_LOCATION_ATTRIBUTE_NAME,
3280                                       this.platformProviderLocation );
3281 
3282             }
3283 
3284             if ( this.modletLocation != null )
3285             {
3286                 context.setAttribute( DefaultModletProvider.MODLET_LOCATION_ATTRIBUTE_NAME, this.modletLocation );
3287             }
3288 
3289             if ( this.transformerLocation != null )
3290             {
3291                 context.setAttribute( DefaultModelProcessor.TRANSFORMER_LOCATION_ATTRIBUTE_NAME,
3292                                       this.transformerLocation );
3293             }
3294 
3295             if ( this.moduleLocation != null )
3296             {
3297                 context.setAttribute( DefaultModelProvider.MODULE_LOCATION_ATTRIBUTE_NAME, this.moduleLocation );
3298             }
3299 
3300             context.setAttribute( ToolsModelProvider.MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME,
3301                                   this.modelObjectClasspathResolutionEnabled );
3302 
3303             context.setAttribute( ToolsModelProcessor.MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME,
3304                                   this.modelObjectClasspathResolutionEnabled );
3305 
3306             context.setAttribute( DefaultModletProvider.VALIDATING_ATTRIBUTE_NAME,
3307                                   this.modletResourceValidationEnabled );
3308 
3309             context.setAttribute( DefaultModelProvider.VALIDATING_ATTRIBUTE_NAME, this.modelResourceValidationEnabled );
3310             context.setAttribute( DefaultModelValidator.VALIDATE_JAVA_ATTRIBUTE_NAME, this.javaValidationEnabled );
3311 
3312             if ( this.modelContextAttributes != null )
3313             {
3314                 for ( final ModelContextAttribute e : this.modelContextAttributes )
3315                 {
3316                     final Object object = e.getObject( context );
3317 
3318                     if ( object != null )
3319                     {
3320                         context.setAttribute( e.getKey(), object );
3321                     }
3322                     else
3323                     {
3324                         context.clearAttribute( e.getKey() );
3325                     }
3326                 }
3327             }
3328 
3329             if ( ( this.modletIncludes != null && !this.modletIncludes.isEmpty() )
3330                      || ( this.modletExcludes != null && !this.modletExcludes.isEmpty() ) )
3331             {
3332                 final Modlets modlets = context.getModlets().clone();
3333 
3334                 for ( final Iterator<Modlet> it = modlets.getModlet().iterator(); it.hasNext(); )
3335                 {
3336                     final Modlet modlet = it.next();
3337 
3338                     if ( this.modletIncludes != null
3339                              && !this.modletIncludes.isEmpty()
3340                              && !this.modletIncludes.contains( modlet.getName() ) )
3341                     {
3342                         it.remove();
3343                         this.log( Level.INFO, Messages.getMessage( "excludingModlet", modlet.getName() ), null );
3344                         continue;
3345                     }
3346 
3347                     if ( this.modletExcludes != null
3348                              && !this.modletExcludes.isEmpty()
3349                              && this.modletExcludes.contains( modlet.getName() ) )
3350                     {
3351                         it.remove();
3352                         this.log( Level.INFO, Messages.getMessage( "excludingModlet", modlet.getName() ), null );
3353                         continue;
3354                     }
3355 
3356                     this.log( Level.INFO, Messages.getMessage( "includingModlet", modlet.getName() ), null );
3357                 }
3358 
3359                 context.setModlets( modlets );
3360             }
3361         }
3362         catch ( final InstantiationException e )
3363         {
3364             throw new MojoExecutionException( Messages.getMessage( e ), e );
3365         }
3366         catch ( final ModelException e )
3367         {
3368             throw new MojoExecutionException( Messages.getMessage( e ), e );
3369         }
3370     }
3371 
3372     /**
3373      * Configures a {@code JomcTool} instance.
3374      *
3375      * @param context The model context to use for configuring {@code tool}.
3376      * @param tool The tool to configure.
3377      *
3378      * @throws NullPointerException if {@code context} of {@code tool} is {@code null}.
3379      * @throws MojoExecutionException if configuring {@code tool} fails.
3380      */
3381     @SuppressWarnings( "deprecation" )
3382     protected void setupJomcTool( final ModelContext context, final JomcTool tool ) throws MojoExecutionException
3383     {
3384         if ( context == null )
3385         {
3386             throw new NullPointerException( "context" );
3387         }
3388         if ( tool == null )
3389         {
3390             throw new NullPointerException( "tool" );
3391         }
3392 
3393         try
3394         {
3395             if ( this.isVerbose() || this.getLog().isDebugEnabled() )
3396             {
3397                 tool.setLogLevel( this.getLog().isDebugEnabled() ? Level.ALL : Level.INFO );
3398             }
3399 
3400             tool.setExecutorService( this.getExecutorService() );
3401             tool.getListeners().add( new JomcTool.Listener()
3402             {
3403 
3404                 @Override
3405                 public void onLog( final Level level, final String message, final Throwable t )
3406                 {
3407                     super.onLog( level, message, t );
3408 
3409                     try
3410                     {
3411                         log( level, message, t );
3412                     }
3413                     catch ( final MojoExecutionException e )
3414                     {
3415                         getLog().error( e );
3416                     }
3417                 }
3418 
3419             } );
3420 
3421             if ( this.templateEncoding != null )
3422             {
3423                 if ( this.isLoggable( Level.WARNING ) )
3424                 {
3425                     this.log( Level.WARNING, Messages.getMessage(
3426                               "deprecationWarning", "templateEncoding", "defaultTemplateEncoding" ), null );
3427 
3428                 }
3429 
3430                 tool.setDefaultTemplateEncoding( this.templateEncoding );
3431             }
3432             else
3433             {
3434                 tool.setDefaultTemplateEncoding( this.defaultTemplateEncoding );
3435             }
3436 
3437             tool.setInputEncoding( this.sourceEncoding );
3438             tool.setOutputEncoding( this.sourceEncoding );
3439             tool.setDefaultTemplateProfile( this.defaultTemplateProfile );
3440             tool.setTemplateProfile( this.templateProfile );
3441             tool.setModel( this.getModel( context ) );
3442 
3443             if ( this.indentation != null )
3444             {
3445                 tool.setIndentation( StringEscapeUtils.unescapeJava( this.indentation ) );
3446             }
3447 
3448             if ( this.lineSeparator != null )
3449             {
3450                 tool.setLineSeparator( StringEscapeUtils.unescapeJava( this.lineSeparator ) );
3451             }
3452 
3453             if ( this.locale != null )
3454             {
3455                 tool.setLocale( new Locale( StringUtils.defaultString( this.locale.getLanguage() ),
3456                                             StringUtils.defaultString( this.locale.getCountry() ),
3457                                             StringUtils.defaultString( this.locale.getVariant() ) ) );
3458 
3459             }
3460 
3461             if ( this.velocityPropertyResources != null )
3462             {
3463                 for ( int i = 0, s0 = this.velocityPropertyResources.size(); i < s0; i++ )
3464                 {
3465                     for ( final Map.Entry<Object, Object> e : this.getProperties(
3466                         context, this.velocityPropertyResources.get( i ) ).entrySet() )
3467                     {
3468                         if ( e.getValue() != null )
3469                         {
3470                             tool.getVelocityEngine().setProperty( e.getKey().toString(), e );
3471                         }
3472                         else
3473                         {
3474                             tool.getVelocityEngine().clearProperty( e.getKey().toString() );
3475                         }
3476                     }
3477                 }
3478             }
3479 
3480             if ( this.velocityProperties != null )
3481             {
3482                 for ( final VelocityProperty e : this.velocityProperties )
3483                 {
3484                     final Object object = e.getObject( context );
3485 
3486                     if ( object != null )
3487                     {
3488                         tool.getVelocityEngine().setProperty( e.getKey(), object );
3489                     }
3490                     else
3491                     {
3492                         tool.getVelocityEngine().clearProperty( e.getKey() );
3493                     }
3494                 }
3495             }
3496 
3497             for ( final Map.Entry<Object, Object> e : System.getProperties().entrySet() )
3498             {
3499                 tool.getTemplateParameters().put( e.getKey().toString(), e.getValue() );
3500             }
3501 
3502             if ( this.getMavenProject().getProperties() != null )
3503             {
3504                 for ( final Map.Entry<Object, Object> e : System.getProperties().entrySet() )
3505                 {
3506                     tool.getTemplateParameters().put( e.getKey().toString(), e.getValue() );
3507                 }
3508             }
3509 
3510             if ( this.templateParameterResources != null )
3511             {
3512                 for ( int i = 0, s0 = this.templateParameterResources.size(); i < s0; i++ )
3513                 {
3514                     for ( final Map.Entry<Object, Object> e : this.getProperties(
3515                         context, this.templateParameterResources.get( i ) ).entrySet() )
3516                     {
3517                         if ( e.getValue() != null )
3518                         {
3519                             tool.getTemplateParameters().put( e.getKey().toString(), e.getValue() );
3520                         }
3521                         else
3522                         {
3523                             tool.getTemplateParameters().remove( e.getKey().toString() );
3524                         }
3525                     }
3526                 }
3527             }
3528 
3529             if ( this.templateParameters != null )
3530             {
3531                 for ( final TemplateParameter e : this.templateParameters )
3532                 {
3533                     final Object object = e.getObject( context );
3534 
3535                     if ( object != null )
3536                     {
3537                         tool.getTemplateParameters().put( e.getKey(), object );
3538                     }
3539                     else
3540                     {
3541                         tool.getTemplateParameters().remove( e.getKey() );
3542                     }
3543                 }
3544             }
3545 
3546             if ( this.templateLocation != null )
3547             {
3548                 final URL url = this.getDirectory( this.templateLocation );
3549                 tool.setTemplateLocation( url );
3550 
3551                 if ( url == null && this.isLoggable( Level.WARNING ) )
3552                 {
3553                     this.log( Level.WARNING, Messages.getMessage( "locationNotFound", this.templateLocation ), null );
3554                 }
3555             }
3556         }
3557         catch ( final InstantiationException e )
3558         {
3559             throw new MojoExecutionException( Messages.getMessage( e ), e );
3560         }
3561     }
3562 
3563     /**
3564      * Copies a file.
3565      *
3566      * @param source The file to copy.
3567      * @param target The file to copy the source file to.
3568      *
3569      * @throws IOException if copying fails.
3570      *
3571      * @since 1.10
3572      *
3573      * @deprecated This method will be removed in 2.0 due to availability of Java 7 file copying features.
3574      */
3575     @Deprecated
3576     protected final void copyFile( final File source, final File target ) throws IOException
3577     {
3578         InputStream in = null;
3579         OutputStream out = null;
3580         try
3581         {
3582             if ( !source.equals( target ) )
3583             {
3584                 in = new FileInputStream( source );
3585                 out = new FileOutputStream( target );
3586 
3587                 final byte[] buffer = new byte[ 65536 ];
3588 
3589                 for ( int read = in.read( buffer );
3590                       read >= 0;
3591                       out.write( buffer, 0, read ), read = in.read( buffer ) );
3592 
3593                 out.close();
3594                 out = null;
3595 
3596                 in.close();
3597                 in = null;
3598             }
3599         }
3600         finally
3601         {
3602             try
3603             {
3604                 if ( out != null )
3605                 {
3606                     out.close();
3607                 }
3608             }
3609             catch ( final IOException e )
3610             {
3611                 this.getLog().warn( e );
3612             }
3613             finally
3614             {
3615                 try
3616                 {
3617                     if ( in != null )
3618                     {
3619                         in.close();
3620                     }
3621                 }
3622                 catch ( final IOException e )
3623                 {
3624                     this.getLog().warn( e );
3625                 }
3626             }
3627         }
3628     }
3629 
3630     /**
3631      * Copies a directory recursively.
3632      *
3633      * @param source The directory to copy.
3634      * @param target The directory to copy to.
3635      *
3636      * @throws IOException if copying fails.
3637      *
3638      * @since 1.10
3639      */
3640     @SuppressWarnings( "deprecation" )
3641     protected final void copyDirectory( final File source, final File target ) throws IOException
3642     {
3643         if ( !target.isDirectory() && !target.mkdirs() )
3644         {
3645             throw new IOException( Messages.getMessage( "failedCreatingDirectory", target.getAbsolutePath() ) );
3646         }
3647 
3648         for ( final File file : source.listFiles() )
3649         {
3650             final File targetFile = new File( target, file.getName() );
3651 
3652             if ( file.isFile() )
3653             {
3654                 this.copyFile( file, targetFile );
3655             }
3656             else if ( file.isDirectory() )
3657             {
3658                 this.copyDirectory( file, targetFile );
3659             }
3660             else
3661             {
3662                 throw new IOException( Messages.getMessage( "failedCopying", file.getAbsolutePath(),
3663                                                             targetFile.getAbsolutePath() ) );
3664 
3665             }
3666         }
3667     }
3668 
3669     private Artifact getPluginArtifact( final Artifact a )
3670     {
3671         for ( int i = 0, s0 = this.pluginArtifacts.size(); i < s0; i++ )
3672         {
3673             final Artifact pluginArtifact = this.pluginArtifacts.get( i );
3674 
3675             if ( pluginArtifact.getGroupId().equals( a.getGroupId() )
3676                      && pluginArtifact.getArtifactId().equals( a.getArtifactId() )
3677                      && ( pluginArtifact.hasClassifier()
3678                           ? pluginArtifact.getClassifier().equals( a.getClassifier() )
3679                           : !a.hasClassifier() ) )
3680             {
3681                 return pluginArtifact;
3682             }
3683         }
3684 
3685         return null;
3686     }
3687 
3688 }