diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..f0fb009 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,42 @@ +version: 2.1 + +commands: + build_and_test: + parameters: + reports-dir: + type: string + default: "build/reports/test_results" + steps: + - checkout + - run: + name: Download dependencies + command: ./bld download + - run: + name: Compile source + command: ./bld compile + - run: + name: Run tests + command: ./bld jacoco -reports-dir=<< parameters.reports-dir >> + - store_test_results: + path: << parameters.reports-dir >> + - store_artifacts: + path: build/reports/jacoco/test/html +jobs: + bld_jdk17: + docker: + - image: cimg/openjdk:17.0 + steps: + - build_and_test + + bld_jdk21: + docker: + - image: cimg/openjdk:21.0 + steps: + - build_and_test + +workflows: + bld: + jobs: + - bld_jdk17 + - bld_jdk21 + diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml new file mode 100644 index 0000000..057289d --- /dev/null +++ b/.github/workflows/bld.yml @@ -0,0 +1,53 @@ +name: bld-ci + +on: [ push, pull_request, workflow_dispatch ] + +jobs: + build-bld-project: + env: + COVERAGE_JDK: "17" + + strategy: + matrix: + java-version: [ 17, 21, 24 ] + os: [ ubuntu-latest, windows-latest, macos-latest ] + + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout source repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up JDK ${{ matrix.java-version }} + uses: actions/setup-java@v4 + with: + distribution: "zulu" + java-version: ${{ matrix.java-version }} + + - name: Download dependencies + run: ./bld download + + - name: Compile source + run: ./bld compile + + - name: Run tests + run: ./bld jacoco + + - name: Remove pom.xml + if: success() && matrix.java-version == env.COVERAGE_JDK && matrix.os == 'ubuntu-latest' + run: rm -rf pom.xml + + - name: SonarCloud Scan + uses: sonarsource/sonarcloud-github-action@master + if: success() && matrix.java-version == env.COVERAGE_JDK && matrix.os == 'ubuntu-latest' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v3 + if: success() && matrix.java-version == env.COVERAGE_JDK && matrix.os == 'ubuntu-latest' + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github_changelog_generator b/.github_changelog_generator new file mode 100644 index 0000000..9410c34 --- /dev/null +++ b/.github_changelog_generator @@ -0,0 +1 @@ +future-release=1.1.2 diff --git a/.gitignore b/.gitignore index d7290a0..06c4940 100644 --- a/.gitignore +++ b/.gitignore @@ -1,24 +1,58 @@ -**/.idea/dictionaries -**/.idea/gradle.xml -**/.idea/libraries -**/.idea/tasks.xml -**/.idea/workspace.xml -*.iws -.DS_Store -.classpath .gradle -.nb-gradle -.project -.settings -/bin -/build -/deploy -/dist -/gen -/local.properties -/out -/proguard-project.txt -/project.properties -/test-output -Thumbs.db -ehthumbs.db \ No newline at end of file +.DS_Store +build +lib/bld/** +!lib/bld/bld-wrapper.properties +!lib/bld/bld-wrapper.jar +lib/compile/ +lib/runtime/ +lib/standalone/ +lib/test/ + +# IDEA ignores + +# User-specific +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Editor-based Rest Client +.idea/httpRequests + +target +local.properties diff --git a/.idea/app.iml b/.idea/app.iml new file mode 100644 index 0000000..787b59b --- /dev/null +++ b/.idea/app.iml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/bld.iml b/.idea/bld.iml new file mode 100644 index 0000000..e63e11e --- /dev/null +++ b/.idea/bld.iml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/bld.xml b/.idea/bld.xml new file mode 100644 index 0000000..6600cee --- /dev/null +++ b/.idea/bld.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..d91f848 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/copyright/Erik_s_Copyright.xml b/.idea/copyright/Erik_s_Copyright.xml new file mode 100644 index 0000000..1d3bbab --- /dev/null +++ b/.idea/copyright/Erik_s_Copyright.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..1419e40 --- /dev/null +++ b/.idea/copyright/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..1e01b48 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/.idea/libraries/bld.xml b/.idea/libraries/bld.xml new file mode 100644 index 0000000..153a060 --- /dev/null +++ b/.idea/libraries/bld.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/.idea/libraries/compile.xml b/.idea/libraries/compile.xml new file mode 100644 index 0000000..99cc0c0 --- /dev/null +++ b/.idea/libraries/compile.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/runtime.xml b/.idea/libraries/runtime.xml new file mode 100644 index 0000000..d4069f2 --- /dev/null +++ b/.idea/libraries/runtime.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/test.xml b/.idea/libraries/test.xml new file mode 100644 index 0000000..57ed5ef --- /dev/null +++ b/.idea/libraries/test.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..71f1817 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..55adcb9 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/Run Tests.xml b/.idea/runConfigurations/Run Tests.xml new file mode 100644 index 0000000..99dc892 --- /dev/null +++ b/.idea/runConfigurations/Run Tests.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..32bf99b --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "java", + "name": "Run Tests", + "request": "launch", + "mainClass": "net.thauvin.erik.httpstatus.HttpStatusTest" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..3ef096f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,14 @@ +{ + "java.project.sourcePaths": [ + "src/main/java", + "src/main/resources", + "src/test/java", + "src/bld/java", + "src/bld/resources" + ], + "java.configuration.updateBuildConfiguration": "automatic", + "java.project.referencedLibraries": [ + "${HOME}/.bld/dist/bld-2.2.1.jar", + "lib/**/*.jar", + ] +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..159135a --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,62 @@ +# Changelog + +## [1.1.1](https://github.com/ethauvin/httpstatus/tree/1.1.1) (2024-06-07) + +[Full Changelog](https://github.com/ethauvin/httpstatus/compare/1.1.0...1.1.1) + +**Implemented enhancements:** + +- Sort command line output [\#10](https://github.com/ethauvin/HttpStatus/issues/10) +- Update reasons properties status codes [\#9](https://github.com/ethauvin/HttpStatus/issues/9) + +## [1.1.0](https://github.com/ethauvin/httpstatus/tree/1.1.0) (2023-09-29) + +[Full Changelog](https://github.com/ethauvin/httpstatus/compare/1.0.5...1.1.0) + +**Implemented enhancements:** + +- Print status code by response classes [\#8](https://github.com/ethauvin/HttpStatus/issues/8) +- Move to Jakarta EE [\#7](https://github.com/ethauvin/HttpStatus/issues/7) +- Only use XML character entities in Utils.escapeXml\(\) [\#6](https://github.com/ethauvin/HttpStatus/issues/6) +- Implement a StatusCode bean to check the status code. [\#5](https://github.com/ethauvin/HttpStatus/issues/5) + +## [1.0.5](https://github.com/ethauvin/httpstatus/tree/1.0.5) (2021-03-20) + +[Full Changelog](https://github.com/ethauvin/httpstatus/compare/1.0.4...1.0.5) + +**Implemented enhancements:** + +- Retrieve the error message from the request, if available. [\#4](https://github.com/ethauvin/HttpStatus/issues/4) +- Add Unofficial Codes [\#3](https://github.com/ethauvin/HttpStatus/issues/3) + +## [1.0.4](https://github.com/ethauvin/httpstatus/tree/1.0.4) (2019-05-08) + +[Full Changelog](https://github.com/ethauvin/httpstatus/compare/1.0.3...1.0.4) + +**Implemented enhancements:** + +- Implement all INA HTTP status codes [\#1](https://github.com/ethauvin/HttpStatus/issues/1) + +**Fixed bugs:** + +- hs:reason always outputs the default value. [\#2](https://github.com/ethauvin/HttpStatus/issues/2) + +## [1.0.3](https://github.com/ethauvin/httpstatus/tree/1.0.3) (2016-01-22) + +[Full Changelog](https://github.com/ethauvin/httpstatus/compare/1.0.2...1.0.3) + +## [1.0.2](https://github.com/ethauvin/httpstatus/tree/1.0.2) (2016-01-21) + +[Full Changelog](https://github.com/ethauvin/httpstatus/compare/1.0.1...1.0.2) + +## [1.0.1](https://github.com/ethauvin/httpstatus/tree/1.0.1) (2015-12-16) + +[Full Changelog](https://github.com/ethauvin/httpstatus/compare/1.0...1.0.1) + +## [1.0](https://github.com/ethauvin/httpstatus/tree/1.0) (2015-12-04) + +[Full Changelog](https://github.com/ethauvin/httpstatus/compare/ad982eff1b27c31b54df4abae00ac2d4065543d6...1.0) + + + +\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* diff --git a/HttpStatus.iml b/HttpStatus.iml deleted file mode 100644 index b2c152e..0000000 --- a/HttpStatus.iml +++ /dev/null @@ -1,229 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/HttpStatus.ipr b/HttpStatus.ipr deleted file mode 100644 index 4c4641f..0000000 --- a/HttpStatus.ipr +++ /dev/null @@ -1,454 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..79de5a8 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,27 @@ +Copyright (c) 2015-2025, Erik C. Thauvin (erik@thauvin.net) +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of this project nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.html b/README.html deleted file mode 100644 index ca483a1..0000000 --- a/README.html +++ /dev/null @@ -1,462 +0,0 @@ - - - - - - - - - - - - -

HttpStatus JSP Tag Library

-

A simple JSP Tag Library to display the code, reason and/or cause for HTTP status codes in JSP error pages.

-
-

For example:

-
<%@ page isErrorPage="true" %>
-<%@ taglib prefix="hs" uri="http://erik.thauvin.net/taglibs/httpstatus" %>
-<html><head>
-<title><hs:code/> <hs:reason default="Server Error"/></title>
-</head>
-<h1><hs:reason default="Server Error"/></h1>
-Cause: <pre><hs:cause default="Unable to complete your request."/></pre>
-...
-

or

-
<%@ page isErrorPage="true" import="net.thauvin.erik.httpstatus.Reasons" %>
-<%= Reasons.getReasonPhrase(pageContext.getErrorData().getStatusCode()) %>
-

would display on a 501 status code:

-
Not Implemented
-
-

hs:cause

-

The <hs:cause/> tag displays the cause of current HTTP status code, if any. A shorthand for:

-
<%= pageContext.getErrorData().getThrowable().getCause().getLocalizedMessage() %>
-

Optional attributes are:

- ---- - - - - - - - - - - - - - - - - -
AttributeDescription
defaultThe fallback value to output, if no cause is available.
escapeXmlConverts <, >, &, ', " to their corresponding entity codes. Value is true by default.
-

hs:code

-

The <hs:code/> tag displays the current HTTP status code, if any. A shorthand for:

-
<%= pageContext.getErrorData().getStatusCode() %>
-

hs:reason

-

The <hs:reason/> tag displays the reason for a HTTP status code, if any. Optional attributes are:

- ---- - - - - - - - - - - - - - - - - - - - - -
AttributeDescription
codeThe HTTP status error code. If not specified the current status code is used.
defaultThe fallback value to output, if no reason is available.
escapeXmlConverts <, >, &, ', " to their corresponding entity codes. Value is true by default.
-

The reasons are defined in a ResourceBundle properties as follows:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Status CodeReason
100Continue
101Switching Protocols
102Processing
200OK
201Created
202Accepted
203Non-Authoritative Information
204No Content
205Reset Content
206Partial Content
207Multi-Status
208Already Reported
226IM Used
300Multiple Choices
301Moved Permanently
302Moved Temporarily
303See Other
304Not Modified
305Use Proxy
306Switch Proxy
307Temporary Redirect
308Permanent Redirect
400Bad Request
401Unauthorized
402Payment Required
403Forbidden
404Not Found
405Method Not Allowed
406Not Acceptable
407Proxy Authentication Required
408Request Timeout
409Conflict
410Gone
411Length Required
412Precondition Failed
413Request Entity Too Large
414Request-URI Too Long
415Unsupported Media Type
416Requested Range Not Satisfiable
417Expectation Failed
418I'm A Teapot
419Insufficient Space on Resource
420Method Failure
421Misdirected Request
422Unprocessable Entity
423Locked
424Failed Dependency
426Upgrade Required
428Precondition Required
429Too Many Requests
431Request Header Fields Too Large
440Login Timeout
444No Response
449Retry With
450Blocked by Windows Parental Controls
451Unavailable For Legal Reasons
494Request Header Too Large
495Cert Error
496No Cert
497HTTP to HTTPS
498Token Expired/Invalid
499Client Closed Request
500Internal Server Error
501Not Implemented
502Bad Gateway
503Service Unavailable
504Gateway Timeout
505HTTP Version Not Supported
506Variant Also Negotiates
507Insufficient Storage
508Loop Detected
509Bandwidth Limit Exceeded
510Not Extended
511Network Authentication Required
520Unknown Error
522Origin Connection Time-out
598Network Read Timeout Error
599Network Connect Timeout Error
-
-

Use with Gradle or Maven

-

Include the following in your build.gradle file:

-
dependencies {
-    compile 'net.thauvin.erik.httpstatus:httpstatus:1.0.3'
-}
-

or as a Maven artifact:

-
<dependency>
-    <groupId>net.thauvin.erik.httpstatus</groupId>
-    <artifactId>httpstatus</artifactId>
-    <version>1.0.3</version>
-</dependency>
- - diff --git a/README.md b/README.md index 785b548..40bc87c 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,37 @@ -# HttpStatus JSP Tag Library +# HttpStatus JSP Tag Library -A simple [JSP](http://www.oracle.com/technetwork/java/javaee/jsp/index.html) Tag Library to display the [code](#hscode), [reason](#hsreason) and/or [cause](#hscode) for [HTTP status codes](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html) in JSP error pages. +[![License (3-Clause BSD)](https://img.shields.io/badge/license-BSD%203--Clause-blue.svg?style=flat-square)](http://opensource.org/licenses/BSD-3-Clause) +[![Java](https://img.shields.io/badge/java-17%2B-blue)](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html) +[![bld](https://img.shields.io/badge/2.2.1-FA9052?label=bld&labelColor=2392FF)](https://rife2.com/bld) +[![Release](https://img.shields.io/github/release/ethauvin/httpstatus.svg)](https://github.com/ethauvin/httpstatus/releases/latest) +[![Sonatype Nexus (Snapshots)](https://img.shields.io/nexus/s/net.thauvin.erik.httpstatus/httpstatus.svg?label=sanpshot&server=https%3A%2F%2Foss.sonatype.org)](https://oss.sonatype.org/content/repositories/snapshots/net/thauvin/erik/httpstatus/httpstatus/) +[![Maven Central](https://img.shields.io/maven-central/v/net.thauvin.erik.httpstatus/httpstatus.svg?color=blue)](https://central.sonatype.com/artifact/net.thauvin.erik.httpstatus/httpstatus) ----- +[![Known Vulnerabilities](https://snyk.io/test/github/ethauvin/httpstatus/badge.svg?targetFile=pom.xml)](https://snyk.io/test/github/ethauvin/httpstatus?targetFile=pom.xml) +[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ethauvin_HttpStatus&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ethauvin_HttpStatus) +[![GitHub CI](https://github.com/ethauvin/httpstatus/actions/workflows/bld.yml/badge.svg)](https://github.com/ethauvin/httpstatus/actions/workflows/bld.yml) +[![CircleCI](https://circleci.com/gh/ethauvin/HttpStatus/tree/master.svg?style=shield)](https://circleci.com/gh/ethauvin/HttpStatus/tree/master) + +A simple [JSP](http://www.oracle.com/technetwork/java/javaee/jsp/index.html) Tag Library to display the [code](#hscode), [reason](#hsreason), [cause](#hscode) and/or [message](#hsmessage) for [HTTP status codes](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html) in JSP error pages. + +## Table of Contents + +- [Examples](#examples) +- [Usage](#usage) + - [Gradle](#gradle) + - [bld](#bld) + - [Maven](#maven) +- [JSP Tags](#jsp-tags) + - [hs:cause](#hscause) + - [hs:code](#hscode) + - [hs:message](#hsmessage) + - [hs:reason](#hsreason) +- [StatusCode Bean](#statuscode-bean) +- [Reasons](#reasons) +- [Command Line Usage](#command-line-usage) +- [Contributing](#contributing) + +## Examples For example: @@ -14,6 +43,7 @@ For example:

Cause:
+Message:
... ``` @@ -24,13 +54,53 @@ or <%= Reasons.getReasonPhrase(pageContext.getErrorData().getStatusCode()) %> ``` -would display on a [501 status code](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.2): +would display on a [501 status code](https://www.rfc-editor.org/rfc/rfc9110.html#name-501-not-implemented): - Not Implemented +```console +Not Implemented +``` ----- +## Usage -## hs:cause +### [Gradle](https://gradle.org/) + +Include the following in your `build.gradle` file: + +```gradle +repositories { + mavenCentral() +} + +dependencies { + implementation 'net.thauvin.erik.httpstatus:httpstatus:1.1.1' +} +``` + +### [bld](https://rife2.com/bld) + +Include the following in your `bld` build file: + +```java +scope(compile).include( + dependency("net.thauvin.erik.httpstatus","httpstatus", version(1, 1, 0)) +); +``` + +### [Maven](http://maven.apache.org/) + +As a `Maven` artifact: + +```xml + + net.thauvin.erik.httpstatus + httpstatus + 1.1.1 + +``` + +## JSP Tags + +### hs:cause The `` tag displays the cause of current HTTP status code, if any. A shorthand for: @@ -40,128 +110,262 @@ The `` tag displays the cause of current HTTP status code, if any. A Optional attributes are: -Attribute | Description ------------ | ------------------------------------------------------------------------------------------- -`default` | The fallback value to output, if no cause is available. -`escapeXml` | Converts <, >, &, ', " to their corresponding [entity codes](http://dev.w3.org/html5/html-author/charref). Value is `true` by default. +| Attribute | Description | +| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | +| `default` | The fallback value to output, if no cause is | +| `escapeXml` | Converts <, >, &, ', " to their corresponding [entity codes](http://dev.w3.org/html5/html-author/charref). Value is `true` by default. | + +### hs:code -## hs:code The `` tag displays the current HTTP status code, if any. A shorthand for: ```jsp <%= pageContext.getErrorData().getStatusCode() %> ``` -## hs:reason +### hs:message -The `` tag displays the reason for a HTTP status code, if any. Optional attributes are: +The `` tag displays the current error message, if any. A shorthand for: -Attribute | Description ------------ | ------------------------------------------------------------------------------------------- -`code` | The HTTP status error code. If not specified the current status code is used. -`default` | The fallback value to output, if no reason is available. -`escapeXml` | Converts <, >, &, ', " to their corresponding [entity codes](http://dev.w3.org/html5/html-author/charref). Value is `true` by default. +```jsp +<%= request.getAttribute("javax.servlet.error.message") %> +``` -The reasons are defined in a [ResourceBundle](http://docs.oracle.com/javase/8/docs/api/java/util/ResourceBundle.html) properties as follows: +Optional attributes are: -Status Code | Reason ------------ | ------------------------------- -`100` | Continue -`101` | Switching Protocols -`102` | Processing -`200` | OK -`201` | Created -`202` | Accepted -`203` | Non-Authoritative Information -`204` | No Content -`205` | Reset Content -`206` | Partial Content -`207` | Multi-Status -`208` | Already Reported -`226` | IM Used -`300` | Multiple Choices -`301` | Moved Permanently -`302` | Moved Temporarily -`303` | See Other -`304` | Not Modified -`305` | Use Proxy -`306` | Switch Proxy -`307` | Temporary Redirect -`308` | Permanent Redirect -`400` | Bad Request -`401` | Unauthorized -`402` | Payment Required -`403` | Forbidden -`404` | Not Found -`405` | Method Not Allowed -`406` | Not Acceptable -`407` | Proxy Authentication Required -`408` | Request Timeout -`409` | Conflict -`410` | Gone -`411` | Length Required -`412` | Precondition Failed -`413` | Request Entity Too Large -`414` | Request-URI Too Long -`415` | Unsupported Media Type -`416` | Requested Range Not Satisfiable -`417` | Expectation Failed -`418` | I'm A Teapot -`419` | Insufficient Space on Resource -`420` | Method Failure -`421` | Misdirected Request -`422` | Unprocessable Entity -`423` | Locked -`424` | Failed Dependency -`426` | Upgrade Required -`428` | Precondition Required -`429` | Too Many Requests -`431` | Request Header Fields Too Large -`440` | Login Timeout -`444` | No Response -`449` | Retry With -`450` | Blocked by Windows Parental Controls -`451` | Unavailable For Legal Reasons -`494` | Request Header Too Large -`495` | Cert Error -`496` | No Cert -`497` | HTTP to HTTPS -`498` | Token Expired/Invalid -`499` | Client Closed Request -`500` | Internal Server Error -`501` | Not Implemented -`502` | Bad Gateway -`503` | Service Unavailable -`504` | Gateway Timeout -`505` | HTTP Version Not Supported -`506` | Variant Also Negotiates -`507` | Insufficient Storage -`508` | Loop Detected -`509` | Bandwidth Limit Exceeded -`510` | Not Extended -`511` | Network Authentication Required -`520` | Unknown Error -`522` | Origin Connection Time-out -`598` | Network Read Timeout Error -`599` | Network Connect Timeout Error +| Attribute | Description | +| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | +| `default` | The fallback value to output, if no error message is available. | +| `escapeXml` | Converts <, >, &, ', " to their corresponding [entity codes](http://dev.w3.org/html5/html-author/charref). Value is `true` by default. | ----- +### hs:reason -## Use with [Gradle](https://gradle.org/) or [Maven](http://maven.apache.org/) -Include the following in your `build.gradle` file: +The `` tag displays the reason for an HTTP status code, if any. Optional attributes are: -```gradle -dependencies { - compile 'net.thauvin.erik.httpstatus:httpstatus:1.0.3' +| Attribute | Description | +| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | +| `default` | The fallback value to output, if no reason is available. | +| `code` | The HTTP status error code. If not specified the current status code is used. | +| `escapeXml` | Converts <, >, &, ', " to their corresponding [entity codes](http://dev.w3.org/html5/html-author/charref). Value is `true` by default. | + +## StatusCode Bean + +The `StatusCode` bean can be used to check the class of the status code error. For example, using the JSTL: + +```jsp +<%@ taglib prefix="hs" uri="http://erik.thauvin.net/taglibs/httpstatus" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> + + + + + + An error occurred on your side. () + + + An error occurred on our side. () + + +``` + +or in a Servlet: + +```java +import net.thauvin.erik.httpstatus.StatusCode; + +public class ExampleServlet extends HttpServlet { + public void doGet(HttpServletRequest request, HttpServletResponse response) { + var statusCode = new StatusCode( + (Integer) request.getAttribute("javax.servlet.error.status_code")); + if (statusCode.isError()) { + if (statusCode.isServerError()) { + var reason = statusCode.getReason(); + } else { + // ... + } + } + } } ``` -or as a Maven artifact: +The `StatusCode` bean methods are: -```xml - - net.thauvin.erik.httpstatus - httpstatus - 1.0.3 - -``` \ No newline at end of file +| Method | Description | +| --------------- | -------------------------------------------------------------------- | +| `getReason` | Returns the reason for the status code (eg: `Internal Server Error`) | +| `isClientError` | Checks if the status code is a client error. | +| `isError` | Checks if the status code is a server or client error. | +| `isInfo` | Checks if the status code is informational. | +| `isRedirect` | Checks if the status code is a redirect. | +| `isServerError` | Checks if the status code is a server error. | +| `isSuccess` | Checks if the status code is a success. (`OK`) | +| `isValid` | Checks if the status code is valid. | + +## Reasons + +The reasons are defined in a [ResourceBundle](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/ResourceBundle.html) properties as follows: + +| Status Code | Reason | +| ----------- | ---------------------------------------------------------- | +| `100` | Continue | +| `101` | Switching Protocols | +| `102` | Processing | +| `103` | Early Hints | +| `110` | Response is Stale | +| `111` | Revalidation Failed | +| `112` | Disconnected Operation | +| `113` | Heuristic Expiration | +| `199` | Miscellaneous Warning | +| `200` | OK | +| `201` | Created | +| `202` | Accepted | +| `203` | Non-Authoritative Information | +| `204` | No Content | +| `205` | Reset Content | +| `206` | Partial Content | +| `207` | Multi-Status | +| `208` | Already Reported | +| `214` | Transformation Applied | +| `218` | This is fine | +| `226` | IM Used | +| `299` | Miscellaneous Persistent Warning | +| `300` | Multiple Choices | +| `301` | Moved Permanently | +| `302` | Found/Moved Temporarily | +| `303` | See Other | +| `304` | Not Modified | +| `305` | Use Proxy | +| `306` | Unused | +| `307` | Temporary Redirect | +| `308` | Permanent Redirect | +| `400` | Bad Request | +| `401` | Unauthorized | +| `402` | Payment Required | +| `403` | Forbidden | +| `404` | Not Found | +| `405` | Method Not Allowed | +| `406` | Not Acceptable | +| `407` | Proxy Authentication Required | +| `408` | Request Timeout | +| `409` | Conflict | +| `410` | Gone | +| `411` | Length Required | +| `412` | Precondition Failed | +| `413` | Payload Too Large | +| `414` | URI Too Long | +| `415` | Unsupported Media Type | +| `416` | Range Not Satisfiable | +| `417` | Expectation Failed | +| `418` | I'm A Teapot | +| `419` | Insufficient Space on Resource | +| `420` | Method Failure | +| `421` | Misdirected Request | +| `422` | Unprocessable Content | +| `423` | Locked | +| `424` | Failed Dependency | +| `425` | Too Early | +| `426` | Upgrade Required | +| `428` | Precondition Required | +| `429` | Too Many Requests | +| `430` | Request Header Fields Too Large | +| `431` | Request Header Fields Too Large | +| `440` | Login Timeout | +| `444` | No Response | +| `449` | Retry With | +| `450` | Blocked by Windows Parental Controls | +| `451` | Unavailable For Legal Reasons | +| `460` | Client Closed Connection Before Load Balancer Idle Timeout | +| `463` | X-Forwarded-For Header with More than 30 IP Addresses | +| `494` | Request Header Too Large | +| `495` | SSL Certificate Error | +| `496` | SSL Certificate Required | +| `497` | HTTP Request Sent to HTTPS Port | +| `498` | Token Expired/Invalid | +| `499` | Client Closed Request | +| `500` | Internal Server Error | +| `501` | Not Implemented | +| `502` | Bad Gateway | +| `503` | Service Unavailable | +| `504` | Gateway Timeout | +| `505` | HTTP Version Not Supported | +| `506` | Variant Also Negotiates | +| `507` | Insufficient Storage | +| `508` | Loop Detected | +| `509` | Bandwidth Limit Exceeded | +| `510` | Not Extended | +| `511` | Network Authentication Required | +| `520` | Unknown Error | +| `521` | Web Server Is Down | +| `522` | Connection Timed Out | +| `523` | Origin Is Unreachable | +| `524` | A Timeout Occurred | +| `525` | SSL Handshake Failed | +| `526` | Invalid SSL Certificate | +| `527` | Railgun Error | +| `529` | Site is overloaded | +| `530` | Site is frozen | +| `540` | Temporarily Disabled | +| `561` | Unauthorized | +| `598` | Network Read Timeout Error | +| `599` | Network Connect Timeout Error | +| `783` | Unexpected Token | + +## Command Line Usage + +You can query the reason phrase for status codes as follows: + +```console +$ java -jar httpstatus-1.1.1.jar 404 500 +404: Not Found +500: Internal Server Error +``` + +If no status code is specified, all will be printed: + +```console +$ java -jar httpstatus-1.1.1.jar +100: Continue +101: Switching Protocols +102: Processing +103: Early Hints +110: Response is Stale +111: Revalidation Failed +112: Disconnected Operation +113: Heuristic Expiration +199: Miscellaneous Warning +200: OK +201: Created +202: Accepted +203: Non-Authoritative Information +... +``` + +You can also print status codes by [response classes](https://www.rfc-editor.org/rfc/rfc9110.html#name-status-codes): + +```console +$ java -jar httpstatus-1.1.1.jar 2xx +200: OK +201: Created +202: Accepted +203: Non-Authoritative Information +... +``` + +## Contributing + +If you want to contribute to this project, all you have to do is clone the GitHub +repository: + +```console +git clone git@github.com:ethauvin/HttpStatus.git +``` + +Then use [bld](https://rife2.com/bld) to build: + +```console +cd HttpStatus +./bld compile +``` + +The project has an [IntelliJ IDEA](https://www.jetbrains.com/idea/) project structure. You can just open it after all +the dependencies were downloaded and peruse the code. diff --git a/bld b/bld new file mode 100755 index 0000000..0880033 --- /dev/null +++ b/bld @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +java -jar "$(dirname "$0")/lib/bld/bld-wrapper.jar" "$0" --build net.thauvin.erik.httpstatus.HttpStatusBuild "$@" \ No newline at end of file diff --git a/bld.bat b/bld.bat new file mode 100644 index 0000000..2810ee8 --- /dev/null +++ b/bld.bat @@ -0,0 +1,4 @@ +@echo off +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +java -jar "%DIRNAME%/lib/bld/bld-wrapper.jar" "%0" --build net.thauvin.erik.httpstatus.HttpStatusBuild %* \ No newline at end of file diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 24e19bb..0000000 --- a/build.gradle +++ /dev/null @@ -1,229 +0,0 @@ -plugins { - id "com.jfrog.bintray" version "1.5" -} -apply plugin: 'java' -apply plugin: 'idea' -apply plugin: 'application' -apply plugin: 'maven' -apply plugin: 'maven-publish' - -import org.apache.tools.ant.taskdefs.condition.Os - -defaultTasks 'deploy' - -def deployDir = 'deploy' -def localProps = 'local.properties' -def isRelease = 'release' in gradle.startParameter.taskNames - -def mavenGroupId = 'net.thauvin.erik.httpstatus' -def mavenName = 'HttpStatus' -def mavenDescription = 'HttpStatus JSP Tag Library' -def mavenUrl = 'https://github.com/ethauvin/HttpStatus' -def mavenLicense = 'The BSD 3-Clause License' -def mavenLicenseUrl = 'http://opensource.org/licenses/BSD-3-Clause' -def mavenScmCon = 'https://github.com/ethauvin/HttpStatus.git' -def mavenScmDevCon = 'git@github.com:ethauvin/HttpStatus.git' - -def pkgLicenses = ['BSD 3-Clause'] -def pkgIssueTrackerUrl = mavenUrl + '/issues' -def pkgLabels = ['jsp', 'tag library', 'http', 'status code', 'java'] - -def getVersion(isIncrement = false) -{ - def propsFile = 'version.properties' - def majorKey = 'version.major' - def minorKey = 'version.minor' - def patchKey = 'version.patch' - def metaKey = 'version.buildmeta' - def preKey = 'version.prerelease' - if (isIncrement) - { - ant.propertyfile(file: propsFile) { - entry(key: patchKey, - type: 'int', - default: '-1', - operation: '+') - } - } - def p = new Properties() - file(propsFile).withInputStream { stream -> p.load(stream) } - def metadata = p.getProperty(metaKey, '') - def prerelease = p.getProperty(preKey, '') - return (p.getProperty(majorKey, '1') + '.' + p.getProperty(minorKey, '0') + '.' + p.getProperty(patchKey, '0') + - (prerelease.length() > 0 ? '-' + prerelease : '') + (metadata.length() > 0 ? '+' + metadata : '')) -} - -version = getVersion(); - -mainClassName = 'net.thauvin.erik.httpstatus.Reasons' -[compileJava, compileTestJava]*.options*.encoding = 'UTF-8' - -repositories { - mavenCentral() -} - -dependencies { - compile 'javax.servlet:javax.servlet-api:3.1.0' - compile 'javax.servlet.jsp:jsp-api:2.2' - - testCompile 'org.testng:testng:6.9.10' -} - -bintray { - def p = new Properties() - file(localProps).withInputStream { stream -> p.load(stream) } - user = p.getProperty('bintrayUser'); - key = p.getProperty('bintrayApiKey'); - publications = ['MyPublication'] - pkg { - repo = 'maven' - name = mavenName - licenses = pkgLicenses - desc = mavenDescription - websiteUrl = mavenUrl - issueTrackerUrl = pkgIssueTrackerUrl - vcsUrl = mavenScmCon - labels = pkgLabels - publicDownloadNumbers = true - version { - name = project.version - desc = 'Version ' + project.version - vcsTag = project.version - gpg { - sign = true - } - } - } -} - -def pomConfig = { - licenses { - license { - name mavenLicense - url mavenLicenseUrl - distribution 'repo' - } - } - developers { - developer { - id 'ethauvin' - name 'Erik C. Thauvin' - email 'erik@thauvin.net' - } - } - scm { - connection 'scm:git:' + mavenScmCon - developerConnection 'scm:git:' + mavenScmDevCon - url mavenScmCon - } -} - -publishing { - publications { - MyPublication(MavenPublication) { - from components.java - artifact sourcesJar - artifact javadocJar - groupId mavenGroupId - artifactId rootProject.name - version project.version - - pom.withXml { - def root = asNode() - root.appendNode('name', mavenName) - root.appendNode('description', mavenDescription) - root.appendNode('url', mavenUrl) - root.children().last() + pomConfig - } - } - } -} - -task javadocJar(type: Jar, dependsOn: javadoc) { - group = 'Build' - description = 'Builds an archive of the javadoc docs.' - classifier = 'javadoc' - from javadoc.destinationDir -} - -task sourcesJar(type: Jar) { - group = 'Build' - description = 'Builds an archive of the source code.' - classifier = 'sources' - from sourceSets.main.allSource -} - -artifacts { - archives javadocJar - archives sourcesJar -} - -javadoc { - options.tags = ['created'] - options.author = true -} - -compileJava { - doFirst { - project.version = getVersion(isRelease) - } - //options.compilerArgs << '-Xlint:unchecked' << '-Xlint:deprecation' -} - - -jar { - manifest.attributes('Main-Class': mainClassName) -} - -clean { - delete deployDir -} - -test { - useTestNG() -} - -task wrapper(type: Wrapper) { - gradleVersion = gradle.gradleVersion -} - -task copyToDeploy(type: Copy) { - from(configurations.runtime) { - exclude 'javax.servlet-api-*.jar' - exclude 'jsp-api-*.jar' - } - from jar - into deployDir -} - -task deploy(dependsOn: ['build', 'copyToDeploy']) { - description = 'Copies all needed files to the ${deployDir} directory.' - group = 'Publishing' - outputs.dir deployDir - inputs.files copyToDeploy - mustRunAfter clean -} - -task release(dependsOn: ['deploy', 'wrapper']) << { - group = 'Publishing' - description = 'Releases new version.' - isRelease = true -} - -task pandoc(type: Exec) { - group = 'Documentation' - def pandoc_args = ['--from', 'markdown_github', '--to', 'html5', '-s', '-o', 'README.html', 'README.md'] - if (Os.isFamily(Os.FAMILY_WINDOWS)) - { - commandLine(['cmd', '/c', 'pandoc'] + pandoc_args) - } - else - { - executable '/usr/local/bin/pandoc' - args pandoc_args - } - standardOutput = new ByteArrayOutputStream() - ext.output = { - return standardOutput.toString() - } -} \ No newline at end of file diff --git a/config/pmd.xml b/config/pmd.xml new file mode 100644 index 0000000..2641880 --- /dev/null +++ b/config/pmd.xml @@ -0,0 +1,109 @@ + + + Erik's Ruleset + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 13372ae..0000000 Binary files a/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 5e7cc76..0000000 --- a/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Thu Jan 21 15:05:39 PST 2016 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-bin.zip diff --git a/gradlew b/gradlew deleted file mode 100644 index 9d82f78..0000000 --- a/gradlew +++ /dev/null @@ -1,160 +0,0 @@ -#!/usr/bin/env bash - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn ( ) { - echo "$*" -} - -die ( ) { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; -esac - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") -} -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" - -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat deleted file mode 100644 index 8a0b282..0000000 --- a/gradlew.bat +++ /dev/null @@ -1,90 +0,0 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windowz variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/lib/bld/bld-wrapper.jar b/lib/bld/bld-wrapper.jar new file mode 100644 index 0000000..7d0825c Binary files /dev/null and b/lib/bld/bld-wrapper.jar differ diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties new file mode 100644 index 0000000..418f275 --- /dev/null +++ b/lib/bld/bld-wrapper.properties @@ -0,0 +1,7 @@ +bld.downloadExtensionJavadoc=false +bld.downloadExtensionSources=true +bld.downloadLocation= +bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.10 +bld.extension-pmd=com.uwyn.rife2:bld-pmd:1.2.3 +bld.repositories=MAVEN_CENTRAL,RIFE2_RELEASES,MAVEN_LOCAL,RIFE2_SNAPSHOTS +bld.version=2.2.1 diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..73cf087 --- /dev/null +++ b/pom.xml @@ -0,0 +1,50 @@ + + + 4.0.0 + net.thauvin.erik.httpstatus + httpstatus + 1.1.2-SNAPSHOT + HttpStatus + Tag library to display the code, reason, cause and/or message for HTTP status codes in JSP error pages + https://github.com/ethauvin/HttpStatus + + + The BSD 3-Clause License + https://opensource.org/licenses/BSD-3-Clause + + + + + jakarta.servlet + jakarta.servlet-api + 6.1.0 + compile + + + jakarta.servlet.jsp + jakarta.servlet.jsp-api + 4.0.0 + compile + + + jakarta.el + jakarta.el-api + 6.0.1 + compile + + + + + ethauvin + Erik C. Thauvin + erik@thauvin.net + https://erik.thauvin.net/ + + + + scm:git:https://github.com/ethauvin/HttpStatus.git + scm:git:git@github.com:ethauvin/HttpStatus.git + https://github.com/ethauvin/HttpStatus + + diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index b28649f..0000000 --- a/settings.gradle +++ /dev/null @@ -1,19 +0,0 @@ -/* - * This settings file was auto generated by the Gradle buildInit task - * by 'erik' at '12/2/15 1:19 PM' with Gradle 2.9 - * - * The settings file is used to specify which projects to include in your build. - * In a single project build this file can be empty or even removed. - * - * Detailed information about configuring a multi-project build in Gradle can be found - * in the user guide at https://docs.gradle.org/2.9/userguide/multi_project_builds.html - */ - -/* -// To declare projects as part of a multi-project build use the 'include' method -include 'shared' -include 'api' -include 'services:webservice' -*/ - -rootProject.name = 'httpstatus' \ No newline at end of file diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 0000000..468f000 --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,7 @@ +sonar.organization=ethauvin-github +sonar.projectKey=ethauvin_HttpStatus +sonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/test/jacocoTestReport.xml +sonar.sources=src/main/java/ +sonar.tests=src/test/java/ +sonar.java.binaries=build/main,build/test +sonar.java.libraries=lib/compile/*.jar \ No newline at end of file diff --git a/src/bld/java/net/thauvin/erik/httpstatus/HttpStatusBuild.java b/src/bld/java/net/thauvin/erik/httpstatus/HttpStatusBuild.java new file mode 100644 index 0000000..9c36ca8 --- /dev/null +++ b/src/bld/java/net/thauvin/erik/httpstatus/HttpStatusBuild.java @@ -0,0 +1,157 @@ +/* + * HttpStatusBuild.java + * + * Copyright 2015-2025 Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.httpstatus; + +import rife.bld.BuildCommand; +import rife.bld.Project; +import rife.bld.extension.JacocoReportOperation; +import rife.bld.extension.PmdOperation; +import rife.bld.publish.*; +import rife.tools.exceptions.FileUtilsErrorException; + +import java.io.File; +import java.util.List; +import java.util.jar.Attributes; + +import static rife.bld.dependencies.Repository.*; +import static rife.bld.dependencies.Scope.compile; +import static rife.bld.dependencies.Scope.test; +import static rife.bld.operations.JavadocOptions.DocLinkOption.NO_MISSING; + +public class HttpStatusBuild extends Project { + final PmdOperation pmdOp = new PmdOperation() + .fromProject(this) + .failOnViolation(true) + .ruleSets("config/pmd.xml"); + + public HttpStatusBuild() { + pkg = "net.thauvin.erik.httpstatus"; + name = "HttpStatus"; + version = version(1, 1, 2, "SNAPSHOT"); + + var description = "Tag library to display the code, reason, cause and/or message for HTTP status codes in JSP error pages"; + var url = "https://github.com/ethauvin/HttpStatus"; + + mainClass = "net.thauvin.erik.httpstatus.Reasons"; + + javaRelease = 17; + + downloadSources = true; + autoDownloadPurge = true; + repositories = List.of(MAVEN_CENTRAL, SONATYPE_SNAPSHOTS); + + scope(compile) + .include(dependency("jakarta.servlet", "jakarta.servlet-api", version(6, 1, 0))) + .include(dependency("jakarta.servlet.jsp", "jakarta.servlet.jsp-api", version(4, 0, 0))) + .include(dependency("jakarta.el", "jakarta.el-api", version(6, 0, 1))); + scope(test) + .include(dependency("org.assertj", "assertj-core", version(3, 27, 3))) + .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 12, 2))) + .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 12, 2))); + + jarOperation().manifestAttribute(Attributes.Name.MAIN_CLASS, pkg + '.' + "Reasons"); + + javadocOperation().javadocOptions() + .docTitle(description + ' ' + version.toString()) + .docLint(NO_MISSING) + .link("https://jakarta.ee/specifications/platform/9/apidocs/"); + + publishOperation() + .repository(version.isSnapshot() ? repository(SONATYPE_SNAPSHOTS_LEGACY.location()) + .withCredentials(property("sonatype.user"), property("sonatype.password")) + : repository(SONATYPE_RELEASES_LEGACY.location()) + .withCredentials(property("sonatype.user"), property("sonatype.password"))) + .repository(repository("github")) + .info(new PublishInfo() + .groupId(pkg) + .artifactId(name.toLowerCase()) + .name(name) + .version(version) + .description(description) + .url(url) + .developer(new PublishDeveloper() + .id("ethauvin") + .name("Erik C. Thauvin") + .email("erik@thauvin.net") + .url("https://erik.thauvin.net/") + ) + .license(new PublishLicense() + .name("The BSD 3-Clause License") + .url("https://opensource.org/licenses/BSD-3-Clause") + ) + .scm(new PublishScm() + .connection("scm:git:" + url + ".git") + .developerConnection("scm:git:git@github.com:ethauvin/" + name + ".git") + .url(url)) + .signKey(property("sign.key")) + .signPassphrase(property("sign.passphrase"))); + } + + public static void main(String[] args) { + new HttpStatusBuild().start(args); + } + + @BuildCommand(summary = "Generates JaCoCo Reports") + public void jacoco() throws Exception { + new JacocoReportOperation() + .fromProject(this) + .execute(); + } + + @BuildCommand(summary = "Runs PMD analysis") + public void pmd() throws Exception { + pmdOp.execute(); + } + + @BuildCommand(value = "pmd-cli", summary = "Runs PMD analysis (CLI)") + public void pmdCli() throws Exception { + pmdOp.includeLineNumber(false).execute(); + } + + private void pomRoot() throws FileUtilsErrorException { + PomBuilder.generateInto(publishOperation().fromProject(this).info(), dependencies(), + new File(workDirectory, "pom.xml")); + } + + @Override + public void publish() throws Exception { + super.publish(); + pomRoot(); + } + + @Override + public void publishLocal() throws Exception { + super.publishLocal(); + pomRoot(); + } +} diff --git a/src/main/java/net/thauvin/erik/httpstatus/Reasons.java b/src/main/java/net/thauvin/erik/httpstatus/Reasons.java index 86e6972..62b20f0 100644 --- a/src/main/java/net/thauvin/erik/httpstatus/Reasons.java +++ b/src/main/java/net/thauvin/erik/httpstatus/Reasons.java @@ -1,41 +1,41 @@ /* * Reasons.java * - * Copyright (c) 2015, Erik C. Thauvin (erik@thauvin.net) + * Copyright 2015-2025 Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * modification, are permitted provided that the following conditions are met: * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * Neither the name of the author nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.httpstatus; import java.util.Map; import java.util.ResourceBundle; -import java.util.TreeMap; +import java.util.TreeSet; +import java.util.concurrent.ConcurrentHashMap; /** * Populates the {@link #REASON_PHRASES reason phrases} map from {@link #BUNDLE_BASENAME bundle properties}, and @@ -45,66 +45,78 @@ import java.util.TreeMap; * @created 2015-12-02 * @since 1.0 */ -public class Reasons -{ - /** - * The resource bundle base name. - */ - public static final String BUNDLE_BASENAME = "net.thauvin.erik.httpstatus.reasons"; +public final class Reasons { + /** + * The resource bundle base name. + */ + static final String BUNDLE_BASENAME = "net.thauvin.erik.httpstatus.reasons"; + /** + * The reason phrases map. + */ + private static final Map REASON_PHRASES = new ConcurrentHashMap<>(); - /** - * The reason phrases map. - */ - private static final Map REASON_PHRASES = new TreeMap(); + // Initializes the reason phrases map. + static { + var bundle = ResourceBundle.getBundle(BUNDLE_BASENAME); + for (var key : bundle.keySet()) { + REASON_PHRASES.put(key, bundle.getString(key)); + } + } - /** - * Returns the reason phrase for the specified status code. - * - * @param statusCode The status code. - * - * @return The reason phrase, or null. - */ - public static String getReasonPhrase(final int statusCode) - { - return getReasonPhrase(Integer.toString(statusCode)); - } + /** + * Disables the default constructor. + */ + private Reasons() { + throw new UnsupportedOperationException("Illegal constructor call."); + } - /** - * Returns the reason phrase for the specified status code. - * - * @param statusCode The status code. - * - * @return The reason phrase, or null. - */ - public static String getReasonPhrase(final String statusCode) - { - return REASON_PHRASES.get(statusCode); - } + /** + * Returns the reason phrase for the specified status code. + * + * @param statusCode The status code. + * @return The reason phrase, or null. + */ + public static String getReasonPhrase(int statusCode) { + return getReasonPhrase(Integer.toString(statusCode)); + } - /** - * Prints the status codes and reason phrases. - * - * @param args The command line arguments. - */ - public static void main(final String... args) - { - for (final Map.Entry entry : REASON_PHRASES.entrySet()) - { - System.out.println(entry.getKey() + ": " + entry.getValue()); - } + /** + * Returns the reason phrase for the specified status code. + * + * @param statusCode The status code. + * @return The reason phrase, or null. + */ + public static String getReasonPhrase(String statusCode) { + return REASON_PHRASES.get(statusCode); + } - System.out.println("Total: " + REASON_PHRASES.entrySet().size()); - } - - /** - * Initializes the reason phrases map. - */ - static - { - final ResourceBundle bundle = ResourceBundle.getBundle(BUNDLE_BASENAME); - for (final String key : bundle.keySet()) - { - REASON_PHRASES.put(key, bundle.getString(key)); - } - } -} \ No newline at end of file + /** + * Prints the reason phrase for the given status code(s). + * + * @param args The status code(s) or response class(es), prints all if none. + */ + @SuppressWarnings("PMD.SystemPrintln") + public static void main(String... args) { + var keys = new TreeSet<>(REASON_PHRASES.keySet()); + if (args.length >= 1) { + for (var arg : args) { + if (arg.endsWith("xx")) { // e.g.: 2xx + var responseClass = arg.charAt(0); + keys.forEach(k -> { + if (k.charAt(0) == responseClass) { + System.out.println(k + ": " + REASON_PHRASES.get(k)); + } + }); + } else { // e.g.: 404 + var value = REASON_PHRASES.get(arg); + if (value != null) { + System.out.println(arg + ": " + value); + } + } + } + } else { // Print all + keys.forEach(k -> System.out.println(k + ": " + REASON_PHRASES.get(k))); + System.out.println("Total: " + REASON_PHRASES.size()); + } + } +} diff --git a/src/main/java/net/thauvin/erik/httpstatus/StatusCode.java b/src/main/java/net/thauvin/erik/httpstatus/StatusCode.java new file mode 100644 index 0000000..bbcb2ea --- /dev/null +++ b/src/main/java/net/thauvin/erik/httpstatus/StatusCode.java @@ -0,0 +1,153 @@ +/* + * StatusCode.java + * + * Copyright 2015-2025 Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.httpstatus; + +import java.io.Serial; +import java.io.Serializable; + +/** + * The StatusCode bean implements methods to check the class of an HTTP status code. + * + * @author Erik C. Thauvin + * @since 1.1.0 + */ +public class StatusCode implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + private int code; + + /** + * Creates a new StatusCode object. + */ + public StatusCode() { + // Default constructor. + } + + /** + * Creates a new StatusCode object. + * + * @param code The status code. + */ + public StatusCode(int code) { + this.code = code; + } + + /** + * Returns the status code. + */ + public int getCode() { + return code; + } + + /** + * Returns the reason for the status code. + * + * @return The reason, or null. + * @see Reasons#getReasonPhrase(int) + */ + public String getReason() { + return Reasons.getReasonPhrase(code); + } + + /** + * Checks if the status code is a client error. (eg: Internal Server Error) + * + * @return true if the status code is a client error, false otherwise. + */ + public boolean isClientError() { + return code >= 400 && code < 500; + } + + /** + * Checks if the status code is a client or server error. + * + * @return true if the status code is an error, false otherwise. + */ + public boolean isError() { + return code >= 400 && code < 600; + } + + /** + * Checks if the status code is informational. + * + * @return true if the status code is informational, false otherwise. + */ + public boolean isInfo() { + return code >= 100 && code < 200; + } + + /** + * Checks if the status code is a redirect. + * + * @return true if the status code is a redirect, false otherwise. + */ + public boolean isRedirect() { + return code >= 300 && code < 400; + } + + /** + * Checks if the status code is a server error. + * + * @return true if the status code is a server error, false otherwise. + */ + public boolean isServerError() { + return code >= 500 && code < 600; + } + + /** + * Checks if the status code is a (OK) success. + * + * @return true if the status code is a success, false otherwise. + */ + public boolean isSuccess() { + return code >= 200 && code < 300; + } + + /** + * Checks if the status code is valid. + * + * @return true if the status code is valid, false otherwise. + */ + public boolean isValid() { + return code == 783 || (code >= 100 && code < 600); + } + + /** + * Sets the status code. + * + * @param code The HTTP status code. + */ + public void setCode(int code) { + this.code = code; + } +} diff --git a/src/main/java/net/thauvin/erik/httpstatus/Utils.java b/src/main/java/net/thauvin/erik/httpstatus/Utils.java index 1c9e59e..5d42b22 100644 --- a/src/main/java/net/thauvin/erik/httpstatus/Utils.java +++ b/src/main/java/net/thauvin/erik/httpstatus/Utils.java @@ -1,137 +1,97 @@ /* * Utils.java * - * Copyright (c) 2015, Erik C. Thauvin (erik@thauvin.net) + * Copyright 2015-2025 Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * modification, are permitted provided that the following conditions are met: * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * Neither the name of the author nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.httpstatus; import java.io.IOException; import java.io.Writer; /** - * The Utils class. + * The Utils class implements a collection of utility methods used throughout this project. * * @author Erik C. Thauvin * @created 2015-12-03 * @since 1.0 */ -public final class Utils -{ +public final class Utils { + /** + * Disables the default constructor. + * + * @throws UnsupportedOperationException If the constructor is called. + */ + private Utils() { + throw new UnsupportedOperationException("Illegal constructor call."); + } - /** - * Disables the default constructor. - * - * @throws UnsupportedOperationException If the constructor is called. - */ - private Utils() - throws UnsupportedOperationException - { - throw new UnsupportedOperationException("Illegal constructor call."); - } + /** + * Converts <, >, &, ', " + * to their corresponding entity codes. + * + * @param value The string value to convert. + * @return The converted string value. + */ + public static String escapeXml(String value) { + var escaped = new StringBuilder(); - /** - * Writes a string value to the specified writer. The default value is used when the actual value is null. - * - * @param out The writer to output the value to. - * @param value The string value. - * @param defaultValue The default value. - * @param xml The {@link #escapeXml(String) xml} flag. - * - * @throws IOException f an I/O error occurs. - */ - public static void outWrite(final Writer out, final String value, final String defaultValue, final boolean xml) - throws IOException - { - if (xml) - { - if (value != null) - { - out.write(escapeXml(value)); - } - else if (defaultValue != null) - { - out.write(escapeXml(defaultValue)); - } - } - else - { - if (value != null) - { - out.write(value); - } - else if (defaultValue != null) - { - out.write(defaultValue); - } - } - } + for (var i = 0; i < value.length(); i++) { + var c = value.charAt(i); + switch (c) { + case '<' -> escaped.append("<"); + case '>' -> escaped.append(">"); + case '&' -> escaped.append("&"); + case '\'' -> escaped.append("'"); + case '"' -> escaped.append("""); + default -> escaped.append(c); + } + } - /** - * Converts <, >, &, ', " - * to their corresponding entity codes. - * - * @param value The string value to convert. - * - * @return The converted string value. - */ - public static String escapeXml(final String value) - { - final StringBuilder escaped = new StringBuilder(); + return escaped.toString(); + } - for (int i = 0; i < value.length(); i++) - { - final char c = value.charAt(i); - switch (c) - { - case '<': - escaped.append("<"); - break; - case '>': - escaped.append(">"); - break; - case '&': - escaped.append("&"); - break; - case '\'': - escaped.append("'"); - break; - case '"': - escaped.append("""); - break; - default: - escaped.append(c); - break; - } - } - - return escaped.toString(); - } -} \ No newline at end of file + /** + * Writes a string value to the specified writer. The default value is used when the actual value is null. + * + * @param out The writer to output the value to. + * @param value The string value. + * @param defaultValue The default value. + * @param xml The {@link #escapeXml(String) xml} flag. + * @throws IOException If an I/O error occurs. + */ + public static void outWrite(Writer out, String value, String defaultValue, boolean xml) + throws IOException { + if (value != null) { + out.write(xml ? escapeXml(value) : value); + } else if (defaultValue != null) { + out.write(xml ? escapeXml(defaultValue) : defaultValue); + } + } +} diff --git a/src/main/java/net/thauvin/erik/httpstatus/package.html b/src/main/java/net/thauvin/erik/httpstatus/package.html new file mode 100644 index 0000000..07e4484 --- /dev/null +++ b/src/main/java/net/thauvin/erik/httpstatus/package.html @@ -0,0 +1,11 @@ + + + + HttpStatus JSP Tag Library + + +A simple JSP Tag Library to display the code, reason and/or cause for HTTP status codes in JSP error pages. + +@since 1.0 + + diff --git a/src/main/java/net/thauvin/erik/httpstatus/taglibs/CauseTag.java b/src/main/java/net/thauvin/erik/httpstatus/taglibs/CauseTag.java index 6822fce..2bf4018 100644 --- a/src/main/java/net/thauvin/erik/httpstatus/taglibs/CauseTag.java +++ b/src/main/java/net/thauvin/erik/httpstatus/taglibs/CauseTag.java @@ -1,45 +1,44 @@ /* * CauseTag.java * - * Copyright (c) 2015, Erik C. Thauvin (erik@thauvin.net) + * Copyright 2015-2025 Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * modification, are permitted provided that the following conditions are met: * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * Neither the name of the author nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.httpstatus.taglibs; +import jakarta.servlet.jsp.JspWriter; +import jakarta.servlet.jsp.PageContext; import net.thauvin.erik.httpstatus.Utils; -import javax.servlet.jsp.JspException; -import javax.servlet.jsp.JspWriter; -import javax.servlet.jsp.PageContext; import java.io.IOException; + /** * The <hs:cause> tag returns the cause (if any) for the current HTTP Status Error Code. * @@ -47,26 +46,33 @@ import java.io.IOException; * @created 2015-12-03 * @since 1.0 */ -public class CauseTag extends XmlSupport -{ - @Override - public void doTag() - throws JspException, IOException - { - final PageContext pageContext = (PageContext) getJspContext(); - final JspWriter out = pageContext.getOut(); +public class CauseTag extends XmlSupport { + /** + * Prints the cause (if any) for the current HTTP Status Error Code. + * + * @throws IOException If an error occurs while writing the output. + */ + @Override + public void doTag() throws IOException { + PageContext pageContext = (PageContext) getJspContext(); + JspWriter out = pageContext.getOut(); - String cause; + Throwable cause = pageContext.getErrorData().getThrowable().getCause(); - try - { - cause = pageContext.getErrorData().getThrowable().getCause().getLocalizedMessage(); - } - catch (NullPointerException ignore) - { - cause = defaultValue; - } + Utils.outWrite(out, getCause(cause), defaultValue, escapeXml); + } - Utils.outWrite(out, cause, defaultValue, escapeXml); - } -} \ No newline at end of file + /** + * Returns the cause's localized message or default value. + * + * @param cause The cause. + * @return The cause or {@code null}. + */ + public String getCause(Throwable cause) { + if (cause != null && cause.getLocalizedMessage() != null) { + return cause.getLocalizedMessage(); + } else { + return null; + } + } +} diff --git a/src/main/java/net/thauvin/erik/httpstatus/taglibs/CodeTag.java b/src/main/java/net/thauvin/erik/httpstatus/taglibs/CodeTag.java index d591f0a..f74b016 100644 --- a/src/main/java/net/thauvin/erik/httpstatus/taglibs/CodeTag.java +++ b/src/main/java/net/thauvin/erik/httpstatus/taglibs/CodeTag.java @@ -1,42 +1,41 @@ /* * CodeTag.java * - * Copyright (c) 2015, Erik C. Thauvin (erik@thauvin.net) + * Copyright 2015-2025 Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * modification, are permitted provided that the following conditions are met: * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * Neither the name of the author nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.httpstatus.taglibs; -import javax.servlet.jsp.JspException; -import javax.servlet.jsp.JspWriter; -import javax.servlet.jsp.PageContext; -import javax.servlet.jsp.tagext.SimpleTagSupport; +import jakarta.servlet.jsp.JspWriter; +import jakarta.servlet.jsp.PageContext; +import jakarta.servlet.jsp.tagext.SimpleTagSupport; + import java.io.IOException; /** @@ -46,15 +45,17 @@ import java.io.IOException; * @created 2015-12-03 * @since 1.0 */ -public class CodeTag extends SimpleTagSupport -{ - @Override - public void doTag() - throws JspException, IOException - { - final PageContext pageContext = (PageContext) getJspContext(); - final JspWriter out = pageContext.getOut(); +public class CodeTag extends SimpleTagSupport { + /** + * Writes the HTTP Status Error Code to the current JspWriter. + * + * @throws IOException If an I/O error occurs. + */ + @Override + public void doTag() throws IOException { + PageContext pageContext = (PageContext) getJspContext(); + JspWriter out = pageContext.getOut(); - out.write(String.valueOf(pageContext.getErrorData().getStatusCode())); - } + out.write(String.valueOf(pageContext.getErrorData().getStatusCode())); + } } \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/httpstatus/taglibs/MessageTag.java b/src/main/java/net/thauvin/erik/httpstatus/taglibs/MessageTag.java new file mode 100644 index 0000000..7674774 --- /dev/null +++ b/src/main/java/net/thauvin/erik/httpstatus/taglibs/MessageTag.java @@ -0,0 +1,64 @@ +/* + * MessageTag.java + * + * Copyright 2015-2025 Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.httpstatus.taglibs; + +import jakarta.servlet.jsp.JspWriter; +import jakarta.servlet.jsp.PageContext; +import net.thauvin.erik.httpstatus.Utils; + +import java.io.IOException; + +/** + * The <hs:message> tag returns the message (if any) for the current error. + * + * @author Erik C. Thauvin + * @created 2022-03-16 + * @since 1.0.5 + */ +public class MessageTag extends XmlSupport { + /** + * Writes the error message associated with the current HTTP Status Error Code. + * + * @throws IOException If an I/O error occurs. + */ + @Override + public void doTag() throws IOException { + PageContext pageContext = (PageContext) getJspContext(); + JspWriter out = pageContext.getOut(); + + String message = (String) pageContext.getRequest().getAttribute( + jakarta.servlet.RequestDispatcher.ERROR_MESSAGE); + + Utils.outWrite(out, message, defaultValue, escapeXml); + } +} diff --git a/src/main/java/net/thauvin/erik/httpstatus/taglibs/ReasonTag.java b/src/main/java/net/thauvin/erik/httpstatus/taglibs/ReasonTag.java index 6de2327..16cd818 100644 --- a/src/main/java/net/thauvin/erik/httpstatus/taglibs/ReasonTag.java +++ b/src/main/java/net/thauvin/erik/httpstatus/taglibs/ReasonTag.java @@ -1,44 +1,42 @@ /* * ReasonTag.java * - * Copyright (c) 2015, Erik C. Thauvin (erik@thauvin.net) + * Copyright 2015-2025 Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * modification, are permitted provided that the following conditions are met: * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * Neither the name of the author nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.httpstatus.taglibs; +import jakarta.servlet.jsp.JspWriter; +import jakarta.servlet.jsp.PageContext; import net.thauvin.erik.httpstatus.Reasons; import net.thauvin.erik.httpstatus.Utils; -import javax.servlet.jsp.JspException; -import javax.servlet.jsp.JspWriter; -import javax.servlet.jsp.PageContext; import java.io.IOException; /** @@ -49,47 +47,35 @@ import java.io.IOException; * @created 2015-12-02 * @since 1.0 */ -public class ReasonTag extends XmlSupport -{ - private int statusCode; +public class ReasonTag extends XmlSupport { + private int statusCode = -1; - @Override - public void doTag() - throws JspException, IOException - { - final PageContext pageContext = (PageContext) getJspContext(); - final JspWriter out = pageContext.getOut(); - - try - { - if (statusCode >= 0) - { - Utils.outWrite(out, Reasons.getReasonPhrase(statusCode), defaultValue, escapeXml); - } - else - { - Utils.outWrite(out, - Reasons.getReasonPhrase(pageContext.getErrorData().getStatusCode()), - defaultValue, - escapeXml); - } - } - catch (IOException ignore) - { - // Ignore. - } - } - - /** - * Sets the status code. - * - * @param statusCode The status code. - */ - @SuppressWarnings("unused") - public void setCode(final int statusCode) - { - this.statusCode = statusCode; - } + /** + * Writes the Reason Phrase for the current (or specified) HTTP Status Error Code. + */ + @Override + public void doTag() { + PageContext pageContext = (PageContext) getJspContext(); + JspWriter out = pageContext.getOut(); + try { + if (statusCode > -1) { + Utils.outWrite(out, Reasons.getReasonPhrase(statusCode), defaultValue, escapeXml); + } else { + Utils.outWrite(out, Reasons.getReasonPhrase(pageContext.getErrorData().getStatusCode()), defaultValue, + escapeXml); + } + } catch (IOException ignored) { + // Ignore. + } + } + /** + * Sets the status code. + * + * @param statusCode The status code. + */ + public void setCode(int statusCode) { + this.statusCode = statusCode; + } } diff --git a/src/main/java/net/thauvin/erik/httpstatus/taglibs/XmlSupport.java b/src/main/java/net/thauvin/erik/httpstatus/taglibs/XmlSupport.java index 09324e6..5898da1 100644 --- a/src/main/java/net/thauvin/erik/httpstatus/taglibs/XmlSupport.java +++ b/src/main/java/net/thauvin/erik/httpstatus/taglibs/XmlSupport.java @@ -1,39 +1,38 @@ /* * XmlSupport.java * - * Copyright (c) 2015, Erik C. Thauvin (erik@thauvin.net) + * Copyright 2015-2025 Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * modification, are permitted provided that the following conditions are met: * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * Neither the name of the author nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.httpstatus.taglibs; -import javax.servlet.jsp.tagext.SimpleTagSupport; +import jakarta.servlet.jsp.tagext.SimpleTagSupport; /** * Adds support for the default and escapeXml tag attributes. @@ -42,37 +41,34 @@ import javax.servlet.jsp.tagext.SimpleTagSupport; * @created 2015-12-03 * @since 1.0 */ -public abstract class XmlSupport extends SimpleTagSupport -{ - /** - * Default value string. - */ - protected String defaultValue; +public abstract class XmlSupport extends SimpleTagSupport { + /** + * Default value string. + */ + String defaultValue; - /** - * Escape XML flag. - */ - protected boolean escapeXml = true; + /** + * Escape XML flag. + */ + boolean escapeXml = true; - /** - * Sets the default value. - * - * @param defaultValue The default value. - */ - @SuppressWarnings("unused") - public void setDefault(final String defaultValue) - { - this.defaultValue = defaultValue; - } + /** + * Sets the default value. + * + * @param defaultValue The default value. + */ + @SuppressWarnings("unused") + public void setDefault(String defaultValue) { + this.defaultValue = defaultValue; + } - /** - * Sets the {@link net.thauvin.erik.httpstatus.Utils#escapeXml(String) xml} flag. - * - * @param escapeXml true or false - */ - @SuppressWarnings("unused") - public void setEscapeXml(final boolean escapeXml) - { - this.escapeXml = escapeXml; - } + /** + * Sets the {@link net.thauvin.erik.httpstatus.Utils#escapeXml(String) xml} flag. + * + * @param escapeXml true or false + */ + @SuppressWarnings("unused") + public void setEscapeXml(boolean escapeXml) { + this.escapeXml = escapeXml; + } } \ No newline at end of file diff --git a/src/main/java/net/thauvin/erik/httpstatus/taglibs/package.html b/src/main/java/net/thauvin/erik/httpstatus/taglibs/package.html new file mode 100644 index 0000000..712a542 --- /dev/null +++ b/src/main/java/net/thauvin/erik/httpstatus/taglibs/package.html @@ -0,0 +1,11 @@ + + + + HttpStatus JSP Tag Library + + +This package contains the JSP tags used to display the code, reason and/or cause for HTTP status codes. + +@since 1.0 + + \ No newline at end of file diff --git a/src/main/resources/META-INF/httpstatus.tld b/src/main/resources/META-INF/httpstatus.tld index 11de943..eb1a1d5 100644 --- a/src/main/resources/META-INF/httpstatus.tld +++ b/src/main/resources/META-INF/httpstatus.tld @@ -1,45 +1,43 @@ - HttpStatus JSP Tag Library HttpStatus JSP Tags - 1.0 + 1.1.1 hs http://erik.thauvin.net/taglibs/httpstatus @@ -77,6 +75,31 @@ empty + + + Returns the message (if any) for the current error. + + message + net.thauvin.erik.httpstatus.taglibs.MessageTag + empty + + + Default value if the resulting error message is null. + + default + false + true + + + + Converts <, > ,& ,' ," to their corresponding entity codes. Value is true by default. + + escapeXml + false + true + + + Returns the Reason Phrase for the current (or specified) HTTP Status Error Code. @@ -109,5 +132,4 @@ true - diff --git a/src/main/resources/net/thauvin/erik/httpstatus/reasons.properties b/src/main/resources/net/thauvin/erik/httpstatus/reasons.properties index 8f99dac..7cb399f 100644 --- a/src/main/resources/net/thauvin/erik/httpstatus/reasons.properties +++ b/src/main/resources/net/thauvin/erik/httpstatus/reasons.properties @@ -1,6 +1,44 @@ +# +# reasons.properties +# +# Copyright 2015-2025 Erik C. Thauvin (erik@thauvin.net) +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# Neither the name of this project nor the names of its contributors may be +# used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + 100=Continue 101=Switching Protocols 102=Processing +103=Early Hints +110=Response is Stale +111=Revalidation Failed +112=Disconnected Operation +113=Heuristic Expiration +199=Miscellaneous Warning 200=OK 201=Created 202=Accepted @@ -10,14 +48,17 @@ 206=Partial Content 207=Multi-Status 208=Already Reported +214=Transformation Applied +218=This is fine 226=IM Used +299=Miscellaneous Persistent Warning 300=Multiple Choices 301=Moved Permanently -302=Moved Temporarily +302=Found/Moved Temporarily 303=See Other 304=Not Modified 305=Use Proxy -306=Switch Proxy +306=Unused 307=Temporary Redirect 308=Permanent Redirect 400=Bad Request @@ -33,31 +74,35 @@ 410=Gone 411=Length Required 412=Precondition Failed -413=Request Entity Too Large -414=Request-URI Too Long +413=Payload Too Large +414=URI Too Long 415=Unsupported Media Type -416=Requested Range Not Satisfiable +416=Range Not Satisfiable 417=Expectation Failed 418=I'm A Teapot 419=Insufficient Space on Resource 420=Method Failure 421=Misdirected Request -422=Unprocessable Entity +422=Unprocessable Content 423=Locked 424=Failed Dependency +425=Too Early 426=Upgrade Required 428=Precondition Required 429=Too Many Requests +430=Request Header Fields Too Large 431=Request Header Fields Too Large 440=Login Timeout 444=No Response 449=Retry With 450=Blocked by Windows Parental Controls 451=Unavailable For Legal Reasons +460=Client Closed Connection Before Load Balancer Idle Timeout +463=X-Forwarded-For Header with More than 30 IP Addresses 494=Request Header Too Large -495=Cert Error -496=No Cert -497=HTTP to HTTPS +495=SSL Certificate Error +496=SSL Certificate Required +497=HTTP Request Sent to HTTPS Port 498=Token Expired/Invalid 499=Client Closed Request 500=Internal Server Error @@ -73,6 +118,17 @@ 510=Not Extended 511=Network Authentication Required 520=Unknown Error -522=Origin Connection Time-out +521=Web Server Is Down +522=Connection Timed Out +523=Origin Is Unreachable +524=A Timeout Occurred +525=SSL Handshake Failed +526=Invalid SSL Certificate +527=Railgun Error +529=Site is overloaded +530=Site is frozen +540=Temporarily Disabled +561=Unauthorized 598=Network Read Timeout Error -599=Network Connect Timeout Error \ No newline at end of file +599=Network Connect Timeout Error +783=Unexpected Token diff --git a/src/test/java/net/thauvin/erik/httpstatus/CauseTagTest.java b/src/test/java/net/thauvin/erik/httpstatus/CauseTagTest.java new file mode 100644 index 0000000..82d9705 --- /dev/null +++ b/src/test/java/net/thauvin/erik/httpstatus/CauseTagTest.java @@ -0,0 +1,58 @@ +/* + * CauseTagTest.java + * + * Copyright 2015-2025 Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.httpstatus; + +import net.thauvin.erik.httpstatus.taglibs.CauseTag; +import org.assertj.core.api.AutoCloseableSoftAssertions; +import org.junit.jupiter.api.Test; + +/** + * Implements the CauseTagTest class. + * + * @author Erik C. Thauvin + * @since 1.1.0 + */ +class CauseTagTest { + @Test + void causeTest() { + var message = "This is the cause"; + var tag = new CauseTag(); + + try (var softly = new AutoCloseableSoftAssertions()) { + softly.assertThat(tag.getCause(new Exception(message))).as("has cause").isEqualTo(message); + softly.assertThat(tag.getCause(new Exception())).as("no cause").isNull(); + softly.assertThat(tag.getCause(null)).as("null").isNull(); + softly.assertThat(tag.getCause(new Exception(""))).as("empty").isEmpty(); + } + } +} diff --git a/src/test/java/net/thauvin/erik/httpstatus/ReasonsMainTest.java b/src/test/java/net/thauvin/erik/httpstatus/ReasonsMainTest.java new file mode 100644 index 0000000..f4c8278 --- /dev/null +++ b/src/test/java/net/thauvin/erik/httpstatus/ReasonsMainTest.java @@ -0,0 +1,104 @@ +/* + * ReasonsMainTest.java + * + * Copyright 2015-2025 Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.httpstatus; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Main Class Tests. + * + * @author Erik C. Thauvin + * @created 2019-05-06 + * @since 1.0 + */ +class ReasonsMainTest { + private static final ByteArrayOutputStream OUTPUT_STREAM = new ByteArrayOutputStream(); + private static final PrintStream SYSTEM_OUT = System.out; + + @AfterAll + public static void restoreStreams() { + System.setOut(SYSTEM_OUT); + } + + @BeforeAll + public static void setUpStreams() { + System.setOut(new PrintStream(OUTPUT_STREAM)); + } + + @BeforeEach + public void resetStreams() { + OUTPUT_STREAM.reset(); + } + + @Test + void testMain() { + Reasons.main("401"); + assertThat(OUTPUT_STREAM.toString()).contains(Reasons.getReasonPhrase("401")).as("401"); + assertThat(OUTPUT_STREAM.toString()).doesNotContain("500").as("401 no 500"); + } + + @Test + void testMainAll() { + Reasons.main(); + assertThat(OUTPUT_STREAM.toString()).contains(Reasons.getReasonPhrase(301)).as("301"); + assertThat(OUTPUT_STREAM.toString()).contains(Reasons.getReasonPhrase(404)).as("404"); + } + + @Test + void testMainArgs() { + Reasons.main("500", "302"); + assertThat(OUTPUT_STREAM.toString()).contains(Reasons.getReasonPhrase("500")).as("500 (302)"); + assertThat(OUTPUT_STREAM.toString()).contains(Reasons.getReasonPhrase("302")).as("(500) 302"); + assertThat(OUTPUT_STREAM.toString()).doesNotContain("404").as("500/302 not 404"); + } + + @Test + void testMainArgsClass() { + Reasons.main("2xx"); + assertThat(OUTPUT_STREAM.toString()).contains(Reasons.getReasonPhrase("200")).as("2xx"); + } + + @Test + void testMainInvalid() { + Reasons.main("aaa"); + assertThat(OUTPUT_STREAM.toString()).as("invalid argument: aaa").isEmpty(); + } +} diff --git a/src/test/java/net/thauvin/erik/httpstatus/ReasonsTest.java b/src/test/java/net/thauvin/erik/httpstatus/ReasonsTest.java index 19e75ec..406d01e 100644 --- a/src/test/java/net/thauvin/erik/httpstatus/ReasonsTest.java +++ b/src/test/java/net/thauvin/erik/httpstatus/ReasonsTest.java @@ -1,73 +1,59 @@ /* * ReasonsTest.java * - * Copyright (c) 2015, Erik C. Thauvin (erik@thauvin.net) + * Copyright 2015-2025 Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * modification, are permitted provided that the following conditions are met: * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * Neither the name of the author nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.httpstatus; -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; +import org.junit.jupiter.api.Test; import java.util.ResourceBundle; +import static org.assertj.core.api.Assertions.assertThat; + /** - * The ReasonsTest class. + * Reasons Tests. * * @author Erik C. Thauvin * @created 2015-12-03 * @since 1.0 */ -@SuppressWarnings("unused") -public class ReasonsTest -{ - @DataProvider(name = "reasons") - public Object[][] reasons() - { - final ResourceBundle bundle = ResourceBundle.getBundle(Reasons.BUNDLE_BASENAME); - final Object[][] reasons = new String[bundle.keySet().size()][2]; - int i = 0; - for (final String key : bundle.keySet()) - { - reasons[i][0] = key; - reasons[i][1] = bundle.getString(key); - i++; - } - return reasons; - } +class ReasonsTest { + @Test + void testGetReasonPhrase() { + var bundle = ResourceBundle.getBundle(Reasons.BUNDLE_BASENAME); + for (var key : bundle.keySet()) { + assertThat(Reasons.getReasonPhrase(key)).as("getReasonPhrase(%s)", key).isEqualTo(bundle.getString(key)); + assertThat(Reasons.getReasonPhrase(Integer.parseInt(key))).as("getReasonPhrase(%s)", key) + .isEqualTo(bundle.getString(key)); + } - @Test(dataProvider = "reasons") - public void testGetReasonPhrase(String code, String reason) - throws Exception - { - Assert.assertEquals(reason, Reasons.getReasonPhrase(code)); - } -} \ No newline at end of file + } +} diff --git a/src/test/java/net/thauvin/erik/httpstatus/StatusCodeTest.java b/src/test/java/net/thauvin/erik/httpstatus/StatusCodeTest.java new file mode 100644 index 0000000..8b1ce4f --- /dev/null +++ b/src/test/java/net/thauvin/erik/httpstatus/StatusCodeTest.java @@ -0,0 +1,96 @@ +/* + * StatusCodeTest.java + * + * Copyright 2015-2025 Erik C. Thauvin (erik@thauvin.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.thauvin.erik.httpstatus; + +import org.assertj.core.api.AutoCloseableSoftAssertions; +import org.junit.jupiter.api.Test; + +import java.util.ResourceBundle; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * StatusCode Tests. + * + * @author Erik C. Thauvin + * @since 1.1.0 + */ +class StatusCodeTest { + @Test + void testStatusCode() { + var bundle = ResourceBundle.getBundle(Reasons.BUNDLE_BASENAME); + var statusCode = new StatusCode(); + + try (var softly = new AutoCloseableSoftAssertions()) { + for (var key : bundle.keySet()) { + int code = Integer.parseInt(key); + + statusCode.setCode(code); + softly.assertThat(statusCode.getCode()).as("is not %s", code).isEqualTo(code); + softly.assertThat(statusCode.isInfo()).as("%s is info", code).isEqualTo(code >= 100 && code < 200); + softly.assertThat(statusCode.isSuccess()).as("%s is ok", code).isEqualTo(code >= 200 && code < 300); + softly.assertThat(statusCode.isRedirect()).as("%s is redirect", code) + .isEqualTo(code >= 300 && code < 400); + softly.assertThat(statusCode.isClientError()).as("%s is client error", code) + .isEqualTo(code >= 400 && code < 500); + softly.assertThat(statusCode.isServerError()).as("%s is server error", code) + .isEqualTo(code >= 500 && code < 600); + softly.assertThat(statusCode.isError()).as("%s is error", code).isEqualTo(code >= 400 && code < 600); + softly.assertThat(statusCode.isValid()).as("%s is valid", code).isTrue(); + + softly.assertThat(statusCode.getReason()).as("%s reason phrase is not valid", code) + .isEqualTo(Reasons.getReasonPhrase(code)); + } + } + + try (var softly = new AutoCloseableSoftAssertions()) { + int[] unknowns = {0, 99, 600}; + + for (var code : unknowns) { + statusCode.setCode(code); + softly.assertThat(statusCode.getCode()).as("is not %s", code).isEqualTo(code); + softly.assertThat(statusCode.isInfo()).as("%s is info", code).isFalse(); + softly.assertThat(statusCode.isSuccess()).as("%s is ok", code).isFalse(); + softly.assertThat(statusCode.isRedirect()).as("%s is redirect", code).isFalse(); + softly.assertThat(statusCode.isClientError()).as("%s is client error", code).isFalse(); + softly.assertThat(statusCode.isServerError()).as("%s is server error", code).isFalse(); + softly.assertThat(statusCode.isError()).as("%s is error", code).isFalse(); + softly.assertThat(statusCode.isValid()).as("%s is invalid", code).isFalse(); + softly.assertThat(statusCode.getReason()).as("%s reason phrase is not null.", code).isNull(); + } + } + + statusCode = new StatusCode(900); + assertThat(statusCode.getCode()).as("is not %s", statusCode.getCode()).isEqualTo(900); + } +} diff --git a/src/test/java/net/thauvin/erik/httpstatus/UtilsTest.java b/src/test/java/net/thauvin/erik/httpstatus/UtilsTest.java index 735e693..5c23fd0 100644 --- a/src/test/java/net/thauvin/erik/httpstatus/UtilsTest.java +++ b/src/test/java/net/thauvin/erik/httpstatus/UtilsTest.java @@ -1,58 +1,100 @@ /* * UtilsTest.java * - * Copyright (c) 2015, Erik C. Thauvin (erik@thauvin.net) + * Copyright 2015-2025 Erik C. Thauvin (erik@thauvin.net) * All rights reserved. * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * modification, are permitted provided that the following conditions are met: * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * - * Neither the name of the author nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. + * Neither the name of this project nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.thauvin.erik.httpstatus; -import org.testng.Assert; -import org.testng.annotations.Test; +import org.assertj.core.api.AutoCloseableSoftAssertions; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.io.StringWriter; + +import static org.assertj.core.api.Assertions.assertThat; /** - * The UtilsTest class. + * Utils Tests. * * @author Erik C. Thauvin * @created 2015-12-03 * @since 1.0 */ -@SuppressWarnings("unused") -public class UtilsTest -{ - @Test - public void testEscapeXml() - throws Exception - { - Assert.assertEquals( - "This is a test. We wan't to make sure that everything is <encoded> according the "encoding" parameter & value.", - Utils.escapeXml( - "This is a test. We wan't to make sure that everything is according the \"encoding\" parameter & value.")); - } -} \ No newline at end of file +class UtilsTest { + @Test + void testEscapeXml() { + assertThat(Utils.escapeXml( + "This is a test. We wan't to make sure that everything is according the \"encoding\" " + + "parameter & value.")) + .isEqualTo("This is a test. We wan't to make sure that everything is <encoded> " + + "according the "encoding" parameter & value."); + } + + @Test + void testOutWrite() throws IOException { + try (var sw = new StringWriter(); var softly = new AutoCloseableSoftAssertions()) { + var defaultValue = "default"; + Utils.outWrite(sw, null, defaultValue, false); + softly.assertThat(sw.toString()).as("outWrite(default)").isEqualTo(defaultValue); + + sw.getBuffer().setLength(0); + Utils.outWrite(sw, "", defaultValue, false); + softly.assertThat(sw.toString()).as("outWrite(value empty)").isEmpty(); + + sw.getBuffer().setLength(0); + Utils.outWrite(sw, null, null, true); + softly.assertThat(sw.toString()).as("outWrite(null)").isEmpty(); + + sw.getBuffer().setLength(0); + Utils.outWrite(sw, "value", defaultValue, false); + softly.assertThat(sw.toString()).as("outWrite(value)").isEqualTo("value"); + + sw.getBuffer().setLength(0); + Utils.outWrite(sw, "wan't", defaultValue, true); + softly.assertThat(sw.toString()).as("outWrite(wan't)").isEqualTo("wan't"); + + sw.getBuffer().setLength(0); + Utils.outWrite(sw, null, "1 & 1", true); + softly.assertThat(sw.toString()).as("outWrite(1 & 1)").isEqualTo("1 & 1"); + + sw.getBuffer().setLength(0); + Utils.outWrite(sw, "", defaultValue, true); + softly.assertThat(sw.toString()).as("outWrite(value empty).as(xml)").isEmpty(); + + sw.getBuffer().setLength(0); + Utils.outWrite(sw, null, "", true); + softly.assertThat(sw.toString()).as("outWrite(default empty)").isEmpty(); + + sw.getBuffer().setLength(0); + Utils.outWrite(sw, null, null, true); + softly.assertThat(sw.toString()).as("outWrite(null).as(xml)").isEmpty(); + } + } +} diff --git a/version.properties b/version.properties deleted file mode 100644 index ca908a3..0000000 --- a/version.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Fri, 22 Jan 2016 13:17:16 -0800 -version.major=1 -version.minor=0 -version.patch=3 -version.buildmeta= -version.prerelease=