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