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.sive...@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 gb...@apache.org.