View Javadoc
1   /*
2    *  Licensed under the Apache License, Version 2.0 (the "License");
3    *  you may not use this file except in compliance with the License.
4    *  You may obtain a copy of the License at
5    * 
6    *       http://www.apache.org/licenses/LICENSE-2.0
7    * 
8    *  Unless required by applicable law or agreed to in writing, software
9    *  distributed under the License is distributed on an "AS IS" BASIS,
10   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11   *  See the License for the specific language governing permissions and
12   *  limitations under the License.
13   */
14  
15  package org.apache.maven.plugins.linkcheck;
16  
17  import java.io.File;
18  import java.io.FileNotFoundException;
19  import java.io.FileOutputStream;
20  import java.io.IOException;
21  import java.io.OutputStream;
22  import java.io.PrintStream;
23  import java.io.Reader;
24  import java.io.UnsupportedEncodingException;
25  import java.io.Writer;
26  import java.util.ArrayList;
27  
28  import java.util.Collections;
29  import java.util.List;
30  import java.util.Properties;
31  
32  import org.apache.commons.lang.SystemUtils;
33  import org.apache.maven.artifact.repository.ArtifactRepository;
34  
35  import org.apache.maven.model.Profile;
36  import org.apache.maven.model.Reporting;
37  import org.apache.maven.project.MavenProject;
38  
39  import org.apache.maven.plugin.logging.Log;
40  import org.apache.maven.shared.invoker.DefaultInvocationRequest;
41  import org.apache.maven.shared.invoker.DefaultInvoker;
42  import org.apache.maven.shared.invoker.InvocationOutputHandler;
43  import org.apache.maven.shared.invoker.InvocationRequest;
44  import org.apache.maven.shared.invoker.InvocationResult;
45  import org.apache.maven.shared.invoker.Invoker;
46  import org.apache.maven.shared.invoker.MavenInvocationException;
47  import org.apache.maven.shared.invoker.PrintStreamHandler;
48  
49  import org.codehaus.plexus.util.FileUtils;
50  import org.codehaus.plexus.util.IOUtil;
51  import org.codehaus.plexus.util.ReaderFactory;
52  import org.codehaus.plexus.util.StringUtils;
53  import org.codehaus.plexus.util.WriterFactory;
54  import org.codehaus.plexus.util.cli.CommandLineUtils;
55  
56  
57  /**
58   *
59   * @author ltheussl
60   * @since 1.1
61   */
62  public class SiteInvoker
63  {
64      private final ArtifactRepository localRepository;
65      private final Log log;
66  
67      public SiteInvoker( ArtifactRepository localRepository, Log log )
68      {
69          this.localRepository = localRepository;
70          this.log = log;
71      }
72  
73      /**
74       * Invoke Maven for the <code>site</code> phase for a temporary Maven project using
75       * <code>tmpReportingOutputDirectory</code> as <code>${project.reporting.outputDirectory}</code>.
76       * This is a workaround to be sure that all site files have been correctly generated.
77       * <br/>
78       * <b>Note 1</b>: the Maven Home should be defined in the <code>maven.home</code> Java system property
79       * or defined in <code>M2_HOME</code> system env variables.
80       * <b>Note 2</be>: we can't use <code>siteOutputDirectory</code> param from site plugin because some plugins
81       * <code>${project.reporting.outputDirectory}</code> in their conf.
82       *
83       * @param project the MavenProject to invoke the site on. Not null.
84       * @param tmpReportingOutputDirectory not null
85       * @throws IOException if any
86       */
87      public void invokeSite( MavenProject project, File tmpReportingOutputDirectory )
88          throws IOException
89      {
90          String mavenHome = getMavenHome();
91          if ( StringUtils.isEmpty( mavenHome ) )
92          {
93              getLog().error( "Could NOT invoke Maven because no Maven Home is defined. "
94                  + "You need to set the M2_HOME system env variable or a 'maven.home' Java system property." );
95              return;
96          }
97  
98          // invoker site parameters
99          List goals = Collections.singletonList( "site" );
100         Properties properties = new Properties();
101         properties.put( "linkcheck.skip", "true" ); // to stop recursion
102 
103         File invokerLog =
104             FileUtils.createTempFile( "invoker-site-plugin", ".txt", new File( project.getBuild().getDirectory() ) );
105 
106         // clone project and set a new reporting output dir
107         MavenProject clone;
108         try
109         {
110             clone = (MavenProject) project.clone();
111         }
112         catch ( CloneNotSupportedException e )
113         {
114             IOException ioe = new IOException( "CloneNotSupportedException: " + e.getMessage() );
115             ioe.setStackTrace( e.getStackTrace() );
116             throw ioe;
117         }
118 
119         // MLINKCHECK-1
120         if ( clone.getOriginalModel().getReporting() == null )
121         {
122             clone.getOriginalModel().setReporting( new Reporting() );
123         }
124 
125         clone.getOriginalModel().getReporting().setOutputDirectory( tmpReportingOutputDirectory.getAbsolutePath() );
126         List profileIds = getActiveProfileIds( clone );
127 
128         // create the original model as tmp pom file for the invoker
129         File tmpProjectFile = FileUtils.createTempFile( "pom", ".xml", project.getBasedir() );
130         Writer writer = null;
131         try
132         {
133             writer = WriterFactory.newXmlWriter( tmpProjectFile );
134             clone.writeOriginalModel( writer );
135         }
136         finally
137         {
138             IOUtil.close( writer );
139         }
140 
141         // invoke it
142         try
143         {
144             invoke( tmpProjectFile, invokerLog, mavenHome, goals, profileIds, properties );
145         }
146         finally
147         {
148             if ( !getLog().isDebugEnabled() )
149             {
150                 tmpProjectFile.delete();
151             }
152         }
153     }
154 
155     private static List getActiveProfileIds( MavenProject clone )
156     {
157         List profileIds = new ArrayList();
158 
159         for (Object o : clone.getActiveProfiles()) {
160             profileIds.add(((Profile) o).getId());
161         }
162 
163         return profileIds;
164     }
165 
166     /**
167      * @param projectFile not null, should be in the ${project.basedir}
168      * @param invokerLog not null
169      * @param mavenHome not null
170      * @param goals the list of goals
171      * @param properties the properties for the invoker
172      */
173     private void invoke( File projectFile, File invokerLog, String mavenHome,
174         List goals, List activeProfiles, Properties properties )
175     {
176         Invoker invoker = new DefaultInvoker();
177         invoker.setMavenHome( new File( mavenHome ) );
178         File localRepoDir = new File( localRepository.getBasedir() );
179         invoker.setLocalRepositoryDirectory( localRepoDir );
180 
181         InvocationRequest request = new DefaultInvocationRequest();
182         request.setLocalRepositoryDirectory( localRepoDir );
183         //request.setUserSettingsFile( settingsFile );
184         request.setInteractive( false );
185         request.setShowErrors( getLog().isErrorEnabled() );
186         request.setDebug( getLog().isDebugEnabled() );
187         //request.setShowVersion( false );
188         request.setBaseDirectory( projectFile.getParentFile() );
189         request.setPomFile( projectFile );
190         request.setGoals( goals );
191         request.setProperties( properties );
192         request.setProfiles( activeProfiles );
193 
194         File javaHome = getJavaHome();
195         if ( javaHome != null )
196         {
197             request.setJavaHome( javaHome );
198         }
199 
200         InvocationResult invocationResult;
201         try
202         {
203             if ( getLog().isDebugEnabled() )
204             {
205                 getLog().debug( "Invoking Maven for the goals: " + goals + " with properties=" + properties );
206             }
207             invocationResult = invoke( invoker, request, invokerLog, goals, properties, null );
208         }
209         catch ( MavenInvocationException e )
210         {
211             getLog().error( "Error when invoking Maven, consult the invoker log." );
212             getLog().debug( e );
213             return;
214         }
215 
216         String invokerLogContent = null;
217         Reader reader = null;
218         try
219         {
220             reader = ReaderFactory.newReader( invokerLog, "UTF-8" );
221             invokerLogContent = IOUtil.toString( reader );
222         }
223         catch ( IOException e )
224         {
225             getLog().error( "IOException: " + e.getMessage() );
226             getLog().debug( e );
227         }
228         finally
229         {
230             IOUtil.close( reader );
231         }
232 
233         if ( invokerLogContent != null
234             && invokerLogContent.contains("Error occurred during initialization of VM"))
235         {
236             getLog().info( "Error occurred during initialization of VM, try to use an empty MAVEN_OPTS." );
237 
238             if ( getLog().isDebugEnabled() )
239             {
240                 getLog().debug( "Reinvoking Maven for the goals: " + goals + " with an empty MAVEN_OPTS" );
241             }
242 
243             try
244             {
245                 invocationResult = invoke( invoker, request, invokerLog, goals, properties, "" );
246             }
247             catch ( MavenInvocationException e )
248             {
249                 getLog().error( "Error when reinvoking Maven, consult the invoker log." );
250                 getLog().debug( e );
251                 return;
252             }
253         }
254 
255         if ( invocationResult.getExitCode() != 0 )
256         {
257             if ( getLog().isErrorEnabled() )
258             {
259                 getLog().error( "Error when invoking Maven, consult the invoker log file: "
260                                     + invokerLog.getAbsolutePath() );
261             }
262         }
263     }
264 
265     /**
266      * @param invoker not null
267      * @param request not null
268      * @param invokerLog not null
269      * @param goals the list of goals
270      * @param properties the properties for the invoker
271      * @param mavenOpts could be null
272      * @return the invocation result
273      * @throws MavenInvocationException if any
274      */
275     private InvocationResult invoke( Invoker invoker, InvocationRequest request, File invokerLog, List goals,
276                                      Properties properties, String mavenOpts )
277         throws MavenInvocationException
278     {
279         PrintStream ps;
280         OutputStream os = null;
281         if ( invokerLog != null )
282         {
283             if ( getLog().isDebugEnabled() )
284             {
285                 getLog().debug( "Using " + invokerLog.getAbsolutePath() + " to log the invoker" );
286             }
287 
288             try
289             {
290                 if ( !invokerLog.exists() )
291                 {
292                     invokerLog.getParentFile().mkdirs();
293                 }
294                 os = new FileOutputStream( invokerLog );
295                 ps = new PrintStream( os, true, "UTF-8" );
296             }
297             catch ( FileNotFoundException e )
298             {
299                 if ( getLog().isErrorEnabled() )
300                 {
301                     getLog().error( "FileNotFoundException: " + e.getMessage()
302                                         + ". Using System.out to log the invoker." );
303                 }
304                 ps = System.out;
305             }
306             catch ( UnsupportedEncodingException e )
307             {
308                 if ( getLog().isErrorEnabled() )
309                 {
310                     getLog().error( "UnsupportedEncodingException: " + e.getMessage()
311                                         + ". Using System.out to log the invoker." );
312                 }
313                 ps = System.out;
314             }
315         }
316         else
317         {
318             getLog().debug( "Using System.out to log the invoker." );
319 
320             ps = System.out;
321         }
322 
323         if ( mavenOpts != null )
324         {
325             request.setMavenOpts( mavenOpts );
326         }
327 
328         InvocationOutputHandler outputHandler = new PrintStreamHandler( ps, false );
329         request.setOutputHandler( outputHandler );
330         request.setErrorHandler( outputHandler );
331 
332         outputHandler.consumeLine( "Invoking Maven for the goals: " + goals + " with properties=" + properties );
333         outputHandler.consumeLine( "" );
334         outputHandler.consumeLine( "M2_HOME=" + getMavenHome() );
335         outputHandler.consumeLine( "MAVEN_OPTS=" + getMavenOpts() );
336         outputHandler.consumeLine( "JAVA_HOME=" + getJavaHome() );
337         outputHandler.consumeLine( "JAVA_OPTS=" + getJavaOpts() );
338         outputHandler.consumeLine( "" );
339 
340         try
341         {
342             return invoker.execute( request );
343         }
344         finally
345         {
346             IOUtil.close( os );
347             ps = null;
348         }
349     }
350 
351     /**
352      * @return the Maven home defined in the <code>maven.home</code> system property or defined
353      * in <code>M2_HOME</code> system env variables or null if never setted.
354      * @see #invoke(Invoker, InvocationRequest, File, List, Properties, String)
355      */
356     private String getMavenHome()
357     {
358         String mavenHome = System.getProperty( "maven.home" );
359         if ( mavenHome == null )
360         {
361             try
362             {
363                 mavenHome = CommandLineUtils.getSystemEnvVars().getProperty( "M2_HOME" );
364             }
365             catch ( IOException e )
366             {
367                 getLog().error( "IOException: " + e.getMessage() );
368                 getLog().debug( e );
369             }
370         }
371 
372         File m2Home = new File( mavenHome );
373         if ( !m2Home.exists() )
374         {
375             getLog().error( "Cannot find Maven application directory. Either specify \'maven.home\' "
376                 + "system property, or M2_HOME environment variable." );
377         }
378 
379         return mavenHome;
380     }
381 
382     /**
383      * @return the <code>MAVEN_OPTS</code> env variable value or null if not setted.
384      * @see #invoke(Invoker, InvocationRequest, File, List, Properties, String)
385      */
386     private String getMavenOpts()
387     {
388         String mavenOpts = null;
389         try
390         {
391             mavenOpts = CommandLineUtils.getSystemEnvVars().getProperty( "MAVEN_OPTS" );
392         }
393         catch ( IOException e )
394         {
395             getLog().error( "IOException: " + e.getMessage() );
396             getLog().debug( e );
397         }
398 
399         return mavenOpts;
400     }
401 
402     /**
403      * @return the <code>JAVA_HOME</code> from System.getProperty( "java.home" )
404      * By default, <code>System.getProperty( "java.home" ) = JRE_HOME</code> and <code>JRE_HOME</code>
405      * should be in the <code>JDK_HOME</code> or null if not setted.
406      * @see #invoke(Invoker, InvocationRequest, File, List, Properties, String)
407      */
408     private File getJavaHome()
409     {
410         File javaHome;
411         if ( SystemUtils.IS_OS_MAC_OSX )
412         {
413             javaHome = SystemUtils.getJavaHome();
414         }
415         else
416         {
417             javaHome = new File( SystemUtils.getJavaHome(), ".." );
418         }
419 
420         if ( javaHome == null || !javaHome.exists() )
421         {
422             try
423             {
424                 javaHome = new File( CommandLineUtils.getSystemEnvVars().getProperty( "JAVA_HOME" ) );
425             }
426             catch ( IOException e )
427             {
428                 getLog().error( "IOException: " + e.getMessage() );
429                 getLog().debug( e );
430             }
431         }
432 
433         if ( javaHome == null || !javaHome.exists() )
434         {
435             getLog().error( "Cannot find Java application directory. Either specify \'java.home\' "
436                 + "system property, or JAVA_HOME environment variable." );
437         }
438 
439         return javaHome;
440     }
441 
442     /**
443      * @return the <code>JAVA_OPTS</code> env variable value or null if not setted.
444      * @see #invoke(Invoker, InvocationRequest, File, List, Properties, String)
445      */
446     private String getJavaOpts()
447     {
448         String javaOpts = null;
449         try
450         {
451             javaOpts = CommandLineUtils.getSystemEnvVars().getProperty( "JAVA_OPTS" );
452         }
453         catch ( IOException e )
454         {
455             getLog().error( "IOException: " + e.getMessage() );
456             getLog().debug( e );
457         }
458 
459         return javaOpts;
460     }
461 
462     private Log getLog()
463     {
464         return log;
465     }
466 }