Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 2BFAA200CD2 for ; Wed, 12 Jul 2017 22:00:04 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id 2AADD169C3C; Wed, 12 Jul 2017 20:00:04 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 03DE1169C32 for ; Wed, 12 Jul 2017 22:00:01 +0200 (CEST) Received: (qmail 29467 invoked by uid 500); 12 Jul 2017 20:00:01 -0000 Mailing-List: contact commits-help@maven.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@maven.apache.org Delivered-To: mailing list commits@maven.apache.org Received: (qmail 28782 invoked by uid 99); 12 Jul 2017 19:59:59 -0000 Received: from Unknown (HELO svn01-us-west.apache.org) (209.188.14.144) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 12 Jul 2017 19:59:59 +0000 Received: from svn01-us-west.apache.org (localhost [127.0.0.1]) by svn01-us-west.apache.org (ASF Mail Server at svn01-us-west.apache.org) with ESMTP id 9F0273A25B0 for ; Wed, 12 Jul 2017 19:59:56 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: svn commit: r1801772 [5/11] - in /maven/plugins/trunk/maven-javadoc-plugin: ./ src/main/java/org/apache/maven/plugin/ src/main/java/org/apache/maven/plugins/ src/main/java/org/apache/maven/plugins/javadoc/ src/main/java/org/apache/maven/plugins/javadoc... Date: Wed, 12 Jul 2017 19:59:53 -0000 To: commits@maven.apache.org From: rfscholte@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20170712195956.9F0273A25B0@svn01-us-west.apache.org> archived-at: Wed, 12 Jul 2017 20:00:04 -0000 Added: maven/plugins/trunk/maven-javadoc-plugin/src/main/java/org/apache/maven/plugins/javadoc/JavadocUtil.java URL: http://svn.apache.org/viewvc/maven/plugins/trunk/maven-javadoc-plugin/src/main/java/org/apache/maven/plugins/javadoc/JavadocUtil.java?rev=1801772&view=auto ============================================================================== --- maven/plugins/trunk/maven-javadoc-plugin/src/main/java/org/apache/maven/plugins/javadoc/JavadocUtil.java (added) +++ maven/plugins/trunk/maven-javadoc-plugin/src/main/java/org/apache/maven/plugins/javadoc/JavadocUtil.java Wed Jul 12 19:59:51 2017 @@ -0,0 +1,1875 @@ +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.commons.lang3.SystemUtils; +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.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.artifact.Artifact; +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.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.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +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 list of String directories path that will be validated. + * @return a List of valid String directories absolute paths. + */ + public static List pruneDirs( MavenProject project, List dirs ) + { + List pruned = new ArrayList( 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.contains( directory.getAbsolutePath() ) ) + { + 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( List 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( List 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; + } + + /** + * Copy from {@link org.apache.maven.project.MavenProject#getCompileArtifacts()} + * @param artifacts not null + * @return list of compile artifacts with compile scope + * @deprecated since 2.5, using {@link #getCompileArtifacts(Set, boolean)} instead of. + */ + protected static List getCompileArtifacts( Set artifacts ) + { + return getCompileArtifacts( artifacts, false ); + } + + /** + * Copy from {@link org.apache.maven.project.MavenProject#getCompileArtifacts()} + * @param artifacts not null + * @param withTestScope flag to include or not the artifacts with test scope + * @return list of compile artifacts with or without test scope. + */ + protected static List getCompileArtifacts( Collection artifacts, boolean withTestScope ) + { + List list = new ArrayList( artifacts.size() ); + + for ( Artifact a : artifacts ) + { + // TODO: classpath check doesn't belong here - that's the other method + if ( a.getArtifactHandler().isAddedToClasspath() ) + { + // TODO: let the scope handler deal with this + if ( withTestScope ) + { + if ( Artifact.SCOPE_COMPILE.equals( a.getScope() ) + || Artifact.SCOPE_PROVIDED.equals( a.getScope() ) + || Artifact.SCOPE_SYSTEM.equals( a.getScope() ) + || Artifact.SCOPE_TEST.equals( a.getScope() ) ) + { + list.add( a ); + } + } + else + { + if ( Artifact.SCOPE_COMPILE.equals( a.getScope() ) || Artifact.SCOPE_PROVIDED.equals( a.getScope() ) + || Artifact.SCOPE_SYSTEM.equals( a.getScope() ) ) + { + list.add( a ); + } + } + } + } + + return list; + } + + /** + * 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 + * @throws IOException if any + * @deprecated since 2.5, using {@link #copyJavadocResources(File, File, String)} instead of. + */ + protected static void copyJavadocResources( File outputDirectory, File javadocDir ) + throws IOException + { + copyJavadocResources( outputDirectory, javadocDir, null ); + } + + /** + * 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 JavadocVersion 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 CommandLineUtils.StringStreamConsumer(); + CommandLineUtils.StringStreamConsumer err = new CommandLineUtils.StringStreamConsumer(); + + 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 JavadocVersion.parse( extractJavadocVersion( err.getOutput() ) ); + } + else if ( StringUtils.isNotEmpty( out.getOutput() ) ) + { + return JavadocVersion.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() }, null ); + } + + List tagletClasses = new ArrayList(); + + Class tagletClass = cl.loadClass( "com.sun.tools.doclets.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; + if ( SystemUtils.IS_OS_MAC_OSX ) + { + javaHome = SystemUtils.getJavaHome(); + } + else + { + javaHome = new File( SystemUtils.getJavaHome(), ".." ); + } + + if ( javaHome == null || !javaHome.exists() ) + { + try + { + javaHome = new File( CommandLineUtils.getSystemEnvVars().getProperty( "JAVA_HOME" ) ); + } + catch ( IOException e ) + { + if ( log != null && log.isDebugEnabled() ) + { + log.debug( "IOException: " + e.getMessage() ); + } + } + } + + 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. + */ + public 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; + } + } + + 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. + */ + public static boolean isNotEmpty( final Collection collection ) + { + return collection != null && !collection.isEmpty(); + } + + /** + * Convenience method to determine that a collection is empty or null. + */ + public static boolean isEmpty( final Collection collection ) + { + return collection == null || collection.isEmpty(); + } + + /** + * 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; + } +} Added: maven/plugins/trunk/maven-javadoc-plugin/src/main/java/org/apache/maven/plugins/javadoc/JavadocVersion.java URL: http://svn.apache.org/viewvc/maven/plugins/trunk/maven-javadoc-plugin/src/main/java/org/apache/maven/plugins/javadoc/JavadocVersion.java?rev=1801772&view=auto ============================================================================== --- maven/plugins/trunk/maven-javadoc-plugin/src/main/java/org/apache/maven/plugins/javadoc/JavadocVersion.java (added) +++ maven/plugins/trunk/maven-javadoc-plugin/src/main/java/org/apache/maven/plugins/javadoc/JavadocVersion.java Wed Jul 12 19:59:51 2017 @@ -0,0 +1,87 @@ +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.codehaus.plexus.util.StringUtils; + +/** + * Once the plugin requires Java9, this class can be replaced with java.lang.Runtime.Version + *

+ * Note: Ensure the methods match, although parse+compareTo+toString should be enough. + *

+ * + * + * @author Robert Scholte + * @since 3.0.0 + */ +public class JavadocVersion implements Comparable +{ + private String rawVersion; + + private JavadocVersion( String rawVersion ) + { + if ( StringUtils.isEmpty( rawVersion ) ) + { + throw new IllegalArgumentException( "The rawVersion could not be null." ); + } + this.rawVersion = rawVersion; + } + + /** + * Parser only the version-scheme. + * + * @param s the version string + * @return the version wrapped in a JavadocVersion + */ + static JavadocVersion parse( String s ) + { + return new JavadocVersion( s ); + } + + @Override + public int compareTo( JavadocVersion other ) + { + String[] thisSegments = this.rawVersion.split( "\\." ); + String[] otherSegments = other.rawVersion.split( "\\." ); + + int minSegments = Math.min( thisSegments.length, otherSegments.length ); + + for ( int index = 0; index < minSegments; index++ ) + { + int thisValue = Integer.parseInt( thisSegments[index] ); + int otherValue = Integer.parseInt( otherSegments[index] ); + + int compareValue = Integer.compare( thisValue, otherValue ); + + if ( compareValue != 0 ) + { + return compareValue; + } + } + + return ( thisSegments.length - otherSegments.length ); + } + + @Override + public String toString() + { + return rawVersion; + } +} Added: maven/plugins/trunk/maven-javadoc-plugin/src/main/java/org/apache/maven/plugins/javadoc/ResourcesBundleMojo.java URL: http://svn.apache.org/viewvc/maven/plugins/trunk/maven-javadoc-plugin/src/main/java/org/apache/maven/plugins/javadoc/ResourcesBundleMojo.java?rev=1801772&view=auto ============================================================================== --- maven/plugins/trunk/maven-javadoc-plugin/src/main/java/org/apache/maven/plugins/javadoc/ResourcesBundleMojo.java (added) +++ maven/plugins/trunk/maven-javadoc-plugin/src/main/java/org/apache/maven/plugins/javadoc/ResourcesBundleMojo.java Wed Jul 12 19:59:51 2017 @@ -0,0 +1,145 @@ +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.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.Component; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.apache.maven.project.MavenProjectHelper; +import org.codehaus.plexus.archiver.Archiver; +import org.codehaus.plexus.archiver.ArchiverException; +import org.codehaus.plexus.archiver.manager.ArchiverManager; +import org.codehaus.plexus.archiver.manager.NoSuchArchiverException; + +import java.io.File; +import java.io.IOException; + +/** + * Bundle {@link AbstractJavadocMojo#javadocDirectory}, along with javadoc configuration options such + * as taglet, doclet, and link information into a deployable artifact. This artifact can then be consumed + * by the javadoc plugin mojos when used by the includeDependencySources option, to generate + * javadocs that are somewhat consistent with those generated in the original project itself. + * + * @since 2.7 + */ +@Mojo( name = "resource-bundle", defaultPhase = LifecyclePhase.PACKAGE, + requiresDependencyResolution = ResolutionScope.COMPILE, threadSafe = true ) +public class ResourcesBundleMojo +extends AbstractJavadocMojo +{ + + /** + * Bundle options path. + */ + public static final String BUNDLE_OPTIONS_PATH = "META-INF/maven/javadoc-options.xml"; + + /** + * Resources directory path. + */ + public static final String RESOURCES_DIR_PATH = "resources"; + + /** + * Base name of artifacts produced by this project. This will be combined with + * {@link ResourcesBundleMojo#getAttachmentClassifier()} to produce the name for this bundle + * jar. + */ + @Parameter( defaultValue = "${project.build.finalName}", readonly = true ) + private String finalName; + + /** + * Helper component to provide an easy mechanism for attaching an artifact to the project for + * installation/deployment. + */ + @Component + private MavenProjectHelper projectHelper; + + /** + * Archiver manager, used to manage jar builder. + */ + @Component + private ArchiverManager archiverManager; + + /** + * Assemble a new {@link org.apache.maven.plugins.javadoc.options.JavadocOptions JavadocOptions} instance that + * contains the configuration options in this + * mojo, which are a subset of those provided in derivatives of the {@link AbstractJavadocMojo} + * class (most of the javadoc mojos, in other words). Then, bundle the contents of the + * javadocDirectory along with the assembled JavadocOptions instance (serialized to + * META-INF/maven/javadoc-options.xml) into a project attachment for installation/deployment. + * + * {@inheritDoc} + * @see org.apache.maven.plugin.Mojo#execute() + */ + public void doExecute() + throws MojoExecutionException, MojoFailureException + { + try + { + buildJavadocOptions(); + } + catch ( IOException e ) + { + throw new MojoExecutionException( "Failed to generate javadoc-options file: " + e.getMessage(), e ); + } + + Archiver archiver; + try + { + archiver = archiverManager.getArchiver( "jar" ); + } + catch ( NoSuchArchiverException e ) + { + throw new MojoExecutionException( "Failed to retrieve jar archiver component from manager.", e ); + } + + File optionsFile = getJavadocOptionsFile(); + File bundleFile = + new File( getProject().getBuild().getDirectory(), finalName + "-" + getAttachmentClassifier() + ".jar" ); + try + { + archiver.addFile( optionsFile, BUNDLE_OPTIONS_PATH ); + + File javadocDir = getJavadocDirectory(); + if ( javadocDir.exists() && javadocDir.isDirectory() ) + { + archiver.addDirectory( javadocDir, RESOURCES_DIR_PATH + "/" ); + } + + archiver.setDestFile( bundleFile ); + archiver.createArchive(); + } + catch ( ArchiverException e ) + { + throw new MojoExecutionException( "Failed to assemble javadoc-resources bundle archive. Reason: " + + e.getMessage(), e ); + } + catch ( IOException e ) + { + throw new MojoExecutionException( "Failed to assemble javadoc-resources bundle archive. Reason: " + + e.getMessage(), e ); + } + + projectHelper.attachArtifact( getProject(), bundleFile, getAttachmentClassifier() ); + } +} Added: maven/plugins/trunk/maven-javadoc-plugin/src/main/java/org/apache/maven/plugins/javadoc/TestFixJavadocMojo.java URL: http://svn.apache.org/viewvc/maven/plugins/trunk/maven-javadoc-plugin/src/main/java/org/apache/maven/plugins/javadoc/TestFixJavadocMojo.java?rev=1801772&view=auto ============================================================================== --- maven/plugins/trunk/maven-javadoc-plugin/src/main/java/org/apache/maven/plugins/javadoc/TestFixJavadocMojo.java (added) +++ maven/plugins/trunk/maven-javadoc-plugin/src/main/java/org/apache/maven/plugins/javadoc/TestFixJavadocMojo.java Wed Jul 12 19:59:51 2017 @@ -0,0 +1,79 @@ +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.maven.artifact.DependencyResolutionRequiredException; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.Execute; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.apache.maven.project.MavenProject; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +/** + * Fix Javadoc documentation and tags for the Test Java code for the project. + * See Where Tags Can + * Be Used. + * + * @author Vincent Siveton + * @version $Id: TestFixJavadocMojo.java 1642248 2014-11-28 00:10:50Z hboutemy $ + * @since 2.6 + */ +@Mojo( name = "test-fix", requiresDependencyResolution = ResolutionScope.TEST, threadSafe = true ) +@Execute( phase = LifecyclePhase.TEST_COMPILE ) +public class TestFixJavadocMojo + extends AbstractFixJavadocMojo +{ + /** {@inheritDoc} */ + protected List getProjectSourceRoots( MavenProject p ) + { + return ( p.getTestCompileSourceRoots() == null ? Collections.emptyList() + : new LinkedList( p.getTestCompileSourceRoots() ) ); + } + + /** {@inheritDoc} */ + protected List getCompileClasspathElements( MavenProject p ) + throws DependencyResolutionRequiredException + { + return ( p.getTestClasspathElements() == null ? Collections.emptyList() + : new LinkedList( p.getTestClasspathElements() ) ); + } + + /** {@inheritDoc} */ + protected String getArtifactType( MavenProject p ) + { + return "test-jar"; + } + + /** {@inheritDoc} */ + public void execute() + throws MojoExecutionException, MojoFailureException + { + // clirr doesn't analyze test code, so ignore it + ignoreClirr = true; + + super.execute(); + } +}