View Javadoc

1   /*
2    *   Copyright (c) 2009 The JOMC Project
3    *   Copyright (c) 2005 Christian Schulte <cs@jomc.org>
4    *   All rights reserved.
5    *
6    *   Redistribution and use in source and binary forms, with or without
7    *   modification, are permitted provided that the following conditions
8    *   are met:
9    *
10   *     o Redistributions of source code must retain the above copyright
11   *       notice, this list of conditions and the following disclaimer.
12   *
13   *     o Redistributions in binary form must reproduce the above copyright
14   *       notice, this list of conditions and the following disclaimer in
15   *       the documentation and/or other materials provided with the
16   *       distribution.
17   *
18   *   THIS SOFTWARE IS PROVIDED BY THE JOMC PROJECT AND CONTRIBUTORS "AS IS"
19   *   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20   *   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21   *   PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE JOMC PROJECT OR
22   *   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23   *   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24   *   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
25   *   OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26   *   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
27   *   OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
28   *   ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29   *
30   *   $Id: AbstractJomcMojo.java 1237 2010-01-09 20:22:54Z schulte2005 $
31   *
32   */
33  package org.jomc.mojo;
34  
35  import java.io.BufferedReader;
36  import java.io.File;
37  import java.io.IOException;
38  import java.io.StringReader;
39  import java.io.StringWriter;
40  import java.net.URL;
41  import java.net.URLClassLoader;
42  import java.text.MessageFormat;
43  import java.util.Collection;
44  import java.util.HashSet;
45  import java.util.Iterator;
46  import java.util.LinkedList;
47  import java.util.ResourceBundle;
48  import java.util.Set;
49  import java.util.logging.Level;
50  import javax.xml.bind.JAXBException;
51  import javax.xml.bind.Marshaller;
52  import javax.xml.transform.ErrorListener;
53  import javax.xml.transform.Transformer;
54  import javax.xml.transform.TransformerConfigurationException;
55  import javax.xml.transform.TransformerException;
56  import javax.xml.transform.TransformerFactory;
57  import javax.xml.transform.stream.StreamSource;
58  import org.apache.maven.artifact.Artifact;
59  import org.apache.maven.plugin.AbstractMojo;
60  import org.apache.maven.plugin.MojoExecutionException;
61  import org.apache.maven.plugin.MojoFailureException;
62  import org.apache.maven.project.MavenProject;
63  import org.jomc.model.DefaultModelProvider;
64  import org.jomc.model.ModelContext;
65  import org.jomc.model.ModelException;
66  import org.jomc.model.ModelValidationReport;
67  import org.jomc.model.Module;
68  import org.jomc.model.Modules;
69  import org.jomc.tools.JavaClasses;
70  import org.jomc.tools.JavaSources;
71  import org.jomc.tools.JomcTool;
72  
73  /**
74   * Base mojo class for executing {@code JomcTool}s.
75   *
76   * @author <a href="mailto:cs@jomc.org">Christian Schulte</a>
77   * @version $Id: AbstractJomcMojo.java 1237 2010-01-09 20:22:54Z schulte2005 $
78   */
79  public abstract class AbstractJomcMojo extends AbstractMojo
80  {
81  
82      /**
83       * The encoding to use for reading and writing files.
84       *
85       * @parameter default-value="${project.build.sourceEncoding}"
86       */
87      private String sourceEncoding;
88  
89      /**
90       * The encoding to use for reading templates.
91       *
92       * @parameter
93       */
94      private String templateEncoding;
95  
96      /**
97       * The template profile to use when accessing templates.
98       *
99       * @parameter default-value="default"
100      */
101     private String templateProfile;
102 
103     /**
104      * The location to search for modules.
105      *
106      * @parameter
107      */
108     private String moduleLocation;
109 
110     /**
111      * The Maven project of the instance.
112      * @parameter expression="${project}"
113      * @required
114      * @readonly
115      */
116     private MavenProject mavenProject;
117 
118     /**
119      * Controls verbosity of the plugin.
120      *
121      * @parameter expression="${jomc.verbose}" default-value="false"
122      */
123     private boolean verbose;
124 
125     /**
126      * Contols processing of java sources.
127      *
128      * @parameter expression="${jomc.javaSources.enabled}" default-value="true"
129      */
130     private boolean javaSourceProcessingEnabled;
131 
132     /**
133      * Contols processing of java classes.
134      *
135      * @parameter expression="${jomc.javaClasses.enabled}" default-value="true"
136      */
137     private boolean javaClassProcessingEnabled;
138 
139     /** The tool for managing sources. */
140     private JavaSources javaSourcesTool;
141 
142     /** The tool for managing classes. */
143     private JavaClasses javaClassesTool;
144 
145     /** The class loader of the project's runtime classpath including any provided dependencies. */
146     private ClassLoader mainClassLoader;
147 
148     /** The class loader of the project's test classpath including any provided dependencies. */
149     private ClassLoader testClassLoader;
150 
151     public void execute() throws MojoExecutionException, MojoFailureException
152     {
153         try
154         {
155             this.logSeparator( Level.INFO );
156             this.log( Level.INFO, this.getMessage( "title" ).format( null ), null );
157             this.logSeparator( Level.INFO );
158             this.executeTool();
159         }
160         catch ( final Exception e )
161         {
162             throw new MojoExecutionException( e.getMessage(), e );
163         }
164     }
165 
166     /**
167      * Gets the name of this tool.
168      *
169      * @return The name of this tool.
170      * @throws MojoExecutionException if getting the name of this tool fails.
171      */
172     protected abstract String getToolName() throws MojoExecutionException;
173 
174     /**
175      * Gets the class loader of this tool.
176      *
177      * @return The class loader of this tool.
178      *
179      * @throws MojoExecutionException if getting the class loader fails.
180      */
181     protected abstract ClassLoader getToolClassLoader() throws MojoExecutionException;
182 
183     /**
184      * Executes this tool.
185      *
186      * @throws Exception if execution of this tool fais.
187      */
188     protected abstract void executeTool() throws Exception;
189 
190     /**
191      * Gets the model context of the instance.
192      *
193      * @return The model context of the instance.
194      *
195      * @throws MojoExecutionException if getting the model context of the instance fails.
196      */
197     protected ModelContext getModelContext() throws MojoExecutionException
198     {
199         try
200         {
201             final ModelContext context = ModelContext.createModelContext( this.getToolClassLoader() );
202             this.setupModelContext( context );
203             return context;
204         }
205         catch ( final ModelException e )
206         {
207             throw new MojoExecutionException( e.getMessage(), e );
208         }
209     }
210 
211     /**
212      * Gets the Maven project of the instance.
213      *
214      * @return The Maven project of the instance.
215      *
216      * @throws MojoExecutionException if getting the maven project of the instance fails.
217      */
218     protected MavenProject getMavenProject() throws MojoExecutionException
219     {
220         return this.mavenProject;
221     }
222 
223     /**
224      * Gets the tool for managing sources of the instance.
225      *
226      * @return The tool for managing sources of the instance.
227      *
228      * @throws MojoExecutionException if getting the tool of the instance fails.
229      */
230     protected JavaSources getJavaSourcesTool() throws MojoExecutionException
231     {
232         if ( this.javaSourcesTool == null )
233         {
234             this.javaSourcesTool = new JavaSources();
235             this.setupJomcTool( this.javaSourcesTool );
236         }
237 
238         return this.javaSourcesTool;
239     }
240 
241     /**
242      * Gets the tool for managing classes of the instance.
243      *
244      * @return The tool for managing classes of the instance.
245      *
246      * @throws MojoExecutionException if getting the tool of the instance fails.
247      */
248     protected JavaClasses getJavaClassesTool() throws MojoExecutionException
249     {
250         if ( this.javaClassesTool == null )
251         {
252             this.javaClassesTool = new JavaClasses();
253             this.setupJomcTool( this.javaClassesTool );
254         }
255 
256         return this.javaClassesTool;
257     }
258 
259     /**
260      * Gets the project's runtime class loader of the instance.
261      *
262      * @return The project's runtime class loader of the instance.
263      *
264      * @throws MojoExecutionException if getting the class loader fails.
265      */
266     protected ClassLoader getMainClassLoader() throws MojoExecutionException
267     {
268         try
269         {
270             if ( this.mainClassLoader == null )
271             {
272                 final Collection urls = new LinkedList();
273                 for ( final Iterator it = this.getMainClasspathElements().iterator(); it.hasNext(); )
274                 {
275                     final String element = (String) it.next();
276                     final URL url = new File( element ).toURI().toURL();
277                     if ( !urls.contains( url ) )
278                     {
279                         urls.add( url );
280                         this.log( Level.FINE, this.getClasspathElementMessage( url.toExternalForm() ), null );
281                     }
282                 }
283 
284                 this.mainClassLoader = new URLClassLoader( (URL[]) urls.toArray( new URL[ urls.size() ] ),
285                                                            Thread.currentThread().getContextClassLoader() );
286 
287             }
288 
289             return this.mainClassLoader;
290         }
291         catch ( final IOException e )
292         {
293             throw new MojoExecutionException( e.getMessage(), e );
294         }
295     }
296 
297     /**
298      * Gets the project's test class loader of the instance.
299      *
300      * @return The project's test class loader of the instance.
301      *
302      * @throws MojoExecutionException if getting the class loader fails.
303      */
304     protected ClassLoader getTestClassLoader() throws MojoExecutionException
305     {
306         try
307         {
308             if ( this.testClassLoader == null )
309             {
310                 final Collection urls = new LinkedList();
311                 for ( final Iterator it = this.getTestClasspathElements().iterator(); it.hasNext(); )
312                 {
313                     final String element = (String) it.next();
314                     final URL url = new File( element ).toURI().toURL();
315                     if ( !urls.contains( url ) )
316                     {
317                         urls.add( url );
318                         this.log( Level.FINE, this.getClasspathElementMessage( url.toExternalForm() ), null );
319                     }
320                 }
321 
322                 this.testClassLoader = new URLClassLoader( (URL[]) urls.toArray( new URL[ urls.size() ] ),
323                                                            Thread.currentThread().getContextClassLoader() );
324 
325             }
326 
327             return this.testClassLoader;
328         }
329         catch ( final IOException e )
330         {
331             throw new MojoExecutionException( e.getMessage(), e );
332         }
333     }
334 
335     /**
336      * Gets the project's runtime classpath elements.
337      *
338      * @return A set of classpath element strings.
339      *
340      * @throws MojoExecutionException if getting the classpath elements fails.
341      */
342     protected Set getMainClasspathElements() throws MojoExecutionException
343     {
344         final Set elements = new HashSet();
345         elements.add( this.getMavenProject().getBuild().getOutputDirectory() );
346 
347         for ( final Iterator it = this.getMavenProject().getRuntimeArtifacts().iterator(); it.hasNext(); )
348         {
349             final Artifact a = (Artifact) it.next();
350 
351             if ( a.getFile() == null )
352             {
353                 this.log( Level.WARNING, this.getIgnoredMessage( a.toString() ), null );
354                 continue;
355             }
356 
357             if ( a.getGroupId().equals( "org.jomc" ) &&
358                  ( a.getArtifactId().equals( "jomc-util" ) || a.getArtifactId().equals( "jomc-model" ) ||
359                    a.getArtifactId().equals( "jomc-tools" ) ) )
360             {
361                 continue;
362             }
363 
364             final String element = a.getFile().getAbsolutePath();
365             this.log( Level.FINE, this.getRuntimeElementMessage( element ), null );
366             elements.add( element );
367         }
368 
369         for ( final Iterator it = this.getMavenProject().getCompileArtifacts().iterator(); it.hasNext(); )
370         {
371             final Artifact a = (Artifact) it.next();
372 
373             if ( a.getFile() == null )
374             {
375                 this.log( Level.WARNING, this.getIgnoredMessage( a.toString() ), null );
376                 continue;
377             }
378 
379             if ( a.getGroupId().equals( "org.jomc" ) &&
380                  ( a.getArtifactId().equals( "jomc-util" ) || a.getArtifactId().equals( "jomc-model" ) ||
381                    a.getArtifactId().equals( "jomc-tools" ) ) )
382             {
383                 continue;
384             }
385 
386             final String element = a.getFile().getAbsolutePath();
387             this.log( Level.FINE, this.getCompileElementMessage( element ), null );
388             elements.add( element );
389         }
390 
391         return elements;
392     }
393 
394     /**
395      * Gets the project's test classpath elements.
396      *
397      * @return A set of classpath element strings.
398      *
399      * @throws MojoExecutionException if getting the classpath elements fails.
400      */
401     protected Set getTestClasspathElements() throws MojoExecutionException
402     {
403         final Set elements = new HashSet();
404 
405         elements.add( this.getMavenProject().getBuild().getOutputDirectory() );
406         elements.add( this.getMavenProject().getBuild().getTestOutputDirectory() );
407 
408         for ( final Iterator it = this.getMavenProject().getTestArtifacts().iterator(); it.hasNext(); )
409         {
410             final Artifact a = (Artifact) it.next();
411 
412             if ( a.getFile() == null )
413             {
414                 this.log( Level.WARNING, this.getIgnoredMessage( a.toString() ), null );
415                 continue;
416             }
417 
418             if ( a.getGroupId().equals( "org.jomc" ) &&
419                  ( a.getArtifactId().equals( "jomc-util" ) || a.getArtifactId().equals( "jomc-model" ) ||
420                    a.getArtifactId().equals( "jomc-tools" ) ) )
421             {
422                 continue;
423             }
424 
425             final String element = a.getFile().getAbsolutePath();
426             this.log( Level.FINE, this.getTestElementMessage( element ), null );
427             elements.add( element );
428         }
429 
430         return elements;
431     }
432 
433     /**
434      * Gets a flag indicating the processing of Java sources is enabled.
435      *
436      * @return {@code true} if processing of Java sources is enabled; {@code false} else.
437      */
438     protected boolean isJavaSourceProcessingEnabled()
439     {
440         return this.javaSourceProcessingEnabled;
441     }
442 
443     /**
444      * Gets a flag indicating the processing of Java classes is enabled.
445      *
446      * @return {@code true} if processing of Java classes is enabled; {@code false} else.
447      */
448     protected boolean isJavaClassProcessingEnabled()
449     {
450         return this.javaClassProcessingEnabled;
451     }
452 
453     /**
454      * Gets the name of the JOMC module to process.
455      *
456      * @return The name of the JOMC module to process.
457      *
458      * @throws MojoExecutionException if getting the name of the JOMC module fails.
459      */
460     protected String getJomcModuleName() throws MojoExecutionException
461     {
462         return this.getMavenProject().getName();
463     }
464 
465     /**
466      * Gets the name of the JOMC test module to process.
467      *
468      * @return The name of the JOMC test module to process.
469      *
470      * @throws MojoExecutionException if getting the name of the JOMC test module fails.
471      */
472     protected String getJomcTestModuleName() throws MojoExecutionException
473     {
474         return this.getMavenProject().getName() + " Tests";
475     }
476 
477     /**
478      * Gets a transformer from a given file.
479      *
480      * @param file The file to initialize the transformer with.
481      *
482      * @return A {@code Transformer} backed by {@code file}.
483      *
484      * @throws NullPointerException if {@code file} is {@code null}.
485      * @throws MojoExecutionException if there are errors when parsing {@code file} or creating a
486      * {@code Transformer} fails.
487      */
488     protected Transformer getTransformer( final File file ) throws MojoExecutionException
489     {
490         if ( file == null )
491         {
492             throw new NullPointerException( "file" );
493         }
494 
495         try
496         {
497             final TransformerFactory transformerFactory = TransformerFactory.newInstance();
498             transformerFactory.setErrorListener( new ErrorListener()
499             {
500 
501                 public void warning( final TransformerException exception ) throws TransformerException
502                 {
503                     getLog().warn( exception );
504                 }
505 
506                 public void error( final TransformerException exception ) throws TransformerException
507                 {
508                     getLog().error( exception );
509                     throw exception;
510                 }
511 
512                 public void fatalError( final TransformerException exception ) throws TransformerException
513                 {
514                     getLog().error( exception );
515                     throw exception;
516                 }
517 
518             } );
519 
520             return transformerFactory.newTransformer( new StreamSource( file ) );
521         }
522         catch ( final TransformerConfigurationException e )
523         {
524             throw new MojoExecutionException( e.getMessage(), e );
525         }
526     }
527 
528     protected Modules getToolModules() throws MojoExecutionException
529     {
530         try
531         {
532             DefaultModelProvider.setDefaultModuleLocation( this.moduleLocation );
533             final Modules modules = this.getModelContext().findModules();
534             final Module classpathModule = modules.getClasspathModule(
535                 Modules.getDefaultClasspathModuleName(), this.getToolClassLoader() );
536 
537             if ( classpathModule != null )
538             {
539                 modules.getModule().add( classpathModule );
540             }
541 
542             return modules;
543         }
544         catch ( final ModelException e )
545         {
546             throw new MojoExecutionException( e.getMessage(), e );
547         }
548     }
549 
550     protected void logSeparator( final Level level ) throws MojoExecutionException
551     {
552         this.log( level, this.getMessage( "separator" ).format( null ), null );
553     }
554 
555     protected void logProcessingModule( final Module module ) throws MojoExecutionException
556     {
557         this.log( Level.INFO, this.getProcessingModuleMesage( module ), null );
558     }
559 
560     protected void logMissingModule( final String moduleName ) throws MojoExecutionException
561     {
562         this.log( Level.WARNING, this.getMissingModuleMesage( moduleName ), null );
563     }
564 
565     protected void logToolSuccess() throws MojoExecutionException
566     {
567         this.log( Level.INFO, this.getToolSuccessMessage(), null );
568     }
569 
570     protected void log( final Level level, final ModelValidationReport report ) throws MojoExecutionException
571     {
572         try
573         {
574             if ( !report.isModelValid() || !report.getDetails().isEmpty() )
575             {
576                 this.logSeparator( level );
577             }
578 
579             if ( !report.isModelValid() )
580             {
581                 this.log( level, this.getMessage( "invalidModel" ).format( null ), null );
582             }
583 
584             if ( !report.getDetails().isEmpty() )
585             {
586                 final Marshaller marshaller = this.getModelContext().createMarshaller();
587 
588                 for ( ModelValidationReport.Detail detail : report.getDetails() )
589                 {
590                     this.log( detail.getLevel(), System.getProperty( "line.separator" ), null );
591                     this.log( detail.getLevel(), detail.getMessage(), null );
592 
593                     if ( detail.getElement() != null )
594                     {
595                         final StringWriter stringWriter = new StringWriter();
596                         marshaller.marshal( detail.getElement(), stringWriter );
597                         this.log( Level.FINE, stringWriter.toString(), null );
598                     }
599                 }
600             }
601 
602             if ( !report.isModelValid() || !report.getDetails().isEmpty() )
603             {
604                 this.logSeparator( level );
605             }
606         }
607         catch ( final ModelException e )
608         {
609             throw new MojoExecutionException( e.getMessage(), e );
610         }
611         catch ( final JAXBException e )
612         {
613             throw new MojoExecutionException( e.getMessage(), e );
614         }
615     }
616 
617     protected void log( final Level level, final String message, final Throwable throwable )
618     {
619         try
620         {
621             if ( level.intValue() < Level.INFO.intValue() || level.intValue() >= Level.WARNING.intValue() ||
622                  this.verbose )
623             {
624                 String line;
625                 final BufferedReader reader = new BufferedReader( new StringReader( message ) );
626                 while ( ( line = reader.readLine() ) != null )
627                 {
628                     final String mojoMessage = "[JOMC] " + line;
629 
630                     if ( ( level.equals( Level.CONFIG ) || level.equals( Level.FINE ) || level.equals( Level.FINER ) ||
631                            level.equals( Level.FINEST ) ) && this.getLog().isDebugEnabled() )
632                     {
633                         this.getLog().debug( mojoMessage, throwable );
634                     }
635                     else if ( level.equals( Level.INFO ) && this.getLog().isInfoEnabled() )
636                     {
637                         this.getLog().info( mojoMessage, throwable );
638                     }
639                     else if ( level.equals( Level.SEVERE ) && this.getLog().isErrorEnabled() )
640                     {
641                         this.getLog().error( mojoMessage, throwable );
642                     }
643                     else if ( level.equals( Level.WARNING ) && this.getLog().isWarnEnabled() )
644                     {
645                         this.getLog().warn( mojoMessage, throwable );
646                     }
647                     else if ( this.getLog().isDebugEnabled() )
648                     {
649                         this.getLog().debug( mojoMessage, throwable );
650                     }
651                 }
652             }
653         }
654         catch ( final IOException e )
655         {
656             this.getLog().error( e );
657             throw new AssertionError( e );
658         }
659     }
660 
661     private void setupJomcTool( final JomcTool tool ) throws MojoExecutionException
662     {
663         if ( this.verbose || this.getLog().isDebugEnabled() )
664         {
665             tool.setLogLevel( this.getLog().isDebugEnabled() ? Level.ALL : Level.INFO );
666         }
667 
668         tool.getListeners().add( new JomcTool.Listener()
669         {
670 
671             public void onLog( final Level level, final String message, final Throwable t )
672             {
673                 log( level, message, t );
674             }
675 
676         } );
677 
678         tool.setTemplateEncoding( this.templateEncoding );
679         tool.setInputEncoding( this.sourceEncoding );
680         tool.setOutputEncoding( this.sourceEncoding );
681         tool.setProfile( this.templateProfile );
682         tool.setModules( this.getToolModules() );
683     }
684 
685     private void setupModelContext( final ModelContext modelContext )
686     {
687         if ( this.verbose || this.getLog().isDebugEnabled() )
688         {
689             modelContext.setLogLevel( this.getLog().isDebugEnabled() ? Level.ALL : Level.INFO );
690         }
691 
692         modelContext.getListeners().add( new ModelContext.Listener()
693         {
694 
695             public void onLog( final Level level, final String message, final Throwable t )
696             {
697                 log( level, message, t );
698             }
699 
700         } );
701     }
702 
703     private MessageFormat getMessage( final String key )
704     {
705         return new MessageFormat( ResourceBundle.getBundle( AbstractJomcMojo.class.getName().replace( '.', '/' ) ).
706             getString( key ) );
707 
708     }
709 
710     private String getIgnoredMessage( final String item )
711     {
712         return this.getMessage( "ignored" ).format( new Object[]
713             {
714                 item
715             } );
716 
717     }
718 
719     private String getRuntimeElementMessage( final String element )
720     {
721         return this.getMessage( "runtimeElement" ).format( new Object[]
722             {
723                 element
724             } );
725 
726     }
727 
728     private String getTestElementMessage( final String element )
729     {
730         return this.getMessage( "testElement" ).format( new Object[]
731             {
732                 element
733             } );
734 
735     }
736 
737     private String getCompileElementMessage( final String element )
738     {
739         return this.getMessage( "compiletimeElement" ).format( new Object[]
740             {
741                 element
742             } );
743 
744     }
745 
746     private String getClasspathElementMessage( final String element )
747     {
748         return this.getMessage( "classpathElement" ).format( new Object[]
749             {
750                 element
751             } );
752 
753     }
754 
755     private String getProcessingModuleMesage( final Module module ) throws MojoExecutionException
756     {
757         return this.getMessage( "processingModule" ).format( new Object[]
758             {
759                 this.getToolName(), module.getName()
760             } );
761 
762     }
763 
764     private String getToolSuccessMessage() throws MojoExecutionException
765     {
766         return this.getMessage( "toolSuccess" ).format( new Object[]
767             {
768                 this.getToolName()
769             } );
770 
771     }
772 
773     private String getMissingModuleMesage( final String moduleName )
774     {
775         return this.getMessage( "missingModule" ).format( new Object[]
776             {
777                 moduleName
778             } );
779 
780     }
781 
782 }