From 274497bbf49db1351cbf1cc2b062535a0d7fe93a Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 14 Mar 2024 16:53:48 +0100 Subject: [PATCH] [doc] Fix more external links --- docs/pages/pmd/about/help.md | 2 +- docs/pages/pmd/devdocs/development.md | 13 ++- docs/pages/pmd/languages/modelica.md | 2 +- docs/pages/pmd/projectdocs/trivia/news.md | 4 +- docs/pages/pmd/userdocs/cli_reference.md | 2 +- docs/pages/pmd/userdocs/cpd/cpd.md | 2 +- docs/pages/pmd/userdocs/installation.md | 2 +- docs/pages/pmd/userdocs/tools/gradle.md | 10 +- docs/pages/release_notes.md | 2 +- .../pmd/doc/internal/DeadLinksChecker.java | 93 ++++++++++++------- .../resources/category/java/errorprone.xml | 2 +- .../resources/category/java/performance.xml | 8 +- 12 files changed, 85 insertions(+), 57 deletions(-) diff --git a/docs/pages/pmd/about/help.md b/docs/pages/pmd/about/help.md index 8e45c96b8b..abc0434f17 100644 --- a/docs/pages/pmd/about/help.md +++ b/docs/pages/pmd/about/help.md @@ -16,6 +16,6 @@ There are numerous ways of getting help: * You can also ask questions on [github discussions](https://github.com/pmd/pmd/discussions). * Or you can join the [Mailing List](https://lists.sourceforge.net/lists/listinfo/pmd-devel) or browse - through the archives ([archive1](http://java-pmd.30631.n5.nabble.com/), [archive2](http://web.archive.org/web/20160715035623/http://blog.gmane.org:80/gmane.comp.java.audit.pmd.devel)). + through the [mailing list archive](https://sourceforge.net/p/pmd/mailman/pmd-devel/). * Of course, you can also directly jump to our [source code on github](https://github.com/pmd/pmd). diff --git a/docs/pages/pmd/devdocs/development.md b/docs/pages/pmd/devdocs/development.md index f7e47c89fd..ad4871fc73 100644 --- a/docs/pages/pmd/devdocs/development.md +++ b/docs/pages/pmd/devdocs/development.md @@ -2,13 +2,12 @@ title: Developer Resources tags: [devdocs] permalink: pmd_devdocs_development.html -last_updated: August 2017 +last_updated: March 2024 --- -The next version of PMD will be developed in parallel with this release. We will release additional bugfix versions as needed. ## Source Code -The complete source code can be found on github: +The complete source code can be found on Github: * [github.com/pmd/pmd](https://github.com/pmd/pmd) - main PMD repository. Includes all the code to support all languages, including this documentation. * [github.com/pmd/pmd.github.io](https://github.com/pmd/pmd.github.io) - Contains the landing page [https://pmd.github.io](https://pmd.github.io) @@ -18,7 +17,7 @@ The complete source code can be found on github: ## Continuous Integration -We use [Travis CI](https://travis-ci.com/pmd) as our ci service. The main repo and the eclipse plugin are built for +We use [GitHub Actions](https://github.com/pmd/pmd/actions) as our ci service. The main repo and the eclipse plugin are built for every push. Each pull request is built as well. The maven snapshot artifacts are deployed at [Sonatypes OSS snapshot repository](https://oss.sonatype.org/content/repositories/snapshots/net/sourceforge/pmd/pmd/). @@ -27,7 +26,11 @@ Ready-to-use binary packages are uploaded to sourceforge at mentions both PMD and CPD as useful code-checking tools. * October 2005 - Levent Gurses' article "Improving Code Quality with PMD and Eclipse" in - [EclipseZone](http://www.eclipsezone.com/articles/pmd/) talks about the PMD Eclipse plugin and explains many + [EclipseZone](https://web.archive.org/web/20080807160502/http://www.eclipsezone.com/articles/pmd/) talks about the PMD Eclipse plugin and explains many different facets of PMD - XPath, writing rules, the AST, all that. Good stuff! * June 2005 - Amit Chaturvedi's article "Java & Static Analysis" in @@ -103,7 +103,7 @@ author: Tom Copeland * June 2004 - [Open Source-Perlen](http://tinyurl.com/3dgpe) - A German article on PMD in Java Magazin -* June 2004 - [Improving Project Quality with PMD](http://jnb.ociweb.com/jnb/jnbJun2004.html) - Tom Wheeler's +* June 2004 - [Improving Project Quality with PMD](https://web.archive.org/web/20211203041417/http://jnb.ociweb.com/jnb/jnbJun2004.html) - Tom Wheeler's "Java News Brief", June 2004 issue * February 2004 - [Software Development](http://www.drdobbs.com/free-as-in-freedom/184415103) - Listed as one of diff --git a/docs/pages/pmd/userdocs/cli_reference.md b/docs/pages/pmd/userdocs/cli_reference.md index 49585b60a4..c616c1847b 100644 --- a/docs/pages/pmd/userdocs/cli_reference.md +++ b/docs/pages/pmd/userdocs/cli_reference.md @@ -180,7 +180,7 @@ The tool comes with a rather extensive help text, simply running with `--help`! ## Additional Java Runtime Options PMD is executed via a Java runtime. In some cases, you might need to set additional runtime options, e.g. -if you want to analyze a project, that uses one of OpenJDK's [Preview Language Features](http://openjdk.java.net/jeps/12). +if you want to analyze a project, that uses one of OpenJDK's [JEP 12: Preview Language Features](https://openjdk.org/jeps/12). Just set the environment variable `PMD_JAVA_OPTS` before executing PMD, e.g. diff --git a/docs/pages/pmd/userdocs/cpd/cpd.md b/docs/pages/pmd/userdocs/cpd/cpd.md index 46ba4f6110..58259b8a71 100644 --- a/docs/pages/pmd/userdocs/cpd/cpd.md +++ b/docs/pages/pmd/userdocs/cpd/cpd.md @@ -529,7 +529,7 @@ CPD has been through three major incarnations: [here](http://www.onjava.com/pub/a/onjava/2003/03/12/pmd_cpd.html)). * Then it was completely rewritten by Brian Ewins using the - [Burrows-Wheeler transform](http://dogma.net/markn/articles/bwt/bwt.htm). + [Burrows-Wheeler transform](https://en.wikipedia.org/wiki/Burrows%E2%80%93Wheeler_transform). * Finally, it was rewritten by Steve Hawkins to use the [Karp-Rabin](http://www.nist.gov/dads/HTML/karpRabin.html) string matching algorithm. diff --git a/docs/pages/pmd/userdocs/installation.md b/docs/pages/pmd/userdocs/installation.md index 455c054997..04a5901180 100644 --- a/docs/pages/pmd/userdocs/installation.md +++ b/docs/pages/pmd/userdocs/installation.md @@ -20,7 +20,7 @@ sidebar: pmd_sidebar * For Windows: [Winzip](http://winzip.com) or the free [7-zip](http://www.7-zip.org/) * For Linux / Unix: [InfoZip](http://infozip.sourceforge.net/) -{% include note.html content="For executing the Designer (`pmd designer`) using [OpenJDK](http://jdk.java.net) or Java 11+, you need additionally [JavaFX](https://gluonhq.com/products/javafx/). Download it, extract it and set the environment variable JAVAFX_HOME pointing at that directory." %} +{% include note.html content="For executing the Designer (`pmd designer`) using [OpenJDK](https://jdk.java.net) or Java 11+, you need additionally [JavaFX](https://gluonhq.com/products/javafx/). Download it, extract it and set the environment variable JAVAFX_HOME pointing at that directory." %} ### Installation diff --git a/docs/pages/pmd/userdocs/tools/gradle.md b/docs/pages/pmd/userdocs/tools/gradle.md index 410d36e1a0..9f1e1b1e99 100644 --- a/docs/pages/pmd/userdocs/tools/gradle.md +++ b/docs/pages/pmd/userdocs/tools/gradle.md @@ -57,8 +57,8 @@ pmd { Source code for Gradles PMD Plugin is available here: -* [gradle/gradle code-quality](https://github.com/gradle/gradle/tree/master/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality) - * [Pmd.java](https://github.com/gradle/gradle/blob/master/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/Pmd.java) - * [PmdExtension.java](https://github.com/gradle/gradle/blob/master/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/PmdExtension.java) - * [PmdPlugin.java](https://github.com/gradle/gradle/blob/master/subprojects/code-quality/src/main/groovy/org/gradle/api/plugins/quality/PmdPlugin.java) -* The default PMD version used by gradle: [DEFAULT_PMD_VERSION](https://github.com/gradle/gradle/blob/5e8d7b6b2eadf314767a4b3426db24e841b86ece/platforms/jvm/code-quality/src/main/groovy/org/gradle/api/plugins/quality/PmdPlugin.java#L67) +* [gradle/gradle code-quality](https://github.com/gradle/gradle/tree/master/platforms/jvm/code-quality/src/main/groovy/org/gradle/api/plugins/quality) + * [Pmd.java](https://github.com/gradle/gradle/blob/master/platforms/jvm/code-quality/src/main/groovy/org/gradle/api/plugins/quality/Pmd.java) + * [PmdExtension.java](https://github.com/gradle/gradle/blob/master/platforms/jvm/code-quality/src/main/groovy/org/gradle/api/plugins/quality/PmdExtension.java) + * [PmdPlugin.java](https://github.com/gradle/gradle/blob/master/platforms/jvm/code-quality/src/main/groovy/org/gradle/api/plugins/quality/PmdPlugin.java) +* The default PMD version used by gradle: [DEFAULT_PMD_VERSION](https://github.com/gradle/gradle/blob/d6daeeb446e6a966c33efea5f3f5f1a2d96f6b8f/platforms/jvm/code-quality/src/main/groovy/org/gradle/api/plugins/quality/PmdPlugin.java#L66) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 934f839350..4d10a36573 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -398,7 +398,7 @@ The rules have been moved into categories with PMD 6. #### API Changes -See [Detailed Release Notes for PMD 7.0.0]({{ baseurl }}pmd_release_notes_pmd7.html#700). +See [Detailed Release Notes for PMD 7]({{ baseurl }}pmd_release_notes_pmd7.html#700). #### External Contributions * [#4093](https://github.com/pmd/pmd/pull/4093): \[apex] Summit-AST Apex module - Part 1 - [Edward Klimoshenko](https://github.com/eklimo) (@eklimo) diff --git a/pmd-doc/src/main/java/net/sourceforge/pmd/doc/internal/DeadLinksChecker.java b/pmd-doc/src/main/java/net/sourceforge/pmd/doc/internal/DeadLinksChecker.java index ad849464da..28b6ce4a39 100644 --- a/pmd-doc/src/main/java/net/sourceforge/pmd/doc/internal/DeadLinksChecker.java +++ b/pmd-doc/src/main/java/net/sourceforge/pmd/doc/internal/DeadLinksChecker.java @@ -31,6 +31,9 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; import java.util.concurrent.RunnableFuture; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -66,20 +69,22 @@ public class DeadLinksChecker { // the link is actually pointing to a file in the pmd project private static final String LOCAL_FILE_PREFIX = "https://github.com/pmd/pmd/blob/master/"; - // don't check links to PMD bugs/issues/pull-requests (performance optimization) + // don't check links to PMD bugs/issues/pull-requests and some other sites (performance optimization) private static final List IGNORED_URL_PREFIXES = Collections.unmodifiableList(Arrays.asList( "https://github.com/pmd/pmd/issues/", "https://github.com/pmd/pmd/pull/", - "https://sourceforge.net/p/pmd/bugs/" + "https://sourceforge.net/p/pmd/bugs/", + "https://pmd.github.io/", + "https://openjdk.org/jeps" // very slow... )); // prevent checking the same link multiple times - private final Map> urlResponseCache = new ConcurrentHashMap<>(); + private final Map> urlResponseCache = new ConcurrentHashMap<>(); private final ExecutorService executorService = Executors.newCachedThreadPool(); - public void checkDeadLinks(Path rootDirectory) { + public void checkDeadLinks(Path rootDirectory) throws InterruptedException { final Path pagesDirectory = rootDirectory.resolve("docs/pages"); final Path docsDirectory = rootDirectory.resolve("docs"); @@ -156,10 +161,7 @@ public class DeadLinksChecker { Future futureMessage = getCachedFutureResponse(linkTarget) - .thenApply(c -> c >= 400) - // It's important not to use the matcher in this mapper! - // It may be exhausted at the time of execution - .thenApply(dead -> dead ? String.format("%8d: %s", lineNo, linkText) : null); + .thenApply(errorMessage -> errorMessage != null ? String.format("%8d: %s (%s)", lineNo, linkText, errorMessage) : null); addDeadLink(fileToDeadLinks, mdFile, futureMessage); @@ -200,6 +202,8 @@ public class DeadLinksChecker { } executorService.shutdown(); + LOG.info("Checking {} external links now...", checkedExternalLinks); + Map> joined = joinFutures(fileToDeadLinks); LOG.info("Scanned {} files for dead links.", scannedFiles); LOG.info(" Found {} external links, {} of those where checked.", foundExternalLinks, checkedExternalLinks); @@ -208,15 +212,13 @@ public class DeadLinksChecker { LOG.info("External links weren't checked, set -D" + CHECK_EXTERNAL_LINKS_PROPERTY + "=true to enable it."); } - Map> joined = joinFutures(fileToDeadLinks); - if (joined.isEmpty()) { LOG.info("No errors found!"); } else { LOG.warn("Found dead link(s):"); for (Path file : joined.keySet()) { - System.err.println(rootDirectory.relativize(file).toString()); - joined.get(file).forEach(LOG::warn); + System.err.println(rootDirectory.relativize(file)); + joined.get(file).forEach(System.err::println); } throw new AssertionError("Dead links detected"); } @@ -276,9 +278,8 @@ public class DeadLinksChecker { while (captionMatcher.find()) { final String anchor = captionMatcher.group(1) .toLowerCase(Locale.ROOT) - .replaceAll("'", "") // remove all apostrophes - .replaceAll("[^a-z0-9_]+", "-") // replace all non-alphanumeric characters with dashes - .replaceAll("^-+|-+$", ""); // trim leading or trailing dashes + .replaceAll("'|\\.", "") // remove all apostrophes and dots + .replaceAll("[^a-z0-9_]+", "-"); // replace all non-alphanumeric characters with dashes htmlPages.add(pageUrl + "#" + anchor); } @@ -308,46 +309,70 @@ public class DeadLinksChecker { } - private CompletableFuture getCachedFutureResponse(String url) { + private CompletableFuture getCachedFutureResponse(String url) { if (urlResponseCache.containsKey(url)) { - LOG.info("response: HTTP {} (CACHED) on {}", urlResponseCache.get(url), url); - return urlResponseCache.get(url); + CompletableFuture cachedFuture = urlResponseCache.get(url); + if (cachedFuture.isDone()) { + try { + LOG.debug("response: HTTP {} (CACHED) on {}", cachedFuture.get(100, TimeUnit.MILLISECONDS), url); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } catch (ExecutionException e) { + LOG.info("response failed: (CACHED) on {}", url); + } catch (TimeoutException e) { + // actually, this shouldn't happen, as we checked with isDone() before + LOG.info("response future timeout: (CACHED) on {}", url); + } + } + return cachedFuture; } else { // process asynchronously - CompletableFuture futureResponse = CompletableFuture.supplyAsync(() -> computeHttpResponse(url), executorService); + CompletableFuture futureResponse = CompletableFuture.supplyAsync(() -> computeHttpResponse(url), executorService); urlResponseCache.put(url, futureResponse); return futureResponse; } } + // limit parallel requests to avoid 429 Too Many Requests + private Semaphore semaphore = new Semaphore(3); - private int computeHttpResponse(String url) { + private String computeHttpResponse(String url) { try { - final HttpURLConnection httpURLConnection = (HttpURLConnection) new URL(url).openConnection(); - httpURLConnection.setRequestMethod("HEAD"); - httpURLConnection.setConnectTimeout(5000); - httpURLConnection.setReadTimeout(15000); - httpURLConnection.connect(); - final int responseCode = httpURLConnection.getResponseCode(); + semaphore.acquire(); + // logging to see something is going on... + LOG.info("Checking {} now...", url); + + final HttpURLConnection httpUrlConnection = (HttpURLConnection) new URL(url).openConnection(); + httpUrlConnection.setRequestMethod("HEAD"); + httpUrlConnection.setConnectTimeout((int) TimeUnit.SECONDS.toMillis(60)); + httpUrlConnection.setReadTimeout((int) TimeUnit.SECONDS.toMillis(60)); + httpUrlConnection.connect(); + int responseCode = httpUrlConnection.getResponseCode(); String response = "HTTP " + responseCode; - if (httpURLConnection.getHeaderField("Location") != null) { - response += ", Location: " + httpURLConnection.getHeaderField("Location"); + if (httpUrlConnection.getHeaderField("Location") != null) { + response += ", Location: " + httpUrlConnection.getHeaderField("Location"); } - LOG.debug("response: {} on {}", response, url); - // success (HTTP 2xx) or redirection (HTTP 3xx) - return responseCode; + // everything above 400 is an error + if (responseCode >= 400) { + LOG.debug("response failure: {} on {}", responseCode, url); + return "HTTP " + responseCode + " " + httpUrlConnection.getResponseMessage(); + } - } catch (IOException ex) { + // success (HTTP 2xx) or redirection (HTTP 3xx) is ok + return null; // no error + } catch (IOException | InterruptedException ex) { LOG.debug("response: {} on {} : {}", ex.getClass().getName(), url, ex.getMessage()); - return 599; + return ex.getClass().getName() + ": " + ex.getMessage(); + } finally { + semaphore.release(); } } - public static void main(String[] args) throws IOException { + public static void main(String[] args) throws IOException, InterruptedException { if (args.length != 1) { System.err.println("Wrong arguments!"); System.err.println(); diff --git a/pmd-java/src/main/resources/category/java/errorprone.xml b/pmd-java/src/main/resources/category/java/errorprone.xml index 784e13d5e9..31be010752 100644 --- a/pmd-java/src/main/resources/category/java/errorprone.xml +++ b/pmd-java/src/main/resources/category/java/errorprone.xml @@ -69,7 +69,7 @@ If the call to `setAccessible` is wrapped within a `PrivilegedAction`, then the is assumed to be deliberate and is not reported. Note that with Java 17 the Security Manager, which is used for `PrivilegedAction` execution, -is deprecated: [JEP 411: Deprecate the Security Manager for Removal](https://openjdk.java.net/jeps/411). +is deprecated: [JEP 411: Deprecate the Security Manager for Removal](https://openjdk.org/jeps/411). For future-proof code, deliberate access alteration should be suppressed using the usual suppression methods (e.g. by using `@SuppressWarnings` annotation). diff --git a/pmd-java/src/main/resources/category/java/performance.xml b/pmd-java/src/main/resources/category/java/performance.xml index e8a4cfcb80..376b536499 100644 --- a/pmd-java/src/main/resources/category/java/performance.xml +++ b/pmd-java/src/main/resources/category/java/performance.xml @@ -236,7 +236,7 @@ public class DateStuff { The FileInputStream and FileOutputStream classes contains a finalizer method which will cause garbage collection pauses. -See [JDK-8080225](https://bugs.openjdk.java.net/browse/JDK-8080225) for details. +See [JDK-8080225](https://bugs.openjdk.org/browse/JDK-8080225) for details. The FileReader and FileWriter constructors instantiate FileInputStream and FileOutputStream, again causing garbage collection issues while finalizer methods are called. @@ -777,11 +777,11 @@ if (s.indexOf('d') {} typeResolution="true" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_performance.html#useiostreamswithapachecommonsfileitem"> -Problem: Use of [FileItem.get()](https://commons.apache.org/proper/commons-fileupload/apidocs/org/apache/commons/fileupload/FileItem.html#get--) -and [FileItem.getString()](https://commons.apache.org/proper/commons-fileupload/apidocs/org/apache/commons/fileupload/FileItem.html#getString--) +Problem: Use of [FileItem.get()](https://javadoc.io/static/commons-fileupload/commons-fileupload/1.5/org/apache/commons/fileupload/FileItem.html#get--) +and [FileItem.getString()](https://javadoc.io/static/commons-fileupload/commons-fileupload/1.5/org/apache/commons/fileupload/FileItem.html#getString--) could exhaust memory since they load the entire file into memory. -Solution: Use [FileItem.getInputStream()](https://commons.apache.org/proper/commons-fileupload/apidocs/org/apache/commons/fileupload/FileItem.html#getInputStream--) +Solution: Use [FileItem.getInputStream()](https://javadoc.io/static/commons-fileupload/commons-fileupload/1.5/org/apache/commons/fileupload/FileItem.html#getInputStream--) and buffering. 3