All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.maven.plugins.javadoc.JavadocUtil Maven / Gradle / Ivy

Go to download

The Maven Javadoc Plugin is a plugin that uses the javadoc tool for generating javadocs for the specified project.

There is a newer version: 3.11.1
Show newest version
package org.apache.maven.plugins.javadoc;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.PoolingClientConnectionManager;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.project.MavenProject;
import org.apache.maven.settings.Proxy;
import org.apache.maven.settings.Settings;
import org.apache.maven.shared.invoker.DefaultInvocationRequest;
import org.apache.maven.shared.invoker.DefaultInvoker;
import org.apache.maven.shared.invoker.InvocationOutputHandler;
import org.apache.maven.shared.invoker.InvocationRequest;
import org.apache.maven.shared.invoker.InvocationResult;
import org.apache.maven.shared.invoker.Invoker;
import org.apache.maven.shared.invoker.MavenInvocationException;
import org.apache.maven.shared.invoker.PrintStreamHandler;
import org.apache.maven.wagon.proxy.ProxyInfo;
import org.apache.maven.wagon.proxy.ProxyUtils;
import org.codehaus.plexus.languages.java.version.JavaVersion;
import org.codehaus.plexus.util.DirectoryScanner;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.Os;
import org.codehaus.plexus.util.StringUtils;
import org.codehaus.plexus.util.cli.CommandLineException;
import org.codehaus.plexus.util.cli.CommandLineUtils;
import org.codehaus.plexus.util.cli.Commandline;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Modifier;
import java.net.SocketTimeoutException;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.NoSuchElementException;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

/**
 * Set of utilities methods for Javadoc.
 *
 * @author Vincent Siveton
 * @version $Id: JavadocUtil.java 1801354 2017-07-09 08:49:46Z rfscholte $
 * @since 2.4
 */
public class JavadocUtil
{
    /** The default timeout used when fetching url, i.e. 2000. */
    public static final int DEFAULT_TIMEOUT = 2000;

    /** Error message when VM could not be started using invoker. */
    protected static final String ERROR_INIT_VM =
        "Error occurred during initialization of VM, try to reduce the Java heap size for the MAVEN_OPTS "
            + "environnement variable using -Xms: and -Xmx:.";

    /**
     * Method that removes the invalid directories in the specified directories. Note: All elements in
     * dirs could be an absolute or relative against the project's base directory String path.
     *
     * @param project the current Maven project not null
     * @param dirs the collection of String directories path that will be validated.
     * @return a List of valid String directories absolute paths.
     */
    public static Collection pruneDirs( MavenProject project, Collection dirs )
    {
        Set pruned = new LinkedHashSet<>( dirs.size() );
        for ( String dir : dirs )
        {
            if ( dir == null )
            {
                continue;
            }

            File directory = new File( dir );
            if ( !directory.isAbsolute() )
            {
                directory = new File( project.getBasedir(), directory.getPath() );
            }

            if ( directory.isDirectory() )
            {
                pruned.add( directory.getAbsolutePath() );
            }
        }

        return pruned;
    }

    /**
     * Method that removes the invalid files in the specified files. Note: All elements in files
     * should be an absolute String path.
     *
     * @param files the list of String files paths that will be validated.
     * @return a List of valid File objects.
     */
    protected static List pruneFiles( Collection files )
    {
        List pruned = new ArrayList<>( files.size() );
        for ( String f : files )
        {
            if ( !shouldPruneFile( f, pruned ) )
            {
                pruned.add( f );
            }
        }

        return pruned;
    }

    /**
     * Determine whether a file should be excluded from the provided list of paths, based on whether it exists and is
     * already present in the list.
     * 
     * @param f The files.
     * @param pruned The list of pruned files..
     * @return true if the file could be pruned false otherwise.
     */
    public static boolean shouldPruneFile( String f, List pruned )
    {
        if ( f != null )
        {
            File file = new File( f );
            if ( file.isFile() && ( isEmpty( pruned ) || !pruned.contains( f ) ) )
            {
                return false;
            }
        }

        return true;
    }

    /**
     * Method that gets all the source files to be excluded from the javadoc on the given source paths.
     *
     * @param sourcePaths the path to the source files
     * @param subpackagesList list of subpackages to be included in the javadoc
     * @param excludedPackages the package names to be excluded in the javadoc
     * @return a List of the source files to be excluded in the generated javadoc
     */
    protected static List getExcludedNames( Collection sourcePaths, String[] subpackagesList,
                                                    String[] excludedPackages )
    {
        List excludedNames = new ArrayList<>();
        for ( String path : sourcePaths )
        {
            for ( String aSubpackagesList : subpackagesList )
            {
                List excludes = getExcludedPackages( path, excludedPackages );
                excludedNames.addAll( excludes );
            }
        }

        return excludedNames;
    }

    /**
     * Convenience method to wrap an argument value in single quotes (i.e. '). Intended for values which
     * may contain whitespaces. 
* To prevent javadoc error, the line separator (i.e. \n) are skipped. * * @param value the argument value. * @return argument with quote */ protected static String quotedArgument( String value ) { String arg = value; if ( StringUtils.isNotEmpty( arg ) ) { if ( arg.contains( "'" ) ) { arg = StringUtils.replace( arg, "'", "\\'" ); } arg = "'" + arg + "'"; // To prevent javadoc error arg = StringUtils.replace( arg, "\n", " " ); } return arg; } /** * Convenience method to format a path argument so that it is properly interpreted by the javadoc tool. Intended for * path values which may contain whitespaces. * * @param value the argument value. * @return path argument with quote */ protected static String quotedPathArgument( String value ) { String path = value; if ( StringUtils.isNotEmpty( path ) ) { path = path.replace( '\\', '/' ); if ( path.contains( "\'" ) ) { String split[] = path.split( "\'" ); path = ""; for ( int i = 0; i < split.length; i++ ) { if ( i != split.length - 1 ) { path = path + split[i] + "\\'"; } else { path = path + split[i]; } } } path = "'" + path + "'"; } return path; } /** * Convenience method that copy all doc-files directories from javadocDir to the * outputDirectory. * * @param outputDirectory the output directory * @param javadocDir the javadoc directory * @param excludedocfilessubdir the excludedocfilessubdir parameter * @throws IOException if any * @since 2.5 */ protected static void copyJavadocResources( File outputDirectory, File javadocDir, String excludedocfilessubdir ) throws IOException { if ( !javadocDir.isDirectory() ) { return; } List excludes = new ArrayList<>(); excludes.addAll( Arrays.asList( FileUtils.getDefaultExcludes() ) ); if ( StringUtils.isNotEmpty( excludedocfilessubdir ) ) { StringTokenizer st = new StringTokenizer( excludedocfilessubdir, ":" ); String current; while ( st.hasMoreTokens() ) { current = st.nextToken(); excludes.add( "**/" + current + "/**" ); } } List docFiles = FileUtils.getDirectoryNames( javadocDir, "resources,**/doc-files", StringUtils.join( excludes.iterator(), "," ), false, true ); for ( String docFile : docFiles ) { File docFileOutput = new File( outputDirectory, docFile ); FileUtils.mkdir( docFileOutput.getAbsolutePath() ); FileUtils.copyDirectoryStructure( new File( javadocDir, docFile ), docFileOutput ); List files = FileUtils.getFileAndDirectoryNames( docFileOutput, StringUtils.join( excludes.iterator(), "," ), null, true, true, true, true ); for ( String filename : files ) { File file = new File( filename ); if ( file.isDirectory() ) { FileUtils.deleteDirectory( file ); } else { file.delete(); } } } } /** * Method that gets the files or classes that would be included in the javadocs using the subpackages parameter. * * @param sourceDirectory the directory where the source files are located * @param fileList the list of all files found in the sourceDirectory * @param excludePackages package names to be excluded in the javadoc * @return a StringBuilder that contains the appended file names of the files to be included in the javadoc */ protected static List getIncludedFiles( File sourceDirectory, String[] fileList, String[] excludePackages ) { List files = new ArrayList<>(); for ( String aFileList : fileList ) { boolean include = true; for ( int k = 0; k < excludePackages.length && include; k++ ) { // handle wildcards (*) in the excludePackageNames String[] excludeName = excludePackages[k].split( "[*]" ); if ( excludeName.length == 0 ) { continue; } if ( excludeName.length > 1 ) { int u = 0; while ( include && u < excludeName.length ) { if ( !"".equals( excludeName[u].trim() ) && aFileList.contains( excludeName[u] ) ) { include = false; } u++; } } else { if ( aFileList.startsWith( sourceDirectory.toString() + File.separatorChar + excludeName[0] ) ) { if ( excludeName[0].endsWith( String.valueOf( File.separatorChar ) ) ) { int i = aFileList.lastIndexOf( File.separatorChar ); String packageName = aFileList.substring( 0, i + 1 ); File currentPackage = new File( packageName ); File excludedPackage = new File( sourceDirectory, excludeName[0] ); if ( currentPackage.equals( excludedPackage ) && aFileList.substring( i ).contains( ".java" ) ) { include = true; } else { include = false; } } else { include = false; } } } } if ( include ) { files.add( quotedPathArgument( aFileList ) ); } } return files; } /** * Method that gets the complete package names (including subpackages) of the packages that were defined in the * excludePackageNames parameter. * * @param sourceDirectory the directory where the source files are located * @param excludePackagenames package names to be excluded in the javadoc * @return a List of the packagenames to be excluded */ protected static List getExcludedPackages( String sourceDirectory, String[] excludePackagenames ) { List files = new ArrayList<>(); for ( String excludePackagename : excludePackagenames ) { String[] fileList = FileUtils.getFilesFromExtension( sourceDirectory, new String[] { "java" } ); for ( String aFileList : fileList ) { String[] excludeName = excludePackagename.split( "[*]" ); int u = 0; while ( u < excludeName.length ) { if ( !"".equals( excludeName[u].trim() ) && aFileList.contains( excludeName[u] ) && !sourceDirectory.contains( excludeName[u] ) ) { files.add( aFileList ); } u++; } } } List excluded = new ArrayList<>(); for ( String file : files ) { int idx = file.lastIndexOf( File.separatorChar ); String tmpStr = file.substring( 0, idx ); tmpStr = tmpStr.replace( '\\', '/' ); String[] srcSplit = tmpStr.split( Pattern.quote( sourceDirectory.replace( '\\', '/' ) + '/' ) ); String excludedPackage = srcSplit[1].replace( '/', '.' ); if ( !excluded.contains( excludedPackage ) ) { excluded.add( excludedPackage ); } } return excluded; } /** * Convenience method that gets the files to be included in the javadoc. * * @param sourceDirectory the directory where the source files are located * @param files the variable that contains the appended filenames of the files to be included in the javadoc * @param excludePackages the packages to be excluded in the javadocs * @param sourceFileIncludes files to include. * @param sourceFileExcludes files to exclude. */ protected static void addFilesFromSource( List files, File sourceDirectory, List sourceFileIncludes, List sourceFileExcludes, String[] excludePackages ) { DirectoryScanner ds = new DirectoryScanner(); if ( sourceFileIncludes == null ) { sourceFileIncludes = Collections.singletonList( "**/*.java" ); } ds.setIncludes( sourceFileIncludes.toArray( new String[sourceFileIncludes.size()] ) ); if ( sourceFileExcludes != null && sourceFileExcludes.size() > 0 ) { ds.setExcludes( sourceFileExcludes.toArray( new String[sourceFileExcludes.size()] ) ); } ds.setBasedir( sourceDirectory ); ds.scan(); String[] fileList = ds.getIncludedFiles(); String[] pathList = new String[fileList.length]; for ( int x = 0; x < fileList.length; x++ ) { pathList[x] = new File( sourceDirectory, fileList[x] ).getAbsolutePath(); } if ( pathList.length != 0 ) { List tmpFiles = getIncludedFiles( sourceDirectory, pathList, excludePackages ); files.addAll( tmpFiles ); } } /** * Call the Javadoc tool and parse its output to find its version, i.e.: * *
     * javadoc.exe( or.sh ) - J - version
     * 
* * @param javadocExe not null file * @return the javadoc version as float * @throws IOException if javadocExe is null, doesn't exist or is not a file * @throws CommandLineException if any * @throws IllegalArgumentException if no output was found in the command line * @throws PatternSyntaxException if the output contains a syntax error in the regular-expression pattern. * @see #extractJavadocVersion(String) */ protected static JavaVersion getJavadocVersion( File javadocExe ) throws IOException, CommandLineException, IllegalArgumentException { if ( ( javadocExe == null ) || ( !javadocExe.exists() ) || ( !javadocExe.isFile() ) ) { throw new IOException( "The javadoc executable '" + javadocExe + "' doesn't exist or is not a file. " ); } Commandline cmd = new Commandline(); cmd.setExecutable( javadocExe.getAbsolutePath() ); cmd.setWorkingDirectory( javadocExe.getParentFile() ); cmd.createArg().setValue( "-J-version" ); CommandLineUtils.StringStreamConsumer out = new JavadocOutputStreamConsumer(); CommandLineUtils.StringStreamConsumer err = new JavadocOutputStreamConsumer(); int exitCode = CommandLineUtils.executeCommandLine( cmd, out, err ); if ( exitCode != 0 ) { StringBuilder msg = new StringBuilder( "Exit code: " + exitCode + " - " + err.getOutput() ); msg.append( '\n' ); msg.append( "Command line was:" + CommandLineUtils.toString( cmd.getCommandline() ) ); throw new CommandLineException( msg.toString() ); } if ( StringUtils.isNotEmpty( err.getOutput() ) ) { return JavaVersion.parse( extractJavadocVersion( err.getOutput() ) ); } else if ( StringUtils.isNotEmpty( out.getOutput() ) ) { return JavaVersion.parse( extractJavadocVersion( out.getOutput() ) ); } throw new IllegalArgumentException( "No output found from the command line 'javadoc -J-version'" ); } /** * Parse the output for 'javadoc -J-version' and return the javadoc version recognized.
* Here are some output for 'javadoc -J-version' depending the JDK used: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
JDKOutput for 'javadoc -J-version'
Sun 1.4java full version "1.4.2_12-b03"
Sun 1.5java full version "1.5.0_07-164"
IBM 1.4javadoc full version "J2RE 1.4.2 IBM Windows 32 build cn1420-20040626"
IBM 1.5 (French JVM)javadoc version complète de "J2RE 1.5.0 IBM Windows 32 build pwi32pdev-20070426a"
FreeBSD 1.5java full version "diablo-1.5.0-b01"
BEA jrockit 1.5java full version "1.5.0_11-b03"
* * @param output for 'javadoc -J-version' * @return the version of the javadoc for the output, only digits and dots * @throws PatternSyntaxException if the output doesn't match with the output pattern * (?s).*?[^a-zA-Z]([0-9]+\\.?[0-9]*)(\\.([0-9]+))?.*. * @throws IllegalArgumentException if the output is null */ protected static String extractJavadocVersion( String output ) throws IllegalArgumentException { if ( StringUtils.isEmpty( output ) ) { throw new IllegalArgumentException( "The output could not be null." ); } Pattern pattern = Pattern.compile( "(?s).*?[^a-zA-Z](([0-9]+\\.?[0-9]*)(\\.[0-9]+)?).*" ); Matcher matcher = pattern.matcher( output ); if ( !matcher.matches() ) { throw new PatternSyntaxException( "Unrecognized version of Javadoc: '" + output + "'", pattern.pattern(), pattern.toString().length() - 1 ); } return matcher.group( 1 ); } /** * Parse a memory string which be used in the JVM arguments -Xms or -Xmx.
* Here are some supported memory string depending the JDK used: * * * * * * * * * * * * * * * * * *
JDKMemory argument support for -Xms or -Xmx
SUN1024k | 128m | 1g | 1t
IBM1024k | 1024b | 128m | 128mb | 1g | 1gb
BEA1024k | 1024kb | 128m | 128mb | 1g | 1gb
* * @param memory the memory to be parsed, not null. * @return the memory parsed with a supported unit. If no unit specified in the memory parameter, the * default unit is m. The units g | gb or t | tb will be converted in * m. * @throws IllegalArgumentException if the memory parameter is null or doesn't match any pattern. */ protected static String parseJavadocMemory( String memory ) throws IllegalArgumentException { if ( StringUtils.isEmpty( memory ) ) { throw new IllegalArgumentException( "The memory could not be null." ); } Pattern p = Pattern.compile( "^\\s*(\\d+)\\s*?\\s*$" ); Matcher m = p.matcher( memory ); if ( m.matches() ) { return m.group( 1 ) + "m"; } p = Pattern.compile( "^\\s*(\\d+)\\s*k(b)?\\s*$", Pattern.CASE_INSENSITIVE ); m = p.matcher( memory ); if ( m.matches() ) { return m.group( 1 ) + "k"; } p = Pattern.compile( "^\\s*(\\d+)\\s*m(b)?\\s*$", Pattern.CASE_INSENSITIVE ); m = p.matcher( memory ); if ( m.matches() ) { return m.group( 1 ) + "m"; } p = Pattern.compile( "^\\s*(\\d+)\\s*g(b)?\\s*$", Pattern.CASE_INSENSITIVE ); m = p.matcher( memory ); if ( m.matches() ) { return ( Integer.parseInt( m.group( 1 ) ) * 1024 ) + "m"; } p = Pattern.compile( "^\\s*(\\d+)\\s*t(b)?\\s*$", Pattern.CASE_INSENSITIVE ); m = p.matcher( memory ); if ( m.matches() ) { return ( Integer.parseInt( m.group( 1 ) ) * 1024 * 1024 ) + "m"; } throw new IllegalArgumentException( "Could convert not to a memory size: " + memory ); } /** * Validate if a charset is supported on this platform. * * @param charsetName the charsetName to be check. * @return true if the given charset is supported by the JVM, false otherwise. */ protected static boolean validateEncoding( String charsetName ) { if ( StringUtils.isEmpty( charsetName ) ) { return false; } OutputStream ost = new ByteArrayOutputStream(); OutputStreamWriter osw = null; try { osw = new OutputStreamWriter( ost, charsetName ); osw.close(); osw = null; } catch ( IOException exc ) { return false; } finally { IOUtil.close( osw ); } return true; } /** * For security reasons, if an active proxy is defined and needs an authentication by username/password, hide the * proxy password in the command line. * * @param cmdLine a command line, not null * @param settings the user settings * @return the cmdline with '*' for the http.proxyPassword JVM property */ protected static String hideProxyPassword( String cmdLine, Settings settings ) { if ( cmdLine == null ) { throw new IllegalArgumentException( "cmdLine could not be null" ); } if ( settings == null ) { return cmdLine; } Proxy activeProxy = settings.getActiveProxy(); if ( activeProxy != null && StringUtils.isNotEmpty( activeProxy.getHost() ) && StringUtils.isNotEmpty( activeProxy.getUsername() ) && StringUtils.isNotEmpty( activeProxy.getPassword() ) ) { String pass = "-J-Dhttp.proxyPassword=\"" + activeProxy.getPassword() + "\""; String hidepass = "-J-Dhttp.proxyPassword=\"" + StringUtils.repeat( "*", activeProxy.getPassword().length() ) + "\""; return StringUtils.replace( cmdLine, pass, hidepass ); } return cmdLine; } /** * Auto-detect the class names of the implementation of com.sun.tools.doclets.Taglet class from a given * jar file.
* Note: JAVA_HOME/lib/tools.jar is a requirement to find * com.sun.tools.doclets.Taglet class. * * @param jarFile not null * @return the list of com.sun.tools.doclets.Taglet class names from a given jarFile. * @throws IOException if jarFile is invalid or not found, or if the JAVA_HOME/lib/tools.jar is not * found. * @throws ClassNotFoundException if any * @throws NoClassDefFoundError if any */ protected static List getTagletClassNames( File jarFile ) throws IOException, ClassNotFoundException, NoClassDefFoundError { List classes = getClassNamesFromJar( jarFile ); ClassLoader cl; // Needed to find com.sun.tools.doclets.Taglet class File tools = new File( System.getProperty( "java.home" ), "../lib/tools.jar" ); if ( tools.exists() && tools.isFile() ) { cl = new URLClassLoader( new URL[] { jarFile.toURI().toURL(), tools.toURI().toURL() }, null ); } else { cl = new URLClassLoader( new URL[] { jarFile.toURI().toURL() }, ClassLoader.getSystemClassLoader() ); } List tagletClasses = new ArrayList<>(); Class tagletClass; try { tagletClass = cl.loadClass( "com.sun.tools.doclets.Taglet" ); } catch ( ClassNotFoundException e ) { tagletClass = cl.loadClass( "jdk.javadoc.doclet.Taglet" ); } for ( String s : classes ) { Class c = cl.loadClass( s ); if ( tagletClass.isAssignableFrom( c ) && !Modifier.isAbstract( c.getModifiers() ) ) { tagletClasses.add( c.getName() ); } } return tagletClasses; } /** * Copy the given url to the given file. * * @param url not null url * @param file not null file where the url will be created * @throws IOException if any * @since 2.6 */ protected static void copyResource( URL url, File file ) throws IOException { if ( file == null ) { throw new IOException( "The file can't be null." ); } if ( url == null ) { throw new IOException( "The url could not be null." ); } if ( !file.getParentFile().exists() ) { file.getParentFile().mkdirs(); } InputStream in = null; OutputStream out = null; try { in = url.openStream(); if ( in == null ) { throw new IOException( "The resource " + url + " doesn't exists." ); } out = new FileOutputStream( file ); IOUtil.copy( in, out ); out.close(); out = null; in.close(); in = null; } finally { IOUtil.close( in ); IOUtil.close( out ); } } /** * Invoke Maven for the given project file with a list of goals and properties, the output will be in the invokerlog * file.
* Note: the Maven Home should be defined in the maven.home Java system property or defined in * M2_HOME system env variables. * * @param log a logger could be null. * @param localRepositoryDir the localRepository not null. * @param projectFile a not null project file. * @param goals a not null goals list. * @param properties the properties for the goals, could be null. * @param invokerLog the log file where the invoker will be written, if null using System.out. * @throws MavenInvocationException if any * @since 2.6 */ protected static void invokeMaven( Log log, File localRepositoryDir, File projectFile, List goals, Properties properties, File invokerLog ) throws MavenInvocationException { if ( projectFile == null ) { throw new IllegalArgumentException( "projectFile should be not null." ); } if ( !projectFile.isFile() ) { throw new IllegalArgumentException( projectFile.getAbsolutePath() + " is not a file." ); } if ( goals == null || goals.size() == 0 ) { throw new IllegalArgumentException( "goals should be not empty." ); } if ( localRepositoryDir == null || !localRepositoryDir.isDirectory() ) { throw new IllegalArgumentException( "localRepositoryDir '" + localRepositoryDir + "' should be a directory." ); } String mavenHome = getMavenHome( log ); if ( StringUtils.isEmpty( mavenHome ) ) { String msg = "Could NOT invoke Maven because no Maven Home is defined. You need to have set the M2_HOME " + "system env variable or a maven.home Java system properties."; if ( log != null ) { log.error( msg ); } else { System.err.println( msg ); } return; } Invoker invoker = new DefaultInvoker(); invoker.setMavenHome( new File( mavenHome ) ); invoker.setLocalRepositoryDirectory( localRepositoryDir ); InvocationRequest request = new DefaultInvocationRequest(); request.setBaseDirectory( projectFile.getParentFile() ); request.setPomFile( projectFile ); if ( log != null ) { request.setDebug( log.isDebugEnabled() ); } else { request.setDebug( true ); } request.setGoals( goals ); if ( properties != null ) { request.setProperties( properties ); } File javaHome = getJavaHome( log ); if ( javaHome != null ) { request.setJavaHome( javaHome ); } if ( log != null && log.isDebugEnabled() ) { log.debug( "Invoking Maven for the goals: " + goals + " with " + ( properties == null ? "no properties" : "properties=" + properties ) ); } InvocationResult result = invoke( log, invoker, request, invokerLog, goals, properties, null ); if ( result.getExitCode() != 0 ) { String invokerLogContent = readFile( invokerLog, "UTF-8" ); // see DefaultMaven if ( invokerLogContent != null && ( !invokerLogContent.contains( "Scanning for projects..." ) || invokerLogContent.contains( OutOfMemoryError.class.getName() ) ) ) { if ( log != null ) { log.error( "Error occurred during initialization of VM, trying to use an empty MAVEN_OPTS..." ); if ( log.isDebugEnabled() ) { log.debug( "Reinvoking Maven for the goals: " + goals + " with an empty MAVEN_OPTS..." ); } } result = invoke( log, invoker, request, invokerLog, goals, properties, "" ); } } if ( result.getExitCode() != 0 ) { String invokerLogContent = readFile( invokerLog, "UTF-8" ); // see DefaultMaven if ( invokerLogContent != null && ( !invokerLogContent.contains( "Scanning for projects..." ) || invokerLogContent.contains( OutOfMemoryError.class.getName() ) ) ) { throw new MavenInvocationException( ERROR_INIT_VM ); } throw new MavenInvocationException( "Error when invoking Maven, consult the invoker log file: " + invokerLog.getAbsolutePath() ); } } /** * Read the given file and return the content or null if an IOException occurs. * * @param javaFile not null * @param encoding could be null * @return the content with unified line separator of the given javaFile using the given encoding. * @see FileUtils#fileRead(File, String) * @since 2.6.1 */ protected static String readFile( final File javaFile, final String encoding ) { try { return FileUtils.fileRead( javaFile, encoding ); } catch ( IOException e ) { return null; } } /** * Split the given path with colon and semi-colon, to support Solaris and Windows path. Examples: * *
     * splitPath( "/home:/tmp" )     = ["/home", "/tmp"]
     * splitPath( "/home;/tmp" )     = ["/home", "/tmp"]
     * splitPath( "C:/home:C:/tmp" ) = ["C:/home", "C:/tmp"]
     * splitPath( "C:/home;C:/tmp" ) = ["C:/home", "C:/tmp"]
     * 
* * @param path which can contain multiple paths separated with a colon (:) or a semi-colon * (;), platform independent. Could be null. * @return the path splitted by colon or semi-colon or null if path was null. * @since 2.6.1 */ protected static String[] splitPath( final String path ) { if ( path == null ) { return null; } List subpaths = new ArrayList<>(); PathTokenizer pathTokenizer = new PathTokenizer( path ); while ( pathTokenizer.hasMoreTokens() ) { subpaths.add( pathTokenizer.nextToken() ); } return subpaths.toArray( new String[subpaths.size()] ); } /** * Unify the given path with the current System path separator, to be platform independent. Examples: * *
     * unifyPathSeparator( "/home:/tmp" ) = "/home:/tmp" (Solaris box)
     * unifyPathSeparator( "/home:/tmp" ) = "/home;/tmp" (Windows box)
     * 
* * @param path which can contain multiple paths by separating them with a colon (:) or a semi-colon * (;), platform independent. Could be null. * @return the same path but separated with the current System path separator or null if path was * null. * @since 2.6.1 * @see #splitPath(String) * @see File#pathSeparator */ protected static String unifyPathSeparator( final String path ) { if ( path == null ) { return null; } return StringUtils.join( splitPath( path ), File.pathSeparator ); } // ---------------------------------------------------------------------- // private methods // ---------------------------------------------------------------------- /** * @param jarFile not null * @return all class names from the given jar file. * @throws IOException if any or if the jarFile is null or doesn't exist. */ private static List getClassNamesFromJar( File jarFile ) throws IOException { if ( jarFile == null || !jarFile.exists() || !jarFile.isFile() ) { throw new IOException( "The jar '" + jarFile + "' doesn't exist or is not a file." ); } List classes = new ArrayList<>(); JarInputStream jarStream = null; try { jarStream = new JarInputStream( new FileInputStream( jarFile ) ); for ( JarEntry jarEntry = jarStream.getNextJarEntry(); jarEntry != null; jarEntry = jarStream.getNextJarEntry() ) { if ( jarEntry.getName().toLowerCase( Locale.ENGLISH ).endsWith( ".class" ) ) { String name = jarEntry.getName().substring( 0, jarEntry.getName().indexOf( "." ) ); classes.add( name.replaceAll( "/", "\\." ) ); } jarStream.closeEntry(); } jarStream.close(); jarStream = null; } finally { IOUtil.close( jarStream ); } return classes; } /** * @param log could be null * @param invoker not null * @param request not null * @param invokerLog not null * @param goals not null * @param properties could be null * @param mavenOpts could be null * @return the invocation result * @throws MavenInvocationException if any * @since 2.6 */ private static InvocationResult invoke( Log log, Invoker invoker, InvocationRequest request, File invokerLog, List goals, Properties properties, String mavenOpts ) throws MavenInvocationException { PrintStream ps; OutputStream os = null; if ( invokerLog != null ) { if ( log != null && log.isDebugEnabled() ) { log.debug( "Using " + invokerLog.getAbsolutePath() + " to log the invoker" ); } try { if ( !invokerLog.exists() ) { // noinspection ResultOfMethodCallIgnored invokerLog.getParentFile().mkdirs(); } os = new FileOutputStream( invokerLog ); ps = new PrintStream( os, true, "UTF-8" ); } catch ( FileNotFoundException e ) { if ( log != null && log.isErrorEnabled() ) { log.error( "FileNotFoundException: " + e.getMessage() + ". Using System.out to log the invoker." ); } ps = System.out; } catch ( UnsupportedEncodingException e ) { if ( log != null && log.isErrorEnabled() ) { log.error( "UnsupportedEncodingException: " + e.getMessage() + ". Using System.out to log the invoker." ); } ps = System.out; } } else { if ( log != null && log.isDebugEnabled() ) { log.debug( "Using System.out to log the invoker." ); } ps = System.out; } if ( mavenOpts != null ) { request.setMavenOpts( mavenOpts ); } InvocationOutputHandler outputHandler = new PrintStreamHandler( ps, false ); request.setOutputHandler( outputHandler ); outputHandler.consumeLine( "Invoking Maven for the goals: " + goals + " with " + ( properties == null ? "no properties" : "properties=" + properties ) ); outputHandler.consumeLine( "" ); outputHandler.consumeLine( "M2_HOME=" + getMavenHome( log ) ); outputHandler.consumeLine( "MAVEN_OPTS=" + getMavenOpts( log ) ); outputHandler.consumeLine( "JAVA_HOME=" + getJavaHome( log ) ); outputHandler.consumeLine( "JAVA_OPTS=" + getJavaOpts( log ) ); outputHandler.consumeLine( "" ); try { return invoker.execute( request ); } finally { IOUtil.close( os ); } } /** * @param log a logger could be null * @return the Maven home defined in the maven.home system property or defined in M2_HOME * system env variables or null if never set. * @since 2.6 */ private static String getMavenHome( Log log ) { String mavenHome = System.getProperty( "maven.home" ); if ( mavenHome == null ) { try { mavenHome = CommandLineUtils.getSystemEnvVars().getProperty( "M2_HOME" ); } catch ( IOException e ) { if ( log != null && log.isDebugEnabled() ) { log.debug( "IOException: " + e.getMessage() ); } } } File m2Home = new File( mavenHome ); if ( !m2Home.exists() ) { if ( log != null && log.isErrorEnabled() ) { log.error( "Cannot find Maven application directory. Either specify \'maven.home\' system property, or " + "M2_HOME environment variable." ); } } return mavenHome; } /** * @param log a logger could be null * @return the MAVEN_OPTS env variable value * @since 2.6 */ private static String getMavenOpts( Log log ) { String mavenOpts = null; try { mavenOpts = CommandLineUtils.getSystemEnvVars().getProperty( "MAVEN_OPTS" ); } catch ( IOException e ) { if ( log != null && log.isDebugEnabled() ) { log.debug( "IOException: " + e.getMessage() ); } } return mavenOpts; } /** * @param log a logger could be null * @return the JAVA_HOME from System.getProperty( "java.home" ) By default, * System.getProperty( "java.home" ) = JRE_HOME and JRE_HOME should be in the * JDK_HOME * @since 2.6 */ private static File getJavaHome( Log log ) { File javaHome = null; String javaHomeValue = null; try { javaHomeValue = CommandLineUtils.getSystemEnvVars().getProperty( "JAVA_HOME" ); } catch ( IOException e ) { if ( log != null && log.isDebugEnabled() ) { log.debug( "IOException: " + e.getMessage() ); } } // if maven.home is set, we can assume JAVA_HOME must be used for testing if ( System.getProperty( "maven.home" ) == null || javaHomeValue == null ) { // JEP220 (Java9) restructured the JRE/JDK runtime image if ( SystemUtils.IS_OS_MAC_OSX || JavaVersion.JAVA_VERSION.isAtLeast( "9" ) ) { javaHome = SystemUtils.getJavaHome(); } else { javaHome = new File( SystemUtils.getJavaHome(), ".." ); } } if ( javaHome == null || !javaHome.exists() ) { javaHome = new File( javaHomeValue ); } if ( javaHome == null || !javaHome.exists() ) { if ( log != null && log.isErrorEnabled() ) { log.error( "Cannot find Java application directory. Either specify \'java.home\' system property, or " + "JAVA_HOME environment variable." ); } } return javaHome; } /** * @param log a logger could be null * @return the JAVA_OPTS env variable value * @since 2.6 */ private static String getJavaOpts( Log log ) { String javaOpts = null; try { javaOpts = CommandLineUtils.getSystemEnvVars().getProperty( "JAVA_OPTS" ); } catch ( IOException e ) { if ( log != null && log.isDebugEnabled() ) { log.debug( "IOException: " + e.getMessage() ); } } return javaOpts; } /** * A Path tokenizer takes a path and returns the components that make up that path. The path can use path separators * of either ':' or ';' and file separators of either '/' or '\'. * * @version revision 439418 taken on 2009-09-12 from Ant Project (see * http://svn.apache.org/repos/asf/ant/core/trunk/src/main/org/apache/tools/ant/PathTokenizer.java) */ private static class PathTokenizer { /** * A tokenizer to break the string up based on the ':' or ';' separators. */ private StringTokenizer tokenizer; /** * A String which stores any path components which have been read ahead due to DOS filesystem compensation. */ private String lookahead = null; /** * A boolean that determines if we are running on Novell NetWare, which exhibits slightly different path name * characteristics (multi-character volume / drive names) */ private boolean onNetWare = Os.isFamily( "netware" ); /** * Flag to indicate whether or not we are running on a platform with a DOS style filesystem */ private boolean dosStyleFilesystem; /** * Constructs a path tokenizer for the specified path. * * @param path The path to tokenize. Must not be null. */ PathTokenizer( String path ) { if ( onNetWare ) { // For NetWare, use the boolean=true mode, so we can use delimiter // information to make a better decision later. tokenizer = new StringTokenizer( path, ":;", true ); } else { // on Windows and Unix, we can ignore delimiters and still have // enough information to tokenize correctly. tokenizer = new StringTokenizer( path, ":;", false ); } dosStyleFilesystem = File.pathSeparatorChar == ';'; } /** * Tests if there are more path elements available from this tokenizer's path. If this method returns * true, then a subsequent call to nextToken will successfully return a token. * * @return true if and only if there is at least one token in the string after the current * position; false otherwise. */ public boolean hasMoreTokens() { return lookahead != null || tokenizer.hasMoreTokens(); } /** * Returns the next path element from this tokenizer. * * @return the next path element from this tokenizer. * @exception NoSuchElementException if there are no more elements in this tokenizer's path. */ public String nextToken() throws NoSuchElementException { String token; if ( lookahead != null ) { token = lookahead; lookahead = null; } else { token = tokenizer.nextToken().trim(); } if ( !onNetWare ) { if ( token.length() == 1 && Character.isLetter( token.charAt( 0 ) ) && dosStyleFilesystem && tokenizer.hasMoreTokens() ) { // we are on a dos style system so this path could be a drive // spec. We look at the next token String nextToken = tokenizer.nextToken().trim(); if ( nextToken.startsWith( "\\" ) || nextToken.startsWith( "/" ) ) { // we know we are on a DOS style platform and the next path // starts with a slash or backslash, so we know this is a // drive spec token += ":" + nextToken; } else { // store the token just read for next time lookahead = nextToken; } } } else { // we are on NetWare, tokenizing is handled a little differently, // due to the fact that NetWare has multiple-character volume names. if ( token.equals( File.pathSeparator ) || token.equals( ":" ) ) { // ignore ";" and get the next token token = tokenizer.nextToken().trim(); } if ( tokenizer.hasMoreTokens() ) { // this path could be a drive spec, so look at the next token String nextToken = tokenizer.nextToken().trim(); // make sure we aren't going to get the path separator next if ( !nextToken.equals( File.pathSeparator ) ) { if ( nextToken.equals( ":" ) ) { if ( !token.startsWith( "/" ) && !token.startsWith( "\\" ) && !token.startsWith( "." ) && !token.startsWith( ".." ) ) { // it indeed is a drive spec, get the next bit String oneMore = tokenizer.nextToken().trim(); if ( !oneMore.equals( File.pathSeparator ) ) { token += ":" + oneMore; } else { token += ":"; lookahead = oneMore; } } // implicit else: ignore the ':' since we have either a // UNIX or a relative path } else { // store the token just read for next time lookahead = nextToken; } } } } return token; } } /** * Ignores line like 'Picked up JAVA_TOOL_OPTIONS: ...' as can happen on CI servers. * * @author Robert Scholte * @since 3.0.1 */ private static class JavadocOutputStreamConsumer extends CommandLineUtils.StringStreamConsumer { @Override public void consumeLine( String line ) { if ( !line.startsWith( "Picked up " ) ) { super.consumeLine( line ); } } } static List toList( String src ) { return toList( src, null, null ); } static List toList( String src, String elementPrefix, String elementSuffix ) { if ( StringUtils.isEmpty( src ) ) { return null; } List result = new ArrayList<>(); StringTokenizer st = new StringTokenizer( src, "[,:;]" ); StringBuilder sb = new StringBuilder( 256 ); while ( st.hasMoreTokens() ) { sb.setLength( 0 ); if ( StringUtils.isNotEmpty( elementPrefix ) ) { sb.append( elementPrefix ); } sb.append( st.nextToken() ); if ( StringUtils.isNotEmpty( elementSuffix ) ) { sb.append( elementSuffix ); } result.add( sb.toString() ); } return result; } static List toList( T[] multiple ) { return toList( null, multiple ); } static List toList( T single, T[] multiple ) { if ( single == null && ( multiple == null || multiple.length < 1 ) ) { return null; } List result = new ArrayList<>(); if ( single != null ) { result.add( single ); } if ( multiple != null && multiple.length > 0 ) { result.addAll( Arrays.asList( multiple ) ); } return result; } // TODO: move to plexus-utils or use something appropriate from there public static String toRelative( File basedir, String absolutePath ) { String relative; absolutePath = absolutePath.replace( '\\', '/' ); String basedirPath = basedir.getAbsolutePath().replace( '\\', '/' ); if ( absolutePath.startsWith( basedirPath ) ) { relative = absolutePath.substring( basedirPath.length() ); if ( relative.startsWith( "/" ) ) { relative = relative.substring( 1 ); } if ( relative.length() <= 0 ) { relative = "."; } } else { relative = absolutePath; } return relative; } /** * Convenience method to determine that a collection is not empty or null. * @param collection the collection to verify * @return {@code true} if not {@code null} and not empty, otherwise {@code false} */ public static boolean isNotEmpty( final Collection collection ) { return collection != null && !collection.isEmpty(); } /** * Convenience method to determine that a collection is empty or null. * @param collection the collection to verify * @return {@code true} if {@code null} or empty, otherwise {@code false} */ public static boolean isEmpty( final Collection collection ) { return collection == null || collection.isEmpty(); } /** * Execute an Http request at the given URL, follows redirects, and returns the last redirect locations. For URLs * that aren't http/https, this does nothing and simply returns the given URL unchanged. * * @param url URL. * @param settings Maven settings. * @return Last redirect location. * @throws IOException if there was an error during the Http request. */ protected static URL getRedirectUrl( URL url, Settings settings ) throws IOException { String protocol = url.getProtocol(); if ( !"http".equals( protocol ) && !"https".equals( protocol ) ) { return url; } HttpClient httpClient = null; try { httpClient = createHttpClient( settings, url ); HttpClientContext httpContext = HttpClientContext.create(); HttpGet httpMethod = new HttpGet( url.toString() ); HttpResponse response = httpClient.execute( httpMethod, httpContext ); int status = response.getStatusLine().getStatusCode(); if ( status != HttpStatus.SC_OK ) { throw new FileNotFoundException( "Unexpected HTTP status code " + status + " getting resource " + url.toExternalForm() + "." ); } List redirects = httpContext.getRedirectLocations(); return redirects.isEmpty() ? url : redirects.get( redirects.size() - 1 ).toURL(); } finally { if ( httpClient != null ) { httpClient.getConnectionManager().shutdown(); } } } /** * Validates an URL to point to a valid package-list resource. * * @param url The URL to validate. * @param settings The user settings used to configure the connection to the URL or {@code null}. * @param validateContent true to validate the content of the package-list resource; * false to only check the existence of the package-list resource. * @return true if url points to a valid package-list resource; * false else. * @throws IOException if reading the resource fails. * @see #createHttpClient(org.apache.maven.settings.Settings, java.net.URL) * @since 2.8 */ protected static boolean isValidPackageList( URL url, Settings settings, boolean validateContent ) throws IOException { if ( url == null ) { throw new IllegalArgumentException( "The url is null" ); } BufferedReader reader = null; HttpGet httpMethod = null; HttpClient httpClient = null; try { if ( "file".equals( url.getProtocol() ) ) { // Intentionally using the platform default encoding here since this is what Javadoc uses internally. reader = new BufferedReader( new InputStreamReader( url.openStream() ) ); } else { // http, https... httpClient = createHttpClient( settings, url ); httpMethod = new HttpGet( url.toString() ); HttpResponse response; try { response = httpClient.execute( httpMethod ); } catch ( SocketTimeoutException e ) { // could be a sporadic failure, one more retry before we give up response = httpClient.execute( httpMethod ); } int status = response.getStatusLine().getStatusCode(); if ( status != HttpStatus.SC_OK ) { throw new FileNotFoundException( "Unexpected HTTP status code " + status + " getting resource " + url.toExternalForm() + "." ); } // Intentionally using the platform default encoding here since this is what Javadoc uses internally. reader = new BufferedReader( new InputStreamReader( response.getEntity().getContent() ) ); } if ( validateContent ) { for ( String line = reader.readLine(); line != null; line = reader.readLine() ) { if ( !isValidPackageName( line ) ) { return false; } } } reader.close(); reader = null; return true; } finally { IOUtil.close( reader ); if ( httpMethod != null ) { httpMethod.releaseConnection(); } if ( httpClient != null ) { httpClient.getConnectionManager().shutdown(); } } } private static boolean isValidPackageName( String str ) { if ( StringUtils.isEmpty( str ) ) { // unnamed package is valid (even if bad practice :) ) return true; } int idx; while ( ( idx = str.indexOf( '.' ) ) != -1 ) { if ( !isValidClassName( str.substring( 0, idx ) ) ) { return false; } str = str.substring( idx + 1 ); } return isValidClassName( str ); } private static boolean isValidClassName( String str ) { if ( StringUtils.isEmpty( str ) || !Character.isJavaIdentifierStart( str.charAt( 0 ) ) ) { return false; } for ( int i = str.length() - 1; i > 0; i-- ) { if ( !Character.isJavaIdentifierPart( str.charAt( i ) ) ) { return false; } } return true; } /** * Creates a new {@code HttpClient} instance. * * @param settings The settings to use for setting up the client or {@code null}. * @param url The {@code URL} to use for setting up the client or {@code null}. * @return A new {@code HttpClient} instance. * @see #DEFAULT_TIMEOUT * @since 2.8 */ private static HttpClient createHttpClient( Settings settings, URL url ) { DefaultHttpClient httpClient = new DefaultHttpClient( new PoolingClientConnectionManager() ); httpClient.getParams().setIntParameter( CoreConnectionPNames.SO_TIMEOUT, DEFAULT_TIMEOUT ); httpClient.getParams().setIntParameter( CoreConnectionPNames.CONNECTION_TIMEOUT, DEFAULT_TIMEOUT ); httpClient.getParams().setBooleanParameter( ClientPNames.ALLOW_CIRCULAR_REDIRECTS, true ); // Some web servers don't allow the default user-agent sent by httpClient httpClient.getParams().setParameter( CoreProtocolPNames.USER_AGENT, "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)" ); if ( settings != null && settings.getActiveProxy() != null ) { Proxy activeProxy = settings.getActiveProxy(); ProxyInfo proxyInfo = new ProxyInfo(); proxyInfo.setNonProxyHosts( activeProxy.getNonProxyHosts() ); if ( StringUtils.isNotEmpty( activeProxy.getHost() ) && ( url == null || !ProxyUtils.validateNonProxyHosts( proxyInfo, url.getHost() ) ) ) { HttpHost proxy = new HttpHost( activeProxy.getHost(), activeProxy.getPort() ); httpClient.getParams().setParameter( ConnRoutePNames.DEFAULT_PROXY, proxy ); if ( StringUtils.isNotEmpty( activeProxy.getUsername() ) && activeProxy.getPassword() != null ) { Credentials credentials = new UsernamePasswordCredentials( activeProxy.getUsername(), activeProxy.getPassword() ); httpClient.getCredentialsProvider().setCredentials( AuthScope.ANY, credentials ); } } } return httpClient; } static boolean equalsIgnoreCase( String value, String... strings ) { for ( String s : strings ) { if ( s.equalsIgnoreCase( value ) ) { return true; } } return false; } static boolean equals( String value, String... strings ) { for ( String s : strings ) { if ( s.equals( value ) ) { return true; } } return false; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy