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