001/*
002 *   Copyright (C) 2005 Christian Schulte <cs@schulte.it>
003 *   All rights reserved.
004 *
005 *   Redistribution and use in source and binary forms, with or without
006 *   modification, are permitted provided that the following conditions
007 *   are met:
008 *
009 *     o Redistributions of source code must retain the above copyright
010 *       notice, this list of conditions and the following disclaimer.
011 *
012 *     o Redistributions in binary form must reproduce the above copyright
013 *       notice, this list of conditions and the following disclaimer in
014 *       the documentation and/or other materials provided with the
015 *       distribution.
016 *
017 *   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
018 *   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
019 *   AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
020 *   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
021 *   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
022 *   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
023 *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
024 *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025 *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
026 *   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027 *
028 *   $JOMC: AbstractJomcMojo.java 5328 2016-09-01 01:05:43Z schulte $
029 *
030 */
031package org.jomc.mojo;
032
033import java.io.BufferedReader;
034import java.io.File;
035import java.io.FileInputStream;
036import java.io.FileOutputStream;
037import java.io.IOException;
038import java.io.InputStream;
039import java.io.OutputStream;
040import java.io.StringReader;
041import java.io.StringWriter;
042import java.net.HttpURLConnection;
043import java.net.MalformedURLException;
044import java.net.SocketTimeoutException;
045import java.net.URI;
046import java.net.URISyntaxException;
047import java.net.URL;
048import java.net.URLClassLoader;
049import java.net.URLConnection;
050import java.util.Collection;
051import java.util.Date;
052import java.util.HashSet;
053import java.util.Iterator;
054import java.util.List;
055import java.util.Locale;
056import java.util.Map;
057import java.util.Properties;
058import java.util.Set;
059import java.util.concurrent.ExecutorService;
060import java.util.concurrent.Executors;
061import java.util.concurrent.ThreadFactory;
062import java.util.concurrent.atomic.AtomicInteger;
063import java.util.logging.Level;
064import javax.xml.bind.JAXBException;
065import javax.xml.bind.Marshaller;
066import javax.xml.transform.ErrorListener;
067import javax.xml.transform.Transformer;
068import javax.xml.transform.TransformerConfigurationException;
069import javax.xml.transform.TransformerException;
070import javax.xml.transform.TransformerFactory;
071import javax.xml.transform.stream.StreamSource;
072import org.apache.commons.lang.StringEscapeUtils;
073import org.apache.commons.lang.StringUtils;
074import org.apache.maven.artifact.Artifact;
075import org.apache.maven.artifact.ArtifactUtils;
076import org.apache.maven.execution.MavenSession;
077import org.apache.maven.plugin.AbstractMojo;
078import org.apache.maven.plugin.MojoExecutionException;
079import org.apache.maven.plugin.MojoFailureException;
080import org.apache.maven.plugin.descriptor.MojoDescriptor;
081import org.apache.maven.plugins.annotations.Parameter;
082import org.apache.maven.project.MavenProject;
083import org.jomc.model.Module;
084import org.jomc.model.Modules;
085import org.jomc.model.modlet.DefaultModelProcessor;
086import org.jomc.model.modlet.DefaultModelProvider;
087import org.jomc.model.modlet.DefaultModelValidator;
088import org.jomc.model.modlet.ModelHelper;
089import org.jomc.modlet.DefaultModelContext;
090import org.jomc.modlet.DefaultModletProvider;
091import org.jomc.modlet.Model;
092import org.jomc.modlet.ModelContext;
093import org.jomc.modlet.ModelContextFactory;
094import org.jomc.modlet.ModelException;
095import org.jomc.modlet.ModelValidationReport;
096import org.jomc.modlet.Modlet;
097import org.jomc.modlet.Modlets;
098import org.jomc.tools.ClassFileProcessor;
099import org.jomc.tools.JomcTool;
100import org.jomc.tools.ResourceFileProcessor;
101import org.jomc.tools.SourceFileProcessor;
102import org.jomc.tools.modlet.ToolsModelProcessor;
103import 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 */
111public 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}