diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 69bcdeab..00000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Build - -on: - push: - paths: - - .github/workflows/build.yml - - build.gradle - - settings.gradle - - src/** - - launcher/build.gradle - - launcher/src/** - workflow_dispatch: - -jobs: - build: - runs-on: antville - - steps: - - uses: actions/checkout@v4 - - - name: Compile with Gradle - run: ./gradlew :compileJava diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml deleted file mode 100644 index 9eeb14b0..00000000 --- a/.github/workflows/deploy.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: Deploy - -on: - workflow_dispatch: - inputs: - hostname: - description: Hostname - type: string - required: true - default: antville.org - -jobs: - stage: - runs-on: antville - - environment: - name: production - url: ${{ inputs.hostname }} - - steps: - - uses: actions/checkout@v4 - - - name: Build with Gradle - run: ./gradlew installDist - - - name: Copy build files to server - run: | - rsync ./build/install/helma/ ${{ inputs.hostname }}:./ \ - --verbose --archive --delete --compress \ - --filter '+ /bin' \ - --filter '+ /extras' \ - --filter '+ /launcher.jar' \ - --filter '- /lib/ext' \ - --filter '+ /lib' \ - --filter '+ /modules' \ - --filter '- /*' - - - name: Restart Helma - run: ssh ${{ inputs.hostname }} restart diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ac8a3d1a..35333503 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,59 +1,45 @@ name: Release on: - workflow_dispatch: push: - tags: '2*' + tags: + - 'v*' permissions: contents: write jobs: - release: - runs-on: antville + build: + runs-on: ubuntu-latest env: - GH_TOKEN: ${{ secrets.GH_TOKEN }} + GH_TOKEN: ${{ github.token }} LC_TIME: en_US.UTF-8 steps: - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Create release notes - id: create_release_notes - run: | - release_notes=$(npx git-cliff@latest --latest) - # Write the release notes as a heredoc to the workflow output - # ⚠️ No white space around `<<` is crucial! - echo "release_notes<<.eot0x03" >> $GITHUB_OUTPUT - echo "$release_notes" >> $GITHUB_OUTPUT - echo ".eot0x03" >> $GITHUB_OUTPUT + - name: Set up Java + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 21 + + - name: Set up Gradle + uses: gradle/actions/setup-gradle@v3 - name: Build with Gradle run: ./gradlew assembleDist - name: Create release - uses: actions/forgejo-release@v2 - with: - direction: upload - url: https://code.host.antville.org - token: ${{ github.token }} - title: Helma ${{ github.ref_name }} - release-dir: build/distributions - release-notes: ${{ steps.create_release_notes.outputs.release_notes }} - verbose: true - - - name: Create release at GitHub run: | gh release create "$GITHUB_REF_NAME" \ --repo "$GITHUB_REPOSITORY" \ - --title "Helma ${{ github.ref_name }}" \ - --notes "${{ steps.create_release_notes.outputs.release_notes }}" + --title "$(date +'%d %b %Y')" \ + --generate-notes - - name: Upload release assets to GitHub + - name: Upload assets run: | - gh release upload "$GITHUB_REF_NAME" build/distributions/helma-*.* \ - --repo "$GITHUB_REPOSITORY" \ + gh release upload "$GITHUB_REF_NAME" \ + build/distributions/helma-*.* \ --clobber diff --git a/.github/workflows/renovate.yml b/.github/workflows/renovate.yml deleted file mode 100644 index e470128c..00000000 --- a/.github/workflows/renovate.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: Run Renovate - -on: - schedule: - - cron: "13 * * * *" - workflow_dispatch: - -jobs: - renovate: - runs-on: antville - - steps: - - uses: actions/checkout@v4 - - - name: Run Renovate - # See - # debug | info | warn | error | fatal - run: LOG_LEVEL=info npx renovate - env: - # Renovate is using this token to retrieve release notes - GITHUB_COM_TOKEN: ${{ secrets.renovate_github_com_token }} - # Autodiscover is better suited for an extra repo running Renovate on all desired repos - #RENOVATE_AUTODISCOVER: 'true' - RENOVATE_CONFIG_FILE: renovate.json - RENOVATE_ENDPOINT: ${{ github.api_url }} - RENOVATE_GIT_AUTHOR: Renovate Bot - #RENOVATE_GIT_IGNORED_AUTHORS: - # - 29139614+renovate[bot]@users.noreply.github.com - RENOVATE_IGNORE_PR_AUTHOR: 'true' - RENOVATE_LOG_FILE: renovate-log.ndjson - RENOVATE_LOG_FILE_LEVEL: debug - RENOVATE_PLATFORM: gitea - RENOVATE_REPOSITORIES: ${{ github.repository }} - RENOVATE_REPOSITORY_CACHE: 'enabled' - # github.token is not working here, it lacks some permissions required by Renovate - RENOVATE_TOKEN: ${{ secrets.renovate_token }} - - - name: Save log file - # FIXME: v4 of this action causes an error on Forgejo (“You must configure a GitHub token”) - uses: actions/upload-artifact@v3 - if: always() - with: - name: renovate-log.ndjson - path: renovate-log.ndjson diff --git a/.github/workflows/stage.yml b/.github/workflows/stage.yml new file mode 100644 index 00000000..177d84b1 --- /dev/null +++ b/.github/workflows/stage.yml @@ -0,0 +1,45 @@ +name: Deploy (Staging) + +on: workflow_dispatch + +jobs: + stage: + runs-on: ubuntu-latest + + environment: + name: stage + url: https://antville-test.click + + steps: + - uses: actions/checkout@v4 + + - name: Set up SSH agent + uses: ./.github/actions/ssh + with: + config: ${{ vars.SSH_CONFIG }} + key: ${{ secrets.SSH_PRIVATE_KEY }} + known-hosts: ${{ vars.SSH_KNOWN_HOSTS }} + + - name: Set up Java + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 21 + + - name: Set up Gradle + uses: gradle/actions/setup-gradle@v3 + + - name: Build with Gradle + run: ./gradlew installDist + + - name: Publish to staging server + run: | + rsync build/install/helma/ antville.dev:/ \ + --verbose --archive --delete --compress \ + --filter 'protect /lib/ext' \ + --filter '+ /launcher.jar' \ + --filter '+ /lib' \ + --filter '- /*' \ + + - name: Restart Helma + run: ssh antville.dev restart diff --git a/.gitignore b/.gitignore index 7bf7bcbc..e9557b6a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,30 +1,22 @@ -# Generally ignore hidden files -.* +.gradle +.idea +.settings +build -# Manage some Codium configuration -.vscode/* -!.vscode -!.vscode/extensions.json -!.vscode/launch.json -!.vscode/settings.json -!.vscode/tasks.json - -# Ignore files created during build or run +/apps /bin /backups -build +/db /docs +/extras /lib /licenses /log - -# Ignore files managed in src/dist -/*.properties -/apps -/db -/extras -/launcher.jar /static -# Manage Gradle configuration +/*.properties +/launcher.jar +/passwd +/start.* + !/gradle.properties diff --git a/.java-version b/.java-version index 98d9bcb7..3b5b5d8f 100644 --- a/.java-version +++ b/.java-version @@ -1 +1 @@ -17 +11.0 \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json index c52f6863..0d95f065 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,6 +1,5 @@ { "recommendations": [ - "vscjava.vscode-java-pack", - "vscjava.vscode-gradle" + "vscjava.vscode-java-pack" ] } diff --git a/.vscode/launch.json b/.vscode/launch.json index b9116072..8310c621 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,21 +1,84 @@ { - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "java", - "name": "Current File", - "request": "launch", - "mainClass": "${file}" - }, - { - "type": "java", - "name": "Debug", - "request": "launch", - "mainClass": "helma.main.Server", - "projectName": "helma_" - } - ] -} + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "java", + "name": "Current File", + "request": "launch", + "mainClass": "${file}" + }, + { + "type": "java", + "name": "ImageInfo", + "request": "launch", + "mainClass": "helma.image.ImageInfo", + "projectName": "helma_" + }, + { + "type": "java", + "name": "CommandlineRunner", + "request": "launch", + "mainClass": "helma.main.CommandlineRunner", + "projectName": "helma_" + }, + { + "type": "java", + "name": "Server", + "request": "launch", + "mainClass": "helma.main.Server", + "projectName": "helma_" + }, + { + "type": "java", + "name": "XmlConverter", + "request": "launch", + "mainClass": "helma.objectmodel.dom.XmlConverter", + "projectName": "helma_" + }, + { + "type": "java", + "name": "Crypt", + "request": "launch", + "mainClass": "helma.util.Crypt", + "projectName": "helma_" + }, + { + "type": "java", + "name": "HtmlEncoder", + "request": "launch", + "mainClass": "helma.util.HtmlEncoder", + "projectName": "helma_" + }, + { + "type": "java", + "name": "Logo", + "request": "launch", + "mainClass": "helma.util.Logo", + "projectName": "helma_" + }, + { + "type": "java", + "name": "MarkdownProcessor", + "request": "launch", + "mainClass": "helma.util.MarkdownProcessor", + "projectName": "helma_" + }, + { + "type": "java", + "name": "Commandline", + "request": "launch", + "mainClass": "helma.main.launcher.Commandline", + "projectName": "launcher" + }, + { + "type": "java", + "name": "Main", + "request": "launch", + "mainClass": "helma.main.launcher.Main", + "projectName": "launcher" + } + ] +} \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md index 65dfb884..df5926e7 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ # License -Copyright (c) 1999-2025 Helma Project. All rights reserved. +Copyright (c) 1999-2008 Helma Project. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions diff --git a/README.md b/README.md index f6438677..2a134485 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ ## TL;DR -- Make sure you have Java 17 or higher installed -- Download and unpack the [latest release](https://code.host.antville.org/antville/helma/releases) +- Make sure you have Java 11 or higher installed +- Download and unpack the [latest release](https://github.com/antville/helma/releases) - Invoke `./bin/helma`, resp. `./bin/helma.bat`, depending on your platform - Direct your web browser to @@ -33,12 +33,10 @@ Helma is built with [Gradle](https://gradle.org), the build task depends on the ### Additional Prerequisites -* [Node.js](https://nodejs.org) LTS version * [Rsync](https://rsync.samba.org) version ≥ 3.1.0 +* [Node.js](https://nodejs.org) LTS version -Clone this repository to your machine and run Helma with `./gradlew run`. - -To update the installation from a build, run `./gradlew update` and enter `yes` at the prompt. +Clone this repository to your machine and start the build process with `./gradlew install`. The build script is going to ask you if you want to update the installation, enter `yes` or `no`. > ⚠️ > Please be aware that this step is going to overwrite files in the installation directory – escpecially at a later time when there might be substantial changes. Should this happen by accident you find the previous installation in the `backups` directory. diff --git a/build.gradle b/build.gradle index cd60e214..e5150e09 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { id 'application' - id 'com.github.jk1.dependency-license-report' version '2.9' + id 'com.github.jk1.dependency-license-report' version '2.7' } import org.apache.tools.ant.filters.FixCrLfFilter @@ -17,9 +17,9 @@ def textFiles = ['**/*.hac', '**/.html', '**/*.js', '**/*.md', '**/*.properties' allprojects { apply plugin: 'java' - java { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 + compileJava { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 } repositories { @@ -27,7 +27,7 @@ allprojects { } } -version = new Date().format("yy.M.d") +version = new Date().format("yyyyMMdd") tasks.build.dependsOn javadoc, 'jsdoc', 'generateLicenseReport' tasks.compileJava.dependsOn 'processSource' @@ -42,6 +42,15 @@ if (JavaVersion.current().isJava8Compatible()) { } } +sourceSets { + main { + java { + // Sources in `src` will be available here after processing + srcDirs = ["$buildDir/src/main/java"] + } + } +} + configurations { // Wrapping implementation because it does not allow access to its files // (i.e. cannot be resolved) @@ -49,18 +58,17 @@ configurations { } dependencies { - implementation 'com.google.code.gson:gson:2.12.1' - implementation 'commons-codec:commons-codec:1.18.0' - implementation 'org.apache.commons:commons-fileupload2-core:2.0.0-M2' - implementation 'org.apache.commons:commons-fileupload2-jakarta:2.0.0-M1' - implementation 'commons-logging:commons-logging:1.3.5' - implementation 'commons-net:commons-net:3.11.1' + implementation 'com.google.code.gson:gson:2.11.0' + implementation 'commons-codec:commons-codec:1.17.0' + implementation 'commons-fileupload:commons-fileupload:1.5' + implementation 'commons-logging:commons-logging:1.3.2' + implementation 'commons-net:commons-net:3.10.0' implementation 'com.sun.mail:javax.mail:1.6.2' - implementation 'jakarta.servlet:jakarta.servlet-api:5.0.0' + implementation 'javax.servlet:javax.servlet-api:4.0.1' implementation 'org.ccil.cowan.tagsoup:tagsoup:1.2.1' - implementation 'org.eclipse.jetty.ee9:jetty-ee9-servlet:12.0.19' - implementation 'org.eclipse.jetty:jetty-xml:12.0.19' - implementation 'org.mozilla:rhino-all:1.8.0' + implementation 'org.eclipse.jetty:jetty-servlet:9.4.54.v20240208' + implementation 'org.eclipse.jetty:jetty-xml:9.4.54.v20240208' + implementation 'org.mozilla:rhino:1.7.13' implementation 'org.sejda.imageio:webp-imageio:0.1.6' implementation 'xerces:xercesImpl:2.12.2' implementation 'xmlrpc:xmlrpc:2.0.1' @@ -70,37 +78,6 @@ def rhinoJar = configurations.library.files.find { jar -> jar.name.startsWith('rhino') } -run { - jvmArgs jettyLogLevel, suppressMacosDockIcon - classpath += fileTree(dir: 'lib/ext', include: '*.jar') -} - -application { - mainClass = 'helma.main.Server' - - applicationDistribution.from(projectDir) { - include 'modules/**' - include 'LICENSE.md' - include 'README.md' - include 'start.*' - } - - applicationDistribution.from(javadoc.destinationDir) { - include '**' - into 'docs/javadoc' - } - - applicationDistribution.from("${project.buildDir}/docs/jsdoc") { - include '**' - into 'docs/jsdoc' - } - - applicationDistribution.from("${project.buildDir}/reports/dependency-license") { - include '**' - into 'licenses' - } -} - startScripts { applicationName = 'helma' classpath = files('../launcher.jar') @@ -126,6 +103,30 @@ distributions { } } +application { + applicationDistribution.from(projectDir) { + include 'modules/**' + include 'LICENSE.md' + include 'README.md' + include 'start.*' + } + + applicationDistribution.from(javadoc.destinationDir) { + include '**' + into 'docs/javadoc' + } + + applicationDistribution.from("${project.buildDir}/docs/jsdoc") { + include '**' + into 'docs/jsdoc' + } + + applicationDistribution.from("${project.buildDir}/reports/dependency-license") { + include '**' + into 'licenses' + } +} + distTar { dependsOn ':generateLicenseReport', ':javadoc', ':jsdoc' @@ -146,35 +147,42 @@ distZip { installDist { dependsOn build + + if (!System.getenv('CI')) { + finalizedBy 'update' + } } -def processSource = tasks.register('processSource', Sync) { +run { + classpath = files('launcher.jar') + jvmArgs jettyLogLevel, suppressMacosDockIcon +} + +task processSource(type: Sync) { + def date = new Date().format("MMMM dd, yyyy") def gitOutput = new ByteArrayOutputStream() - outputs.dir "${project.buildDir}/src" - exec { - commandLine 'git', 'rev-parse', '--short', 'HEAD' + commandLine 'git', 'describe' standardOutput = gitOutput errorOutput = new ByteArrayOutputStream() ignoreExitValue = true } + def description = date + def tag = gitOutput.toString().trim() + + // TODO: Implement extended description in Java code + if (tag) description = "$tag; $description" + from 'src' filter { - line -> line - .replaceAll('__builddate__', new Date().format("d MMM yyyy")) - .replaceAll('__commithash__', gitOutput.toString().trim()) - .replaceAll('__version__', version) - } into outputs.files.singleFile + line -> line.replaceAll('__builddate__', date) + } into "${project.buildDir}/src" } -tasks.compileJava.source = processSource.map { it.outputs.files } - -tasks.register('update') { - dependsOn installDist - +task update { def rsyncArgs = ['--archive', '--filter', '- backups'] def confirm = { @@ -218,7 +226,7 @@ tasks.register('update') { } } -tasks.register('jsdoc', Exec) { +task jsdoc(type: Exec) { description 'Generates JSDoc API documentation for the included JavaScript modules.' group 'Documentation' @@ -232,7 +240,7 @@ tasks.register('jsdoc', Exec) { args = ['jsdoc', '-d', "$destination"].plus(sources) } -tasks.register('xgettext', JavaExec) { +task xgettext(type: JavaExec) { description 'Extracts translatable message strings from source code.' group 'i18n' @@ -249,7 +257,7 @@ tasks.register('xgettext', JavaExec) { ] } -tasks.register('po2js', JavaExec) { +task po2js(type: JavaExec) { description 'Converts translated message strings from PO format to JavaScript.' group 'i18n' @@ -264,7 +272,7 @@ tasks.register('po2js', JavaExec) { ] } -tasks.register('rhinoShell', JavaExec) { +task rhinoShell(type: JavaExec) { description 'Runs the interactive Rhino JavaScript shell.' group 'Application' @@ -276,7 +284,7 @@ tasks.register('rhinoShell', JavaExec) { // Call this task with a function definition using the `-P` parameter, e.g. // `./gradlew commandLine -Pfunction=manage.getAllApplications` -tasks.register('commandLine', JavaExec) { +task commandLine(type: JavaExec) { description 'Runs a function in a Helma application with `-Pfunction=app.functionName`.' group 'Application' @@ -284,11 +292,3 @@ tasks.register('commandLine', JavaExec) { mainClass = 'helma.main.launcher.Commandline' args '-h', projectDir, function } - -tasks.register('debug', JavaExec) { - group = 'application' - main = 'helma.main.Server' - classpath = sourceSets.main.runtimeClasspath - jvmArgs = ['-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005'] - classpath += fileTree(dir: 'lib/ext', include: '*.jar') -} diff --git a/cliff.toml b/cliff.toml deleted file mode 100644 index 755c4ca2..00000000 --- a/cliff.toml +++ /dev/null @@ -1,52 +0,0 @@ -# git-cliff ~ default configuration file -# https://git-cliff.org/docs/configuration -# -# Lines starting with "#" are comments. -# Configuration options are organized into tables and keys. -# See documentation for more information on available options. - -[changelog] -trim = true - -header = "## Changes" - -body = """ -{% for group, commits in commits | filter(attribute="merge_commit") | group_by(attribute="group") %} - ### {{ group | striptags | trim | upper_first }} - {% for commit in commits %} - * [{{ commit.id | split(pat="") | slice(end=11) | join() }}]\ - (https://code.host.antville.org/antville/helma/commit/{{ commit.id }}) \ - {% if commit.breaking %}**Breaking:** {% endif %}\ - {{ commit.message | split(pat="\\n") | first | upper_first | escape }}\ - {% endfor %} -{% endfor %} - -**Full Changelog:** [{{ previous.version }} → {{ version }}]\ -(https://code.host.antville.org/antville/helma/compare/\ -{{ previous.version | urlencode }}..{{ version | urlencode }})\n\n -""" - -footer = """ -Generated by [git-cliff](https://git-cliff.org/). -""" - -[git] -conventional_commits = false -filter_commits = false -filter_unconventional = false -protect_breaking_commits = false -sort_commits = "newest" -split_commits = false -topo_order = false - -commit_parsers = [ - { message = "^Apply \\d+ suggestion", skip = true }, - { message = "^Merge .*(branch|dependabot|dependency|renovate)", skip = true }, - { message = "^Lock file maintenance", skip = true }, - { message = "yarn\\.lock", skip = true }, - - { message = "^[Ff]ix", group = " 🐛 Bug Fixes" }, - { field = "author.name", pattern = "[Rr]enovate|[Dd]ependabot", group = " 📦 Dependency Updates" }, - { message = "^Merge pull request", group = " 🔀 Merges" }, - { message = ".*", group = " Uncategorized" }, -] diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 1b33c55b..e6441136 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 002b867c..a4413138 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 23d15a93..b740cf13 100755 --- a/gradlew +++ b/gradlew @@ -15,8 +15,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # -# SPDX-License-Identifier: Apache-2.0 -# ############################################################################## # @@ -86,7 +84,7 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -114,7 +112,7 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH="\\\"\\\"" +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -205,7 +203,7 @@ fi DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. @@ -213,7 +211,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -classpath "$CLASSPATH" \ - -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + org.gradle.wrapper.GradleWrapperMain \ "$@" # Stop when "xargs" is not available. diff --git a/gradlew.bat b/gradlew.bat index 5eed7ee8..7101f8e4 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,8 +13,6 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem -@rem SPDX-License-Identifier: Apache-2.0 -@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -70,11 +68,11 @@ goto fail :execute @rem Setup the command line -set CLASSPATH= +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%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell diff --git a/lib/ext/.keep b/lib/ext/.keep deleted file mode 100644 index e69de29b..00000000 diff --git a/modules/helma/build.gradle b/modules/helma/build.gradle index 401dd406..6b8c2b5e 100644 --- a/modules/helma/build.gradle +++ b/modules/helma/build.gradle @@ -1,8 +1,8 @@ dependencies { runtimeOnly 'ch.ethz.ganymed:ganymed-ssh2:build209' runtimeOnly 'net.sourceforge.jexcelapi:jxl:2.5.7' - runtimeOnly 'org.apache.lucene:lucene-analyzers:2.2.0' - runtimeOnly 'org.apache.lucene:lucene-core:2.2.0' + runtimeOnly 'org.apache.lucene:lucene-analyzers:2.9.4' + runtimeOnly 'org.apache.lucene:lucene-core:2.9.4' } jar.enabled = false @@ -12,7 +12,7 @@ processResources.enabled = false processTestResources.enabled = false test.enabled = false -tasks.register('deps', Copy) { +task deps(type: Copy) { from sourceSets.main.runtimeClasspath into '.' } diff --git a/modules/jala/build.gradle b/modules/jala/build.gradle index 3b9b6684..e1ef800f 100644 --- a/modules/jala/build.gradle +++ b/modules/jala/build.gradle @@ -15,7 +15,7 @@ processResources.enabled = false processTestResources.enabled = false test.enabled = false -tasks.register('deps', Copy) { +task deps(type: Copy) { from sourceSets.main.runtimeClasspath into 'lib' } diff --git a/modules/jala/util/HopKit/build.gradle b/modules/jala/util/HopKit/build.gradle index f8c9778e..f6b40342 100644 --- a/modules/jala/util/HopKit/build.gradle +++ b/modules/jala/util/HopKit/build.gradle @@ -10,7 +10,7 @@ processResources.enabled = false processTestResources.enabled = false test.enabled = false -tasks.register('deps', Copy) { +task deps(type: Copy) { from sourceSets.main.runtimeClasspath into 'lib' } diff --git a/modules/jala/util/Test/build.gradle b/modules/jala/util/Test/build.gradle index 7e596efa..91015efc 100644 --- a/modules/jala/util/Test/build.gradle +++ b/modules/jala/util/Test/build.gradle @@ -10,7 +10,7 @@ processResources.enabled = false processTestResources.enabled = false test.enabled = false -tasks.register('deps', Copy) { +task deps(type: Copy) { from sourceSets.main.runtimeClasspath into 'code' } diff --git a/settings.gradle b/settings.gradle index 5fd2dc60..c6fe1184 100644 --- a/settings.gradle +++ b/settings.gradle @@ -16,6 +16,3 @@ project(':modules').projectDir = file('modules/helma') project(':jala').projectDir = file('modules/jala') project(':hopKit').projectDir = file('modules/jala/util/HopKit') project(':test').projectDir = file('modules/jala/util/Test') - -// Rename this project to prevent redundancy and renaming of main project (VSC does not care, though) -project(':modules').name = 'modules' diff --git a/src/dist/extras/deploy.sh b/src/dist/extras/deploy.sh deleted file mode 100755 index 869cf945..00000000 --- a/src/dist/extras/deploy.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh - -# Use this script as forced command of an authorized SSH key: -# command="/home/helma/extras/deploy.sh" ssh-ed25519 AAAAC3NzaC… - -case "$SSH_ORIGINAL_COMMAND" in - ping) - echo pong - ;; - - restart) - printf 'Restarting Helma… ' - sudo /bin/systemctl restart helma - printf '%s\n' 'done.' - ;; - - *) - # Allow any rsync command but restrict it to the installation directory - rrsync -wo /home/helma - ;; -esac diff --git a/src/dist/extras/helma.service b/src/dist/extras/helma.service index 7d42310b..308093aa 100644 --- a/src/dist/extras/helma.service +++ b/src/dist/extras/helma.service @@ -18,7 +18,7 @@ ExecStart = /usr/bin/java -server \ -jar launcher.jar \ -w 8080 -x 8081 -ExecReload = /bin/sh -c 'touch apps.properties && touch server.properties' +ExecReload = touch apps.properties && touch server.properties ExecStop = /bin/kill -15 $MAINPID [Install] diff --git a/src/main/java/helma/framework/CookieTrans.java b/src/main/java/helma/framework/CookieTrans.java index 5293b843..fed45960 100644 --- a/src/main/java/helma/framework/CookieTrans.java +++ b/src/main/java/helma/framework/CookieTrans.java @@ -17,7 +17,7 @@ package helma.framework; import java.io.Serializable; -import jakarta.servlet.http.Cookie; +import javax.servlet.http.Cookie; /** * Cookie Transmitter. A simple, serializable representation diff --git a/src/main/java/helma/framework/RequestBean.java b/src/main/java/helma/framework/RequestBean.java index bf377a63..49091022 100644 --- a/src/main/java/helma/framework/RequestBean.java +++ b/src/main/java/helma/framework/RequestBean.java @@ -16,12 +16,12 @@ package helma.framework; -import jakarta.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequest; import java.io.Serializable; import java.util.Map; /** - * + * */ public class RequestBean implements Serializable { private static final long serialVersionUID = -6826881712426326687L; @@ -89,7 +89,7 @@ public class RequestBean implements Serializable { * @return the header value, or null */ public String getHeader(String name) { - return req.getHeader(name); + return req.getHeader(name); } /** diff --git a/src/main/java/helma/framework/RequestTrans.java b/src/main/java/helma/framework/RequestTrans.java index ec8218fe..83665ba8 100644 --- a/src/main/java/helma/framework/RequestTrans.java +++ b/src/main/java/helma/framework/RequestTrans.java @@ -19,9 +19,9 @@ package helma.framework; import helma.util.SystemMap; import helma.util.StringUtils; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.Cookie; import org.apache.commons.codec.binary.Base64; diff --git a/src/main/java/helma/framework/ResponseBean.java b/src/main/java/helma/framework/ResponseBean.java index 061ecb54..c2790a34 100644 --- a/src/main/java/helma/framework/ResponseBean.java +++ b/src/main/java/helma/framework/ResponseBean.java @@ -19,7 +19,7 @@ package helma.framework; import helma.objectmodel.db.Transactor; import helma.scripting.ScriptingException; -import jakarta.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpServletResponse; import java.io.Serializable; import java.io.StringWriter; import java.io.PrintWriter; diff --git a/src/main/java/helma/framework/ResponseTrans.java b/src/main/java/helma/framework/ResponseTrans.java index 239487d7..af65a4c1 100644 --- a/src/main/java/helma/framework/ResponseTrans.java +++ b/src/main/java/helma/framework/ResponseTrans.java @@ -21,7 +21,7 @@ import helma.framework.core.Application; import helma.util.*; import helma.scripting.ScriptingException; -import jakarta.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpServletResponse; import java.io.*; import java.security.*; import java.util.*; diff --git a/src/main/java/helma/main/ApplicationManager.java b/src/main/java/helma/main/ApplicationManager.java index e699a8d8..2deb88ee 100644 --- a/src/main/java/helma/main/ApplicationManager.java +++ b/src/main/java/helma/main/ApplicationManager.java @@ -19,13 +19,11 @@ import java.util.Vector; import org.apache.commons.logging.Log; import org.apache.xmlrpc.XmlRpcHandler; - import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ContextHandlerCollection; import org.eclipse.jetty.server.handler.ResourceHandler; -import org.eclipse.jetty.ee9.servlet.ServletContextHandler; -import org.eclipse.jetty.ee9.servlet.ServletHolder; -import org.eclipse.jetty.util.resource.ResourceFactory; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; import helma.framework.core.Application; import helma.framework.repository.FileRepository; @@ -483,29 +481,23 @@ public class ApplicationManager implements XmlRpcHandler { // if there is a static direcory specified, mount it if (this.staticDir != null) { - String staticPath = getAbsoluteFile(this.staticDir).getCanonicalPath(); - getLogger().info("Serving static from " + staticPath); + File staticContent = getAbsoluteFile(this.staticDir); + + getLogger().info("Serving static from " + staticContent.getPath()); getLogger().info("Mounting static at " + staticMountpoint); ResourceHandler rhandler = new ResourceHandler(); - rhandler.setBaseResource(ResourceFactory.of(rhandler).newResource(staticPath)); + rhandler.setResourceBase(staticContent.getPath()); rhandler.setWelcomeFiles(staticHome); - ContextHandler staticContext = new ContextHandler(); - staticContext.setContextPath(staticMountpoint); + staticContext = ApplicationManager.this.context.addContext(staticMountpoint, ""); //$NON-NLS-1$ staticContext.setHandler(rhandler); - ApplicationManager.this.context.addHandler(staticContext); staticContext.start(); } - // I hope I am correct assuming Helma does not need Jetty’s session management, but using - // `ServletContextHandler.SESSIONS` causes an exception: Shared scheduler not started - appContext = new ServletContextHandler(ServletContextHandler.NO_SESSIONS); - appContext.setContextPath(pathPattern); - context.addHandler(appContext); - + appContext = new ServletContextHandler(context, pathPattern, true, true); Class servletClass = servletClassName == null ? EmbeddedServletClient.class : Class.forName(servletClassName); ServletHolder holder = new ServletHolder(servletClass); @@ -537,9 +529,10 @@ public class ApplicationManager implements XmlRpcHandler { } if (protectedStaticDir != null) { - String protectedContent = getAbsoluteFile(protectedStaticDir).getCanonicalPath(); - appContext.setBaseResourceAsString(protectedContent); - getLogger().info("Serving protected static from " + protectedContent); + File protectedContent = getAbsoluteFile(protectedStaticDir); + appContext.setResourceBase(protectedContent.getPath()); + getLogger().info("Serving protected static from " + + protectedContent.getPath()); } // Remap the context paths and start @@ -563,9 +556,7 @@ public class ApplicationManager implements XmlRpcHandler { // unbind from Jetty HTTP server if (ApplicationManager.this.jetty != null) { if (this.appContext != null) { - // Adding appContext to the ContextHandlerCollection works (see above) but removing it causes an exception of - // incompatible types: ServletContextHandler cannot be converted to Handler - //ApplicationManager.this.context.removeHandler(this.appContext); + ApplicationManager.this.context.removeHandler(this.appContext); this.appContext.stop(); this.appContext.destroy(); this.appContext = null; diff --git a/src/main/java/helma/main/JettyServer.java b/src/main/java/helma/main/JettyServer.java index 83e09bcf..88ba8c10 100644 --- a/src/main/java/helma/main/JettyServer.java +++ b/src/main/java/helma/main/JettyServer.java @@ -16,12 +16,11 @@ package helma.main; + import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.ServerConnector; -import org.eclipse.jetty.util.resource.Resource; -import org.eclipse.jetty.util.resource.URLResourceFactory; import org.eclipse.jetty.xml.XmlConfiguration; import java.net.URL; @@ -37,20 +36,18 @@ public class JettyServer { public static JettyServer init(Server server, ServerConfig config) throws IOException { File configFile = config.getConfigFile(); if (configFile != null && configFile.exists()) { - URLResourceFactory resourceFactory = new URLResourceFactory(); - Resource resource = resourceFactory.newResource(configFile.toURI()); - return new JettyServer(resource); + return new JettyServer(configFile.toURI().toURL()); } else if (config.hasWebsrvPort()) { return new JettyServer(config.getWebsrvPort(), server); } return null; } - private JettyServer(Resource resource) throws IOException { + private JettyServer(URL url) throws IOException { http = new org.eclipse.jetty.server.Server(); try { - XmlConfiguration config = new XmlConfiguration(resource); + XmlConfiguration config = new XmlConfiguration(url); config.configure(http); } catch (IOException e) { @@ -62,7 +59,7 @@ public class JettyServer { private JettyServer(InetSocketAddress webPort, Server server) throws IOException { - + http = new org.eclipse.jetty.server.Server(); // start embedded web server if port is specified @@ -76,6 +73,7 @@ public class JettyServer { connector.setHost(webPort.getAddress().getHostAddress()); connector.setPort(webPort.getPort()); connector.setIdleTimeout(30000); + connector.setSoLingerTime(-1); connector.setAcceptorPriorityDelta(0); connector.setAcceptQueueSize(0); @@ -102,13 +100,12 @@ public class JettyServer { } private void openListeners() throws IOException { - // opening the listener here allows us to run on privileged port 80 under jsvc + // opening the listener here allows us to run on priviledged port 80 under jsvc // even as non-root user, because init() is called with root privileges // while start() will be called with the user we will actually run as - for (var connector : http.getConnectors()) { - if (connector instanceof ServerConnector) { - ((ServerConnector) connector).open(); - } + Connector[] connectors = http.getConnectors(); + for (int i = 0; i < connectors.length; i++) { + ((ServerConnector) connectors[i]).open(); } } } diff --git a/src/main/java/helma/main/Server.java b/src/main/java/helma/main/Server.java index d951adae..4966551c 100644 --- a/src/main/java/helma/main/Server.java +++ b/src/main/java/helma/main/Server.java @@ -21,7 +21,6 @@ import helma.framework.repository.FileResource; import helma.framework.core.*; import helma.objectmodel.db.DbSource; import helma.util.*; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.xmlrpc.*; @@ -37,13 +36,7 @@ import helma.util.ResourceProperties; */ public class Server implements Runnable { // version string - public static final String version = "__version__"; - - // build date - public static final String buildDate = "__builddate__"; - - // commit hash - public static final String commitHash = "__commithash__"; + public static final String version = "🐜 (__builddate__)"; // static server instance private static Server server; @@ -150,13 +143,17 @@ public class Server implements Runnable { * check if we are running on a Java 2 VM - otherwise exit with an error message */ public static void checkJavaVersion() { - String javaVersion = System.getProperty("java.version", "0"); - int majorVersion = Integer.parseInt(javaVersion.split("\\.")[0]); + String javaVersion = System.getProperty("java.version"); - if (majorVersion < 17) { - System.err.println("This version of Helma requires Java 17 or greater."); + if ((javaVersion == null) || javaVersion.startsWith("1.5") + || javaVersion.startsWith("1.4") + || javaVersion.startsWith("1.3") + || javaVersion.startsWith("1.2") + || javaVersion.startsWith("1.1") + || javaVersion.startsWith("1.0")) { + System.err.println("This version of Helma requires Java 1.6 or greater."); - if (majorVersion == 0) { // don't think this will ever happen, but you never know + if (javaVersion == null) { // don't think this will ever happen, but you never know System.err.println("Your Java Runtime did not provide a version number. Please update to a more recent version."); } else { System.err.println("Your Java Runtime is version " + javaVersion + diff --git a/src/main/java/helma/servlet/AbstractServletClient.java b/src/main/java/helma/servlet/AbstractServletClient.java index 546a0698..3e3b3f59 100644 --- a/src/main/java/helma/servlet/AbstractServletClient.java +++ b/src/main/java/helma/servlet/AbstractServletClient.java @@ -22,29 +22,18 @@ package helma.servlet; import helma.framework.*; import helma.framework.core.Application; import helma.util.*; - import java.io.*; -import java.nio.charset.Charset; -import java.nio.charset.UnsupportedCharsetException; - +import java.util.*; import java.security.SecureRandom; import java.security.NoSuchAlgorithmException; -import java.util.*; - -import jakarta.servlet.*; -import jakarta.servlet.http.*; +import javax.servlet.*; +import javax.servlet.http.*; import org.apache.commons.codec.binary.Base64; - -import org.apache.commons.fileupload2.core.DiskFileItemFactory; -import org.apache.commons.fileupload2.core.FileItem; -import org.apache.commons.fileupload2.core.FileUploadException; -import org.apache.commons.fileupload2.core.FileUploadSizeException; -import org.apache.commons.fileupload2.core.ProgressListener; - -import org.apache.commons.fileupload2.jakarta.JakartaServletDiskFileUpload; -import org.apache.commons.fileupload2.jakarta.JakartaServletFileUpload; -import org.apache.commons.fileupload2.jakarta.JakartaServletRequestContext; +import org.apache.commons.fileupload.disk.DiskFileItemFactory; +import org.apache.commons.fileupload.*; +import org.apache.commons.fileupload.servlet.ServletFileUpload; +import org.apache.commons.fileupload.servlet.ServletRequestContext; /** * This is an abstract Hop servlet adapter. This class communicates with hop applications @@ -229,9 +218,9 @@ public abstract class AbstractServletClient extends HttpServlet { // read file uploads List uploads = null; - JakartaServletRequestContext reqcx = new JakartaServletRequestContext(request); + ServletRequestContext reqcx = new ServletRequestContext(request); - if (JakartaServletFileUpload.isMultipartContent(reqcx)) { + if (ServletFileUpload.isMultipartContent(reqcx)) { // get session for upload progress monitoring UploadStatus uploadStatus = getApplication().getUploadStatus(reqtrans); try { @@ -239,7 +228,7 @@ public abstract class AbstractServletClient extends HttpServlet { } catch (Exception upx) { log("Error in file upload", upx); String message; - boolean tooLarge = (upx instanceof FileUploadSizeException); + boolean tooLarge = (upx instanceof FileUploadBase.SizeLimitExceededException); if (tooLarge) { message = "File upload size exceeds limit of " + uploadLimit + " kB"; } else { @@ -664,12 +653,12 @@ public abstract class AbstractServletClient extends HttpServlet { map.put(name, newValues); } - protected List parseUploads(JakartaServletRequestContext reqcx, RequestTrans reqtrans, + protected List parseUploads(ServletRequestContext reqcx, RequestTrans reqtrans, final UploadStatus uploadStatus, String encoding) - throws FileUploadException, UnsupportedCharsetException, IOException { + throws FileUploadException, UnsupportedEncodingException { // handle file upload - DiskFileItemFactory factory = DiskFileItemFactory.builder().get(); - JakartaServletFileUpload upload = new JakartaServletFileUpload(factory); + DiskFileItemFactory factory = new DiskFileItemFactory(); + FileUpload upload = new FileUpload(factory); // use upload limit for individual file size, but also set a limit on overall size upload.setFileSizeMax(uploadLimit * 1024); upload.setSizeMax(totalUploadLimit * 1024); @@ -692,7 +681,7 @@ public abstract class AbstractServletClient extends HttpServlet { Object value; // check if this is an ordinary HTML form element or a file upload if (item.isFormField()) { - value = item.getString(Charset.forName(encoding)); + value = item.getString(encoding); } else { value = new MimePart(item); } diff --git a/src/main/java/helma/servlet/EmbeddedServletClient.java b/src/main/java/helma/servlet/EmbeddedServletClient.java index 9f9a37cc..80b3cd6e 100644 --- a/src/main/java/helma/servlet/EmbeddedServletClient.java +++ b/src/main/java/helma/servlet/EmbeddedServletClient.java @@ -19,7 +19,7 @@ package helma.servlet; import helma.framework.*; import helma.framework.core.Application; import helma.main.*; -import jakarta.servlet.*; +import javax.servlet.*; /** * Servlet client that runs a Helma application for the embedded diff --git a/src/main/java/helma/servlet/StandaloneServletClient.java b/src/main/java/helma/servlet/StandaloneServletClient.java index bdfeeeae..5e1af952 100644 --- a/src/main/java/helma/servlet/StandaloneServletClient.java +++ b/src/main/java/helma/servlet/StandaloneServletClient.java @@ -23,7 +23,7 @@ import helma.main.ServerConfig; import helma.main.Server; import java.io.*; -import jakarta.servlet.*; +import javax.servlet.*; import java.util.*; /** @@ -98,7 +98,7 @@ public final class StandaloneServletClient extends AbstractServletClient { repositoryImpl = "helma.framework.repository.FileRepository"; } } - + try { Repository newRepository = (Repository) Class.forName(repositoryImpl) .getConstructor(parameters) @@ -116,7 +116,7 @@ public final class StandaloneServletClient extends AbstractServletClient { } } } - + // add app dir FileRepository appRep = new FileRepository(appDir); log("adding repository: " + appDir); diff --git a/src/main/java/helma/util/MimePart.java b/src/main/java/helma/util/MimePart.java index e4ed29d8..f69f1d61 100644 --- a/src/main/java/helma/util/MimePart.java +++ b/src/main/java/helma/util/MimePart.java @@ -16,7 +16,7 @@ package helma.util; -import org.apache.commons.fileupload2.core.FileItem; +import org.apache.commons.fileupload.FileItem; import java.io.*; import java.util.Date; @@ -238,7 +238,7 @@ public class MimePart implements Serializable { file = new File(base, filename); if (fileItem != null) { - fileItem.write(file.toPath()); + fileItem.write(file); // null out fileItem, since calling write() may have moved the temp file fileItem = null; } else { @@ -249,7 +249,7 @@ public class MimePart implements Serializable { // return file name return filename; } catch (Exception x) { - System.err.println("Error in MimePart.writeToFile(): " + x); + System.err.println("Error in MimePart.writeToFile(): " + x); return null; } }