You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by gb...@apache.org on 2018/02/24 19:48:13 UTC

[maven-javadoc-plugin] 01/01: [MJAVADOC-427] "Error fetching URL" for valid non-Java API links

This is an automated email from the ASF dual-hosted git repository.

gboue pushed a commit to branch MJAVADOC-427
in repository https://gitbox.apache.org/repos/asf/maven-javadoc-plugin.git

commit 0ade0576e8a294459153161a3d5071814e588dcd
Author: Guillaume Boué <gb...@apache.org>
AuthorDate: Sat Feb 24 20:23:16 2018 +0100

    [MJAVADOC-427] "Error fetching URL" for valid non-Java API links
    
    javadoc tool currently doesn't follow redirects for -link URLs, so we
    follow them ourselves and pass the last redirect location to javadoc.
---
 src/it/projects/MJAVADOC-325/verify.bsh            |  2 +-
 src/it/projects/MJAVADOC-427/invoker.properties    | 21 ++++++
 src/it/projects/MJAVADOC-427/pom.xml               | 61 ++++++++++++++++
 .../src/main/java/mjavadoc427/App.java}            | 77 +++++++++-----------
 .../verify.bsh => MJAVADOC-427/verify.groovy}      | 68 +++++++-----------
 src/it/projects/detectLinks/verify.bsh             |  8 +--
 .../maven/plugins/javadoc/AbstractJavadocMojo.java | 27 ++++++-
 .../apache/maven/plugins/javadoc/JavadocUtil.java  | 45 ++++++++++++
 .../maven/plugins/javadoc/JavadocUtilTest.java     | 84 ++++++++++++++++++++++
 9 files changed, 303 insertions(+), 90 deletions(-)

diff --git a/src/it/projects/MJAVADOC-325/verify.bsh b/src/it/projects/MJAVADOC-325/verify.bsh
index 7c4ce44..3c60e6c 100644
--- a/src/it/projects/MJAVADOC-325/verify.bsh
+++ b/src/it/projects/MJAVADOC-325/verify.bsh
@@ -30,7 +30,7 @@ if ( !optionsFile.exists() )
 }
 
 String optionsContent = FileUtils.fileRead( optionsFile );
-String javaApiLink = "'http://docs.oracle.com/javase/1,5,0/docs/api'";
+String javaApiLink = "'https://docs.oracle.com/javase/1.5.0/docs/api'";
 
 if ( !optionsContent.contains( javaApiLink ) )
 {
diff --git a/src/it/projects/MJAVADOC-427/invoker.properties b/src/it/projects/MJAVADOC-427/invoker.properties
new file mode 100644
index 0000000..aee4e3b
--- /dev/null
+++ b/src/it/projects/MJAVADOC-427/invoker.properties
@@ -0,0 +1,21 @@
+# 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.
+
+invoker.goals=clean javadoc:javadoc
+
+# slf4j javadoc is hosted on https site with a "let's encrypt" certificate, signed by IdenTrust only trusted since 8u101 (JDK-8154757)
+invoker.java.version = 1.8.0.101+
diff --git a/src/it/projects/MJAVADOC-427/pom.xml b/src/it/projects/MJAVADOC-427/pom.xml
new file mode 100644
index 0000000..bf7a406
--- /dev/null
+++ b/src/it/projects/MJAVADOC-427/pom.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>org.apache.maven.plugins.maven-javadoc-plugin.it</groupId>
+  <artifactId>mjavadoc-427</artifactId>
+  <version>1.0-SNAPSHOT</version>
+  <packaging>jar</packaging>
+  <url>https://issues.apache.org/jira/browse/MJAVADOC-427</url>
+  <description>Tests that the plugin follows redirects</description>
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+  </properties>
+
+  <dependencies>
+    <!-- url of slf4j api is http, and javadoc is redirected to https -->
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+      <version>1.7.12</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <pluginManagement>
+      <plugins>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-javadoc-plugin</artifactId>
+          <version>@pom.version@</version>
+          <configuration>
+            <detectLinks>true</detectLinks>
+          </configuration>
+        </plugin>
+      </plugins>
+    </pluginManagement>
+  </build>
+
+</project>
diff --git a/src/it/projects/MJAVADOC-325/verify.bsh b/src/it/projects/MJAVADOC-427/src/main/java/mjavadoc427/App.java
similarity index 57%
copy from src/it/projects/MJAVADOC-325/verify.bsh
copy to src/it/projects/MJAVADOC-427/src/main/java/mjavadoc427/App.java
index 7c4ce44..75194f3 100644
--- a/src/it/projects/MJAVADOC-325/verify.bsh
+++ b/src/it/projects/MJAVADOC-427/src/main/java/mjavadoc427/App.java
@@ -1,42 +1,35 @@
-
-/*
- * 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 java.io.*;
-import org.codehaus.plexus.util.*;
-
-File optionsFile = new File( basedir, "target/site/apidocs/options" );
-
-if ( !optionsFile.exists() ) 
-{
-    System.err.println( optionsFile.getAbsolutePath() + " is missing." );
-    return false;
-}
-
-String optionsContent = FileUtils.fileRead( optionsFile );
-String javaApiLink = "'http://docs.oracle.com/javase/1,5,0/docs/api'";
-
-if ( !optionsContent.contains( javaApiLink ) )
-{
-    System.err.println( "Options is missing the following line:" );
-    System.err.println( javaApiLink );
-    return false;
-} 
-
-return true;
\ No newline at end of file
+package mjavadoc427;
+
+/*
+ * 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.slf4j.LoggerFactory;
+
+/**
+ * Link to slf4j {@link LoggerFactory}.
+ */
+public class App
+{
+
+    public LoggerFactory getLoggerFactory()
+    {
+        return null;
+    }
+
+}
diff --git a/src/it/projects/MJAVADOC-325/verify.bsh b/src/it/projects/MJAVADOC-427/verify.groovy
similarity index 56%
copy from src/it/projects/MJAVADOC-325/verify.bsh
copy to src/it/projects/MJAVADOC-427/verify.groovy
index 7c4ce44..8e3c9ab 100644
--- a/src/it/projects/MJAVADOC-325/verify.bsh
+++ b/src/it/projects/MJAVADOC-427/verify.groovy
@@ -1,42 +1,26 @@
-
-/*
- * 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 java.io.*;
-import org.codehaus.plexus.util.*;
-
-File optionsFile = new File( basedir, "target/site/apidocs/options" );
-
-if ( !optionsFile.exists() ) 
-{
-    System.err.println( optionsFile.getAbsolutePath() + " is missing." );
-    return false;
-}
-
-String optionsContent = FileUtils.fileRead( optionsFile );
-String javaApiLink = "'http://docs.oracle.com/javase/1,5,0/docs/api'";
-
-if ( !optionsContent.contains( javaApiLink ) )
-{
-    System.err.println( "Options is missing the following line:" );
-    System.err.println( javaApiLink );
-    return false;
-} 
-
-return true;
\ No newline at end of file
+/*
+ * 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.
+ */
+
+def file = new File( basedir, 'target/site/apidocs/mjavadoc427/App.html' );
+
+assert file.exists()
+
+// assert that javadoc of class correctly contains link, just like method details
+assert file.text =~ /Link to slf4j <a href=".*?".*?><code>LoggerFactory<\/code><\/a>/
+assert file.text =~ /<pre>public.*?<a href=".*?".*?>LoggerFactory<\/a>.*?getLoggerFactory.*?\(\)<\/pre>/
diff --git a/src/it/projects/detectLinks/verify.bsh b/src/it/projects/detectLinks/verify.bsh
index 4e68ec2..3669ab0 100644
--- a/src/it/projects/detectLinks/verify.bsh
+++ b/src/it/projects/detectLinks/verify.bsh
@@ -69,7 +69,7 @@ try
         System.err.println( "-link not added: " + options1 );
         return false;
     }
-    if ( !contentOptions1.substring( link1 ).contains( "http://commons.apache.org/lang/apidocs" ) )
+    if ( !contentOptions1.substring( link1 ).contains( "commons.apache.org" ) )
     {
         System.err.println( "link for commons-lang not added: " + options1 );
         if ( !log.contains( "Error fetching link: http://commons.apache.org/lang/apidocs" ) )
@@ -77,7 +77,7 @@ try
             return false;
         }
     }
-    if ( !contentOptions1.substring( link1 ).contains( "http://junit.org/apidocs" ) )
+    if ( !contentOptions1.substring( link1 ).contains( "junit.org" ) )
     {
         System.err.println( "link for junit not added: " + options1 );
         if ( !log.contains( "Error fetching link: http://junit.org/apidocs" ) )
@@ -108,7 +108,7 @@ try
         System.err.println( "-link not added: " + options2 );
         return false;
     }
-    if ( !contentOptions2.substring( link2 ).contains( "http://commons.apache.org/lang/apidocs" ) )
+    if ( !contentOptions2.substring( link2 ).contains( "commons.apache.org" ) )
     {
         System.err.println( "link for commons-lang not added: " + options2 );
         if ( !log.contains( "Error fetching link: http://commons.apache.org/lang/apidocs" ) )
@@ -116,7 +116,7 @@ try
             return false;
         }
     }
-    if ( !contentOptions2.substring( link2 ).contains( "http://junit.org/apidocs" ) )
+    if ( !contentOptions2.substring( link2 ).contains( "junit.org" ) )
     {
         System.err.println( "link for junit not added: " + options2 );
         if ( !log.contains( "Error fetching link: http://junit.org/apidocs" ) )
diff --git a/src/main/java/org/apache/maven/plugins/javadoc/AbstractJavadocMojo.java b/src/main/java/org/apache/maven/plugins/javadoc/AbstractJavadocMojo.java
index afe3654..b403c00 100644
--- a/src/main/java/org/apache/maven/plugins/javadoc/AbstractJavadocMojo.java
+++ b/src/main/java/org/apache/maven/plugins/javadoc/AbstractJavadocMojo.java
@@ -3163,7 +3163,7 @@ public abstract class AbstractJavadocMojo
 
         links.addAll( getDependenciesLinks() );
 
-        return links;
+        return followLinks( links );
     }
 
     private Set<Group> collectGroups()
@@ -5839,6 +5839,31 @@ public abstract class AbstractJavadocMojo
     }
 
     /**
+     * Follows all of the given links, and returns their last redirect locations. Ordering is kept.
+     * This is necessary because javadoc tool doesn't follow links, see JDK-8190312 (MJAVADOC-427, MJAVADOC-487)
+     *
+     * @param links Links to follow.
+     * @return Last redirect location of all the links.
+     */
+    private Set<String> followLinks( Set<String> links )
+    {
+        Set<String> redirectLinks = new LinkedHashSet<>( links.size() );
+        for ( String link : links )
+        {
+            try
+            {
+                redirectLinks.add( JavadocUtil.getRedirectUrl( new URI( link ).toURL(), settings ).toString() );
+            }
+            catch ( Exception e )
+            {
+                // only print in debug, it should have been logged already in warn/error because link isn't valid
+                getLog().debug( "Could not follow " + link + ". Reason: " + e.getMessage() );
+            }
+        }
+        return redirectLinks;
+    }
+
+    /**
      * @param link not null
      * @param detecting <code>true</code> if the link is generated by
      * <code>detectLinks</code>, or <code>false</code> otherwise
diff --git a/src/main/java/org/apache/maven/plugins/javadoc/JavadocUtil.java b/src/main/java/org/apache/maven/plugins/javadoc/JavadocUtil.java
index 73ae0f2..e60699f 100644
--- a/src/main/java/org/apache/maven/plugins/javadoc/JavadocUtil.java
+++ b/src/main/java/org/apache/maven/plugins/javadoc/JavadocUtil.java
@@ -29,6 +29,7 @@ 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;
@@ -72,6 +73,7 @@ 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;
@@ -1639,6 +1641,49 @@ public class JavadocUtil
     }
 
     /**
+     * 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<URI> redirects = httpContext.getRedirectLocations();
+            return redirects.isEmpty() ? url : redirects.get( redirects.size() - 1 ).toURL();
+        }
+        finally
+        {
+            if ( httpClient != null )
+            {
+                httpClient.getConnectionManager().shutdown();
+            }
+        }
+    }
+
+    /**
      * Validates an <code>URL</code> to point to a valid <code>package-list</code> resource.
      *
      * @param url The URL to validate.
diff --git a/src/test/java/org/apache/maven/plugins/javadoc/JavadocUtilTest.java b/src/test/java/org/apache/maven/plugins/javadoc/JavadocUtilTest.java
index e13f176..ed6ee00 100644
--- a/src/test/java/org/apache/maven/plugins/javadoc/JavadocUtilTest.java
+++ b/src/test/java/org/apache/maven/plugins/javadoc/JavadocUtilTest.java
@@ -22,7 +22,9 @@ package org.apache.maven.plugins.javadoc;
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.io.OutputStream;
 import java.net.SocketTimeoutException;
+import java.net.URI;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -32,6 +34,10 @@ import java.util.Map;
 import java.util.Set;
 import java.util.regex.PatternSyntaxException;
 
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
 import org.apache.commons.lang3.builder.EqualsBuilder;
 import org.apache.maven.plugins.javadoc.JavadocUtil;
 import org.apache.maven.plugins.javadoc.ProxyServer.AuthAsyncProxyServlet;
@@ -39,6 +45,10 @@ import org.apache.maven.settings.Proxy;
 import org.apache.maven.settings.Settings;
 import org.codehaus.plexus.PlexusTestCase;
 import org.codehaus.plexus.util.FileUtils;
+import org.mortbay.jetty.Server;
+import org.mortbay.jetty.handler.AbstractHandler;
+import org.mortbay.jetty.handler.MovedContextHandler;
+import org.mortbay.util.ByteArrayISO8859Writer;
 
 /**
  * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
@@ -507,6 +517,65 @@ public class JavadocUtilTest
         }
     }
 
+    public void testGetRedirectUrlNotHttp()
+        throws Exception
+    {
+        URL url = new URI( "ftp://some.where" ).toURL();
+        assertEquals( url.toString(), JavadocUtil.getRedirectUrl( url, new Settings() ).toString() );
+
+        url = new URI( "file://some/where" ).toURL();
+        assertEquals( url.toString(), JavadocUtil.getRedirectUrl( url, new Settings() ).toString() );
+    }
+
+    /**
+     * Tests a redirect from localhost:port1 to localhost:port2
+     */
+    public void testGetRedirectUrl()
+        throws Exception
+    {
+        Server server = null, redirectServer = null;
+        try
+        {
+            redirectServer = new Server( 0 );
+            redirectServer.addHandler( new AbstractHandler()
+            {
+                @Override
+                public void handle( String target, HttpServletRequest request, HttpServletResponse response,
+                                    int dispatch )
+                    throws IOException, ServletException
+                {
+                    response.setStatus( HttpServletResponse.SC_OK );
+                    ByteArrayISO8859Writer writer = new ByteArrayISO8859Writer( 100 );
+                    writer.write( "<html>Hello world</html>" );
+                    writer.flush();
+                    response.setContentLength( writer.size() );
+                    OutputStream out = response.getOutputStream();
+                    writer.writeTo( out );
+                    out.close();
+                    writer.close();
+                }
+            } );
+            redirectServer.start();
+
+            server = new Server( 0 );
+            MovedContextHandler handler = new MovedContextHandler();
+            int redirectPort = redirectServer.getConnectors()[0].getLocalPort();
+            handler.setNewContextURL( "http://localhost:" + redirectPort );
+            server.addHandler( handler );
+            server.start();
+
+            URL url = new URI( "http://localhost:" + server.getConnectors()[0].getLocalPort() ).toURL();
+            URL redirectUrl = JavadocUtil.getRedirectUrl( url, new Settings() );
+
+            assertTrue( redirectUrl.toString().startsWith( "http://localhost:" + redirectPort ) );
+        }
+        finally
+        {
+            stopSilently( server );
+            stopSilently( redirectServer );
+        }
+    }
+
     /**
      * Method to test copyJavadocResources()
      *
@@ -626,4 +695,19 @@ public class JavadocUtilTest
         assertEquals( path1 + ps + path2 + ps + path1 + ps + path2, JavadocUtil.unifyPathSeparator( path1 + ";"
             + path2 + ":" + path1 + ":" + path2 ) );
     }
+
+    private void stopSilently( Server server )
+    {
+        try
+        {
+            if ( server != null )
+            {
+                server.stop();
+            }
+        }
+        catch ( Exception e )
+        {
+            // ignored
+        }
+    }
 }

-- 
To stop receiving notification emails like this one, please contact
gboue@apache.org.