From 1cf738767c2265dca702bf9db2efd925f9ab2c7c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 18 May 2024 15:30:36 +0000 Subject: [PATCH 01/42] Update Jetty packages --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 7c861d34..64281785 100644 --- a/build.gradle +++ b/build.gradle @@ -66,8 +66,8 @@ dependencies { implementation 'com.sun.mail:javax.mail:1.6.2' implementation 'javax.servlet:javax.servlet-api:4.0.1' implementation 'org.ccil.cowan.tagsoup:tagsoup:1.2.1' - implementation 'org.eclipse.jetty:jetty-servlet:9.4.54.v20240208' - implementation 'org.eclipse.jetty:jetty-xml:9.4.54.v20240208' + implementation 'org.eclipse.jetty:jetty-servlet:11.0.21' + implementation 'org.eclipse.jetty:jetty-xml:12.0.9' implementation 'org.mozilla:rhino:1.7.13' implementation 'org.sejda.imageio:webp-imageio:0.1.6' implementation 'xerces:xercesImpl:2.12.2' From ed575bc4c5f5e047ec50642666343398db509507 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Sun, 19 May 2024 02:05:49 +0200 Subject: [PATCH 02/42] Update README.md Minor edits --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a735855f..2a134485 100644 --- a/README.md +++ b/README.md @@ -34,9 +34,9 @@ Helma is built with [Gradle](https://gradle.org), the build task depends on the ### Additional Prerequisites * [Rsync](https://rsync.samba.org) version ≥ 3.1.0 -* [NodeJS](https://nodejs.org) LTS version +* [Node.js](https://nodejs.org) LTS version -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 `y`. +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. From c087cb731eef411e278b65ed0c75ef9743cd7810 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Sat, 25 May 2024 14:11:15 +0200 Subject: [PATCH 03/42] Use correct version of Jetty servlet --- build.gradle | 2 +- src/main/java/helma/main/ApplicationManager.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 68e45638..9cf24ba9 100644 --- a/build.gradle +++ b/build.gradle @@ -66,7 +66,7 @@ dependencies { implementation 'com.sun.mail:javax.mail:1.6.2' implementation 'javax.servlet:javax.servlet-api:4.0.1' implementation 'org.ccil.cowan.tagsoup:tagsoup:1.2.1' - implementation 'org.eclipse.jetty:jetty-servlet:11.0.21' + implementation 'org.eclipse.jetty.ee9:jetty-ee9-servlet:12.0.9' implementation 'org.eclipse.jetty:jetty-xml:12.0.9' implementation 'org.mozilla:rhino:1.7.13' implementation 'org.sejda.imageio:webp-imageio:0.1.6' diff --git a/src/main/java/helma/main/ApplicationManager.java b/src/main/java/helma/main/ApplicationManager.java index 2deb88ee..edce8483 100644 --- a/src/main/java/helma/main/ApplicationManager.java +++ b/src/main/java/helma/main/ApplicationManager.java @@ -22,8 +22,8 @@ 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.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee9.servlet.ServletContextHandler; +import org.eclipse.jetty.ee9.servlet.ServletHolder; import helma.framework.core.Application; import helma.framework.repository.FileRepository; From a943124d45ce51c8c4be9b39fb9bc937e45312da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Sat, 25 May 2024 15:04:17 +0200 Subject: [PATCH 04/42] Fix creating Jetty server from config file --- src/main/java/helma/main/JettyServer.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/java/helma/main/JettyServer.java b/src/main/java/helma/main/JettyServer.java index 88ba8c10..cfd30264 100644 --- a/src/main/java/helma/main/JettyServer.java +++ b/src/main/java/helma/main/JettyServer.java @@ -16,11 +16,12 @@ 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; @@ -36,18 +37,20 @@ public class JettyServer { public static JettyServer init(Server server, ServerConfig config) throws IOException { File configFile = config.getConfigFile(); if (configFile != null && configFile.exists()) { - return new JettyServer(configFile.toURI().toURL()); + URLResourceFactory resourceFactory = new URLResourceFactory(); + Resource resource = resourceFactory.newResource(configFile.toURI()); + return new JettyServer(resource); } else if (config.hasWebsrvPort()) { return new JettyServer(config.getWebsrvPort(), server); } return null; } - private JettyServer(URL url) throws IOException { + private JettyServer(Resource resource) throws IOException { http = new org.eclipse.jetty.server.Server(); try { - XmlConfiguration config = new XmlConfiguration(url); + XmlConfiguration config = new XmlConfiguration(resource); config.configure(http); } catch (IOException e) { @@ -59,7 +62,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 From 26975a109a0a13771eb3e955ceca930de9e10103 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Sat, 25 May 2024 15:07:22 +0200 Subject: [PATCH 05/42] Method ServerConnector.setSoLingerTime() was removed --- src/main/java/helma/main/JettyServer.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/helma/main/JettyServer.java b/src/main/java/helma/main/JettyServer.java index cfd30264..c28be40a 100644 --- a/src/main/java/helma/main/JettyServer.java +++ b/src/main/java/helma/main/JettyServer.java @@ -76,7 +76,6 @@ public class JettyServer { connector.setHost(webPort.getAddress().getHostAddress()); connector.setPort(webPort.getPort()); connector.setIdleTimeout(30000); - connector.setSoLingerTime(-1); connector.setAcceptorPriorityDelta(0); connector.setAcceptQueueSize(0); From 62630ae0687524f743b8332af59c412dad480eb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Sat, 25 May 2024 15:08:17 +0200 Subject: [PATCH 06/42] Modernize for loop --- src/main/java/helma/main/JettyServer.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/helma/main/JettyServer.java b/src/main/java/helma/main/JettyServer.java index c28be40a..83e09bcf 100644 --- a/src/main/java/helma/main/JettyServer.java +++ b/src/main/java/helma/main/JettyServer.java @@ -102,12 +102,13 @@ public class JettyServer { } private void openListeners() throws IOException { - // opening the listener here allows us to run on priviledged port 80 under jsvc + // opening the listener here allows us to run on privileged 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 - Connector[] connectors = http.getConnectors(); - for (int i = 0; i < connectors.length; i++) { - ((ServerConnector) connectors[i]).open(); + for (var connector : http.getConnectors()) { + if (connector instanceof ServerConnector) { + ((ServerConnector) connector).open(); + } } } } From 0165e7c80ee63a5415f7a8c28e8ef771e9bb7d53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Sat, 25 May 2024 15:17:43 +0200 Subject: [PATCH 07/42] Fix setup of static resource --- src/main/java/helma/main/ApplicationManager.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/java/helma/main/ApplicationManager.java b/src/main/java/helma/main/ApplicationManager.java index edce8483..c56ccce0 100644 --- a/src/main/java/helma/main/ApplicationManager.java +++ b/src/main/java/helma/main/ApplicationManager.java @@ -19,11 +19,13 @@ 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 helma.framework.core.Application; import helma.framework.repository.FileRepository; @@ -481,19 +483,21 @@ public class ApplicationManager implements XmlRpcHandler { // if there is a static direcory specified, mount it if (this.staticDir != null) { + String staticPath = getAbsoluteFile(this.staticDir).getPath(); - File staticContent = getAbsoluteFile(this.staticDir); - - getLogger().info("Serving static from " + staticContent.getPath()); + getLogger().info("Serving static from " + staticPath); getLogger().info("Mounting static at " + staticMountpoint); ResourceHandler rhandler = new ResourceHandler(); - rhandler.setResourceBase(staticContent.getPath()); + rhandler.setBaseResource(ResourceFactory.of(rhandler).newResource(staticPath)); rhandler.setWelcomeFiles(staticHome); - staticContext = ApplicationManager.this.context.addContext(staticMountpoint, ""); //$NON-NLS-1$ + ContextHandler staticContext = new ContextHandler(); + staticContext.setContextPath(staticMountpoint); + staticContext.setBaseResourceAsString(""); staticContext.setHandler(rhandler); + ApplicationManager.this.context.addHandler(staticContext); staticContext.start(); } From 304ea4e456f5e585b66bda940fb58708df5f3cb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Sat, 25 May 2024 15:21:56 +0200 Subject: [PATCH 08/42] Fix creation of app context --- src/main/java/helma/main/ApplicationManager.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/helma/main/ApplicationManager.java b/src/main/java/helma/main/ApplicationManager.java index c56ccce0..78f66623 100644 --- a/src/main/java/helma/main/ApplicationManager.java +++ b/src/main/java/helma/main/ApplicationManager.java @@ -501,7 +501,10 @@ public class ApplicationManager implements XmlRpcHandler { staticContext.start(); } - appContext = new ServletContextHandler(context, pathPattern, true, true); + appContext = new ServletContextHandler(ServletContextHandler.SESSIONS); + appContext.setContextPath(pathPattern); + context.addHandler(appContext); + Class servletClass = servletClassName == null ? EmbeddedServletClient.class : Class.forName(servletClassName); ServletHolder holder = new ServletHolder(servletClass); From fa59a2785858b4b77c14a7c789c2e5ede21c68c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Sat, 25 May 2024 15:24:28 +0200 Subject: [PATCH 09/42] Fix creation of protected context --- src/main/java/helma/main/ApplicationManager.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/helma/main/ApplicationManager.java b/src/main/java/helma/main/ApplicationManager.java index 78f66623..6bb1eb58 100644 --- a/src/main/java/helma/main/ApplicationManager.java +++ b/src/main/java/helma/main/ApplicationManager.java @@ -536,10 +536,9 @@ public class ApplicationManager implements XmlRpcHandler { } if (protectedStaticDir != null) { - File protectedContent = getAbsoluteFile(protectedStaticDir); - appContext.setResourceBase(protectedContent.getPath()); - getLogger().info("Serving protected static from " + - protectedContent.getPath()); + String protectedContent = getAbsoluteFile(protectedStaticDir).getPath(); + appContext.setBaseResourceAsString(protectedContent); + getLogger().info("Serving protected static from " + protectedContent); } // Remap the context paths and start From d67d0235bd11dcdd13f0b3dcea8e0d3cadd6d90e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Sat, 25 May 2024 15:25:41 +0200 Subject: [PATCH 10/42] Prevent java.lang.IllegalStateException: Shared scheduler not started --- src/main/java/helma/main/ApplicationManager.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/helma/main/ApplicationManager.java b/src/main/java/helma/main/ApplicationManager.java index 6bb1eb58..755b65b8 100644 --- a/src/main/java/helma/main/ApplicationManager.java +++ b/src/main/java/helma/main/ApplicationManager.java @@ -543,7 +543,9 @@ public class ApplicationManager implements XmlRpcHandler { // Remap the context paths and start ApplicationManager.this.context.mapContexts(); - this.appContext.start(); + // FIXME: Causing java.lang.IllegalStateException: Shared scheduler not started + // Is it necessary, anway? + //this.appContext.start(); } // register as XML-RPC handler From 441d952b3502949862f2beeea75a2262934ba959 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Sat, 25 May 2024 15:26:18 +0200 Subject: [PATCH 11/42] Prevent incompatible types: ServletContextHandler cannot be converted to Handler --- src/main/java/helma/main/ApplicationManager.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/helma/main/ApplicationManager.java b/src/main/java/helma/main/ApplicationManager.java index 755b65b8..2c9c357c 100644 --- a/src/main/java/helma/main/ApplicationManager.java +++ b/src/main/java/helma/main/ApplicationManager.java @@ -564,7 +564,9 @@ public class ApplicationManager implements XmlRpcHandler { // unbind from Jetty HTTP server if (ApplicationManager.this.jetty != null) { if (this.appContext != null) { - ApplicationManager.this.context.removeHandler(this.appContext); + // FIXME: Causing incompatible types: ServletContextHandler cannot be converted to Handler + // Is it necessary, anyway? + //ApplicationManager.this.context.removeHandler(this.appContext); this.appContext.stop(); this.appContext.destroy(); this.appContext = null; From 973b3493cb996f4501e6755e2dd20eb8a89f1ac7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Sat, 25 May 2024 15:30:10 +0200 Subject: [PATCH 12/42] Prevent java.lang.IllegalArgumentException: Resource String is invalid --- src/main/java/helma/main/ApplicationManager.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/helma/main/ApplicationManager.java b/src/main/java/helma/main/ApplicationManager.java index 2c9c357c..957eb7fe 100644 --- a/src/main/java/helma/main/ApplicationManager.java +++ b/src/main/java/helma/main/ApplicationManager.java @@ -494,7 +494,8 @@ public class ApplicationManager implements XmlRpcHandler { ContextHandler staticContext = new ContextHandler(); staticContext.setContextPath(staticMountpoint); - staticContext.setBaseResourceAsString(""); + // FIXME: Causing java.lang.IllegalArgumentException: Resource String is invalid + //staticContext.setBaseResourceAsString(""); staticContext.setHandler(rhandler); ApplicationManager.this.context.addHandler(staticContext); From 3dbcd792a6319d16b97548aca462d5b9b5a45e4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Sat, 25 May 2024 15:44:59 +0200 Subject: [PATCH 13/42] Migrate to Jakarte servlet API 5 --- build.gradle | 2 +- src/main/java/helma/framework/CookieTrans.java | 2 +- src/main/java/helma/framework/RequestBean.java | 6 +++--- src/main/java/helma/framework/RequestTrans.java | 6 +++--- src/main/java/helma/framework/ResponseBean.java | 2 +- src/main/java/helma/framework/ResponseTrans.java | 2 +- src/main/java/helma/servlet/AbstractServletClient.java | 10 ++++++---- src/main/java/helma/servlet/EmbeddedServletClient.java | 2 +- .../java/helma/servlet/StandaloneServletClient.java | 6 +++--- 9 files changed, 20 insertions(+), 18 deletions(-) diff --git a/build.gradle b/build.gradle index 9cf24ba9..700545c0 100644 --- a/build.gradle +++ b/build.gradle @@ -64,7 +64,7 @@ dependencies { 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 'javax.servlet:javax.servlet-api:4.0.1' + implementation 'jakarta.servlet:jakarta.servlet-api:5.0.0' implementation 'org.ccil.cowan.tagsoup:tagsoup:1.2.1' implementation 'org.eclipse.jetty.ee9:jetty-ee9-servlet:12.0.9' implementation 'org.eclipse.jetty:jetty-xml:12.0.9' diff --git a/src/main/java/helma/framework/CookieTrans.java b/src/main/java/helma/framework/CookieTrans.java index fed45960..5293b843 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 javax.servlet.http.Cookie; +import jakarta.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 49091022..bf377a63 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 javax.servlet.http.HttpServletRequest; +import jakarta.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 83665ba8..ec8218fe 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 javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.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 71688a96..a1a8c9fa 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 javax.servlet.http.HttpServletResponse; +import jakarta.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 583b41b1..28fc3e65 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 javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletResponse; import java.io.*; import java.security.*; import java.util.*; diff --git a/src/main/java/helma/servlet/AbstractServletClient.java b/src/main/java/helma/servlet/AbstractServletClient.java index 77284078..561405b0 100644 --- a/src/main/java/helma/servlet/AbstractServletClient.java +++ b/src/main/java/helma/servlet/AbstractServletClient.java @@ -22,12 +22,14 @@ package helma.servlet; import helma.framework.*; import helma.framework.core.Application; import helma.util.*; + import java.io.*; import java.util.*; import java.security.SecureRandom; import java.security.NoSuchAlgorithmException; -import javax.servlet.*; -import javax.servlet.http.*; + +import jakarta.servlet.*; +import jakarta.servlet.http.*; import org.apache.commons.codec.binary.Base64; import org.apache.commons.fileupload.disk.DiskFileItemFactory; @@ -218,7 +220,7 @@ public abstract class AbstractServletClient extends HttpServlet { // read file uploads List uploads = null; - ServletRequestContext reqcx = new ServletRequestContext(request); + JakartaServletRequestContext reqcx = new JakartaServletRequestContext(request); if (ServletFileUpload.isMultipartContent(reqcx)) { // get session for upload progress monitoring @@ -653,7 +655,7 @@ public abstract class AbstractServletClient extends HttpServlet { map.put(name, newValues); } - protected List parseUploads(ServletRequestContext reqcx, RequestTrans reqtrans, + protected List parseUploads(JakartaServletRequestContext reqcx, RequestTrans reqtrans, final UploadStatus uploadStatus, String encoding) throws FileUploadException, UnsupportedEncodingException { // handle file upload diff --git a/src/main/java/helma/servlet/EmbeddedServletClient.java b/src/main/java/helma/servlet/EmbeddedServletClient.java index 80b3cd6e..9f9a37cc 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 javax.servlet.*; +import jakarta.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 5e1af952..bdfeeeae 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 javax.servlet.*; +import jakarta.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); From 90e45c91158978e649cad78ef02fdb3a51e35199 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Sat, 25 May 2024 15:46:19 +0200 Subject: [PATCH 14/42] Migrate to Apache file upload API 2 w/ Jakarta --- build.gradle | 3 +- .../helma/servlet/AbstractServletClient.java | 31 ++++++++++++------- src/main/java/helma/util/MimePart.java | 6 ++-- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/build.gradle b/build.gradle index 700545c0..0260b537 100644 --- a/build.gradle +++ b/build.gradle @@ -60,7 +60,8 @@ configurations { dependencies { implementation 'com.google.code.gson:gson:2.10.1' implementation 'commons-codec:commons-codec:1.17.0' - implementation 'commons-fileupload:commons-fileupload:1.5' + 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.2' implementation 'commons-net:commons-net:3.10.0' implementation 'com.sun.mail:javax.mail:1.6.2' diff --git a/src/main/java/helma/servlet/AbstractServletClient.java b/src/main/java/helma/servlet/AbstractServletClient.java index 561405b0..52d1a91d 100644 --- a/src/main/java/helma/servlet/AbstractServletClient.java +++ b/src/main/java/helma/servlet/AbstractServletClient.java @@ -24,18 +24,27 @@ import helma.framework.core.Application; import helma.util.*; import java.io.*; -import java.util.*; +import java.nio.charset.Charset; +import java.nio.charset.UnsupportedCharsetException; + import java.security.SecureRandom; import java.security.NoSuchAlgorithmException; +import java.util.*; import jakarta.servlet.*; import jakarta.servlet.http.*; import org.apache.commons.codec.binary.Base64; -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; + +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; /** * This is an abstract Hop servlet adapter. This class communicates with hop applications @@ -222,7 +231,7 @@ public abstract class AbstractServletClient extends HttpServlet { List uploads = null; JakartaServletRequestContext reqcx = new JakartaServletRequestContext(request); - if (ServletFileUpload.isMultipartContent(reqcx)) { + if (JakartaServletFileUpload.isMultipartContent(reqcx)) { // get session for upload progress monitoring UploadStatus uploadStatus = getApplication().getUploadStatus(reqtrans); try { @@ -230,7 +239,7 @@ public abstract class AbstractServletClient extends HttpServlet { } catch (Exception upx) { log("Error in file upload", upx); String message; - boolean tooLarge = (upx instanceof FileUploadBase.SizeLimitExceededException); + boolean tooLarge = (upx instanceof FileUploadSizeException); if (tooLarge) { message = "File upload size exceeds limit of " + uploadLimit + " kB"; } else { @@ -657,10 +666,10 @@ public abstract class AbstractServletClient extends HttpServlet { protected List parseUploads(JakartaServletRequestContext reqcx, RequestTrans reqtrans, final UploadStatus uploadStatus, String encoding) - throws FileUploadException, UnsupportedEncodingException { + throws FileUploadException, UnsupportedCharsetException, IOException { // handle file upload - DiskFileItemFactory factory = new DiskFileItemFactory(); - FileUpload upload = new FileUpload(factory); + DiskFileItemFactory factory = DiskFileItemFactory.builder().get(); + JakartaServletFileUpload upload = new JakartaServletFileUpload(factory); // use upload limit for individual file size, but also set a limit on overall size upload.setFileSizeMax(uploadLimit * 1024); upload.setSizeMax(totalUploadLimit * 1024); @@ -683,7 +692,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(encoding); + value = item.getString(Charset.forName(encoding)); } else { value = new MimePart(item); } diff --git a/src/main/java/helma/util/MimePart.java b/src/main/java/helma/util/MimePart.java index f69f1d61..e4ed29d8 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.fileupload.FileItem; +import org.apache.commons.fileupload2.core.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); + fileItem.write(file.toPath()); // 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; } } From 79d83389f21ef949eb7113bfef28206836707d3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Sat, 25 May 2024 16:16:04 +0200 Subject: [PATCH 15/42] Use canonical paths when creating static contexts --- src/main/java/helma/main/ApplicationManager.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/helma/main/ApplicationManager.java b/src/main/java/helma/main/ApplicationManager.java index 957eb7fe..7d31b764 100644 --- a/src/main/java/helma/main/ApplicationManager.java +++ b/src/main/java/helma/main/ApplicationManager.java @@ -483,7 +483,7 @@ public class ApplicationManager implements XmlRpcHandler { // if there is a static direcory specified, mount it if (this.staticDir != null) { - String staticPath = getAbsoluteFile(this.staticDir).getPath(); + String staticPath = getAbsoluteFile(this.staticDir).getCanonicalPath(); getLogger().info("Serving static from " + staticPath); getLogger().info("Mounting static at " + staticMountpoint); @@ -537,7 +537,7 @@ public class ApplicationManager implements XmlRpcHandler { } if (protectedStaticDir != null) { - String protectedContent = getAbsoluteFile(protectedStaticDir).getPath(); + String protectedContent = getAbsoluteFile(protectedStaticDir).getCanonicalPath(); appContext.setBaseResourceAsString(protectedContent); getLogger().info("Serving protected static from " + protectedContent); } From 9143faf35ec71163ebb9727daf6e1975602cc7a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Thu, 30 May 2024 18:49:45 +0200 Subject: [PATCH 16/42] Add release action --- .github/workflows/release.yml | 43 +++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..28a2249c --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,43 @@ +name: Release + +on: + push: + tags: + - 'v*' + +permissions: + contents: write + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - 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 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh release create "$GITHUB_REF_NAME" \ + --repo "$GITHUB_REPOSITORY" \ + --title "Helma $GITHUB_REF_NAME" \ + --generate-notes + + - name: Upload assets + run: | + gh release upload "$GITHUB_REF_NAME" \ + build/distributions/helma-*.* + --clobber From 9e870e6cd3cd189491d84af0c24367278b99f21e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Sat, 15 Jun 2024 12:23:03 +0200 Subject: [PATCH 17/42] Slightly modify the format of the build date --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 7fbd71a4..050ca1c5 100644 --- a/build.gradle +++ b/build.gradle @@ -157,7 +157,7 @@ run { } task processSource(type: Sync) { - def date = new Date().format("MMMM dd, yyyy") + def date = new Date().format("d MMMM yyyy") def gitOutput = new ByteArrayOutputStream() exec { From 84abcde0370e9aa800bec7499c7fb95dce1c6f21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Sat, 15 Jun 2024 12:24:17 +0200 Subject: [PATCH 18/42] Update run configuration to always use the correct dependencies --- build.gradle | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 050ca1c5..91a5ec0e 100644 --- a/build.gradle +++ b/build.gradle @@ -102,6 +102,8 @@ distributions { } application { + mainClass = 'helma.main.Server' + applicationDistribution.from(projectDir) { include 'modules/**' include 'LICENSE.md' @@ -152,8 +154,8 @@ installDist { } run { - classpath = files('launcher.jar') jvmArgs jettyLogLevel, suppressMacosDockIcon + classpath += fileTree(dir: 'lib/ext', include: '*.jar') } task processSource(type: Sync) { From ffb259a01c4626a30a9d3d5a3db505166fed7599 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Sat, 15 Jun 2024 12:37:16 +0200 Subject: [PATCH 19/42] Slightly modify the version string (still a date representation) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 91a5ec0e..0eef6f4d 100644 --- a/build.gradle +++ b/build.gradle @@ -25,7 +25,7 @@ allprojects { } } -version = new Date().format("yyyyMMdd") +version = new Date().format("yy.M.d") tasks.build.dependsOn javadoc, 'jsdoc', 'generateLicenseReport' tasks.compileJava.dependsOn 'processSource' From 45b19e32177b1fa8370093357af87d6a56da5039 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Sat, 15 Jun 2024 12:38:04 +0200 Subject: [PATCH 20/42] Reorder the tasks --- build.gradle | 56 ++++++++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/build.gradle b/build.gradle index 0eef6f4d..f1618a02 100644 --- a/build.gradle +++ b/build.gradle @@ -76,29 +76,9 @@ def rhinoJar = configurations.library.files.find { jar -> jar.name.startsWith('rhino') } -startScripts { - applicationName = 'helma' - classpath = files('../launcher.jar') - mainClass = 'helma.main.launcher.Main' - - defaultJvmOpts = [jettyLogLevel, suppressMacosDockIcon] - - doLast { - // Work-around to make the classpath above work (launcher.jar is located outside of `lib` dir) - // See https://discuss.gradle.org/t/classpath-in-application-plugin-is-building-always-relative-to-app-home-lib-directory/2012 - def unixScriptFile = file getUnixScript() - def windowsScriptFile = file getWindowsScript() - unixScriptFile.text = unixScriptFile.text.replace('$APP_HOME/lib', '$APP_HOME') - windowsScriptFile.text = windowsScriptFile.text.replace('%APP_HOME%\\lib', '%APP_HOME%') - } -} - -distributions { - main { - contents { - from project(':launcher').jar - } - } +run { + jvmArgs jettyLogLevel, suppressMacosDockIcon + classpath += fileTree(dir: 'lib/ext', include: '*.jar') } application { @@ -127,6 +107,31 @@ application { } } +startScripts { + applicationName = 'helma' + classpath = files('../launcher.jar') + mainClass = 'helma.main.launcher.Main' + + defaultJvmOpts = [jettyLogLevel, suppressMacosDockIcon] + + doLast { + // Work-around to make the classpath above work (launcher.jar is located outside of `lib` dir) + // See https://discuss.gradle.org/t/classpath-in-application-plugin-is-building-always-relative-to-app-home-lib-directory/2012 + def unixScriptFile = file getUnixScript() + def windowsScriptFile = file getWindowsScript() + unixScriptFile.text = unixScriptFile.text.replace('$APP_HOME/lib', '$APP_HOME') + windowsScriptFile.text = windowsScriptFile.text.replace('%APP_HOME%\\lib', '%APP_HOME%') + } +} + +distributions { + main { + contents { + from project(':launcher').jar + } + } +} + distTar { dependsOn ':generateLicenseReport', ':javadoc', ':jsdoc' @@ -153,11 +158,6 @@ installDist { } } -run { - jvmArgs jettyLogLevel, suppressMacosDockIcon - classpath += fileTree(dir: 'lib/ext', include: '*.jar') -} - task processSource(type: Sync) { def date = new Date().format("d MMMM yyyy") def gitOutput = new ByteArrayOutputStream() From 6401300189b40100d9e230adeff3cb7330409dab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Sat, 15 Jun 2024 13:02:11 +0200 Subject: [PATCH 21/42] Decouple update task from install Now that Gradle runs Helma with the configured dependencies, updating the installation directory has become less crucial --- README.md | 6 ++++-- build.gradle | 6 ++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 2a134485..e93af8d1 100644 --- a/README.md +++ b/README.md @@ -33,10 +33,12 @@ Helma is built with [Gradle](https://gradle.org), the build task depends on the ### Additional Prerequisites -* [Rsync](https://rsync.samba.org) version ≥ 3.1.0 * [Node.js](https://nodejs.org) LTS version +* [Rsync](https://rsync.samba.org) version ≥ 3.1.0 -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`. +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. > ⚠️ > 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 f1618a02..e2ee6c4c 100644 --- a/build.gradle +++ b/build.gradle @@ -152,10 +152,6 @@ distZip { installDist { dependsOn build - - if (!System.getenv('CI')) { - finalizedBy 'update' - } } task processSource(type: Sync) { @@ -183,6 +179,8 @@ task processSource(type: Sync) { } task update { + dependsOn installDist + def rsyncArgs = ['--archive', '--filter', '- backups'] def confirm = { From 3e5af064b0e502820dcf4dea1a89d1c9bf0d2b4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Sat, 15 Jun 2024 14:59:33 +0200 Subject: [PATCH 22/42] Add setup for Gradle debugging in VS Codium --- .vscode/launch.json | 128 ++++++++++++++++---------------------------- .vscode/tasks.json | 66 +++++++++++++++++++++++ build.gradle | 8 +++ 3 files changed, 119 insertions(+), 83 deletions(-) create mode 100644 .vscode/tasks.json diff --git a/.vscode/launch.json b/.vscode/launch.json index 8310c621..d1cf813f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,84 +1,46 @@ { - // 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 + // 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": "Run with Gradle", + "request": "launch", + "mainClass": "helma.main.Server", + "projectName": "helma", + "preLaunchTask": "Run with Gradle", + "console": "internalConsole", + "stopOnEntry": false + }, + { + "name": "Debug with Gradle", + "type": "java", + "request": "attach", + "hostName": "localhost", + "port": 5005, + "preLaunchTask": "Debug with Gradle" + }, + { + "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" + } + ] +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 00000000..78bedb1f --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,66 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "Run with Gradle", + "type": "shell", + "command": "./gradlew run", + "isBackground": true, + "group": { + "isDefault": true + }, + "problemMatcher": { + "owner": "java", + "fileLocation": "absolute", + "pattern": [ + { + // [2024/06/15 16:23:22] [ERROR] [antville-1] GET:main.css helma.scripting.ScriptingException: TypeError: Cannot find function getStaticFile in object HopObject Skin. (/home/tobi/Projects/helma/repo/./apps/antville/code/Site/Site.js#474) + "regexp": "^\\[.+\\] \\[(.+)\\] \\[.+\\] \\S+ \\s+: (.+) \\(([^#]+)#(\\d+)\\)$", + "severity": 1, + "message": 2, + "file": 3, + "line": 4 + }, + { + // [2024/06/15 17:26:00] [INFO] [antville-1] INTERNAL:onStart done in 381 millis + "regexp": "^.+INTERNAL:onStart done in \\d+ millis", + "kind": "file", + "file": 0 + } + ], + "background": { + "activeOnStart": true, + "beginsPattern": "^.+(INTERNAL):onStart done in \\d+ millis", + "endsPattern": "terminated with exit code" + } + } + }, + { + "label": "Debug with Gradle", + "type": "shell", + "command": "./gradlew debug", + "isBackground": true, + "problemMatcher": { + "owner": "custom", + "fileLocation": "absolute", + "pattern": [ + { + // [2024/06/15 16:23:22] [ERROR] [antville-1] GET:main.css helma.scripting.ScriptingException: TypeError: Cannot find function getStaticFile in object HopObject Skin. (/home/tobi/Projects/helma/repo/./apps/antville/code/Site/Site.js#474) + "regexp": "^\\[.+\\] \\[(.+)\\] \\[.+\\] \\S+ \\s+: (.+) \\(([^#]+)#(\\d+)\\)$", + "severity": 1, + "message": 2, + "file": 3, + "line": 4 + } + ], + "background": { + "activeOnStart": true, + "beginsPattern": "Listening for transport dt_socket at address", + "endsPattern": "terminated with exit code" + } + } + } + ] +} diff --git a/build.gradle b/build.gradle index e2ee6c4c..84ef732e 100644 --- a/build.gradle +++ b/build.gradle @@ -290,3 +290,11 @@ task commandLine(type: 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') +} From 51bc14c3a456c3ff3247cbaae4e619d4b1bd30a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Sat, 15 Jun 2024 18:01:30 +0200 Subject: [PATCH 23/42] Remove over-complicated launch and tasks configuration in favor of Gradle plugin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Starting run/debug tasks with the plugin works out of the box; one just has to avoid the default “Run and Debug” button. --- .vscode/extensions.json | 3 +- .vscode/launch.json | 46 ---------------------------- .vscode/tasks.json | 66 ----------------------------------------- 3 files changed, 2 insertions(+), 113 deletions(-) delete mode 100644 .vscode/launch.json delete mode 100644 .vscode/tasks.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 0d95f065..c52f6863 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,5 +1,6 @@ { "recommendations": [ - "vscjava.vscode-java-pack" + "vscjava.vscode-java-pack", + "vscjava.vscode-gradle" ] } diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index d1cf813f..00000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - // 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": "Run with Gradle", - "request": "launch", - "mainClass": "helma.main.Server", - "projectName": "helma", - "preLaunchTask": "Run with Gradle", - "console": "internalConsole", - "stopOnEntry": false - }, - { - "name": "Debug with Gradle", - "type": "java", - "request": "attach", - "hostName": "localhost", - "port": 5005, - "preLaunchTask": "Debug with Gradle" - }, - { - "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" - } - ] -} diff --git a/.vscode/tasks.json b/.vscode/tasks.json deleted file mode 100644 index 78bedb1f..00000000 --- a/.vscode/tasks.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - // See https://go.microsoft.com/fwlink/?LinkId=733558 - // for the documentation about the tasks.json format - "version": "2.0.0", - "tasks": [ - { - "label": "Run with Gradle", - "type": "shell", - "command": "./gradlew run", - "isBackground": true, - "group": { - "isDefault": true - }, - "problemMatcher": { - "owner": "java", - "fileLocation": "absolute", - "pattern": [ - { - // [2024/06/15 16:23:22] [ERROR] [antville-1] GET:main.css helma.scripting.ScriptingException: TypeError: Cannot find function getStaticFile in object HopObject Skin. (/home/tobi/Projects/helma/repo/./apps/antville/code/Site/Site.js#474) - "regexp": "^\\[.+\\] \\[(.+)\\] \\[.+\\] \\S+ \\s+: (.+) \\(([^#]+)#(\\d+)\\)$", - "severity": 1, - "message": 2, - "file": 3, - "line": 4 - }, - { - // [2024/06/15 17:26:00] [INFO] [antville-1] INTERNAL:onStart done in 381 millis - "regexp": "^.+INTERNAL:onStart done in \\d+ millis", - "kind": "file", - "file": 0 - } - ], - "background": { - "activeOnStart": true, - "beginsPattern": "^.+(INTERNAL):onStart done in \\d+ millis", - "endsPattern": "terminated with exit code" - } - } - }, - { - "label": "Debug with Gradle", - "type": "shell", - "command": "./gradlew debug", - "isBackground": true, - "problemMatcher": { - "owner": "custom", - "fileLocation": "absolute", - "pattern": [ - { - // [2024/06/15 16:23:22] [ERROR] [antville-1] GET:main.css helma.scripting.ScriptingException: TypeError: Cannot find function getStaticFile in object HopObject Skin. (/home/tobi/Projects/helma/repo/./apps/antville/code/Site/Site.js#474) - "regexp": "^\\[.+\\] \\[(.+)\\] \\[.+\\] \\S+ \\s+: (.+) \\(([^#]+)#(\\d+)\\)$", - "severity": 1, - "message": 2, - "file": 3, - "line": 4 - } - ], - "background": { - "activeOnStart": true, - "beginsPattern": "Listening for transport dt_socket at address", - "endsPattern": "terminated with exit code" - } - } - } - ] -} From 19259162209ddd8abe8ae009cc0e808570d09cac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Sat, 15 Jun 2024 18:27:30 +0200 Subject: [PATCH 24/42] Bump required minimum Java version to 17 --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index b1aa2021..94a72550 100644 --- a/build.gradle +++ b/build.gradle @@ -17,8 +17,8 @@ def textFiles = ['**/*.hac', '**/.html', '**/*.js', '**/*.md', '**/*.properties' allprojects { apply plugin: 'java' - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 repositories { mavenCentral() From de2150693f955b6e2c9f81f2012f5dcb7dd4cf18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Fri, 28 Feb 2025 21:42:33 +0100 Subject: [PATCH 25/42] Add deploy script usable with rsync and a restricted SSH key --- src/dist/extras/deploy.sh | 60 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 src/dist/extras/deploy.sh diff --git a/src/dist/extras/deploy.sh b/src/dist/extras/deploy.sh new file mode 100644 index 00000000..6ebd6bf2 --- /dev/null +++ b/src/dist/extras/deploy.sh @@ -0,0 +1,60 @@ +#!/bin/sh + +# Use this script as forced command of an authorized SSH key: +# command="/home/helma/extras/deploy.sh" ssh-ed25519 AAAAC3NzaC… + +# Define HELMA_HOST and ANTVILLE_HOST in this file +# shellcheck source=/dev/null +. "$HOME"/deploy.env + +case "$SSH_ORIGINAL_COMMAND" in + ping) + echo pong + ;; + + deploy-helma) + rsync ./ "$HELMA_HOST":./ \ + --archive --compress --delete --verbose \ + --filter '+ /bin' \ + --filter '+ /extras' \ + --filter '+ /launcher.jar' \ + --filter '- /lib/ext' \ + --filter '+ /lib' \ + --filter '+ /modules' \ + --filter '- /*' + printf 'Restarting Helma on HELMA_host… ' + ssh "$HELMA_HOST" sudo /bin/systemctl restart helma + ;; + + deploy-antville) + rsync ./apps/antville/ "$ANTVILLE_HOST":./apps/antville/ \ + --archive --compress --delete --verbose \ + --filter '+ /claustra' \ + --filter '+ /code' \ + --filter '+ /compat' \ + --filter '+ /i18n' \ + --filter '+ /lib' \ + --filter '- /*' + rsync ./apps/antville/static/helma/ "$ANTVILLE_HOST":./apps/antville/static/helma/ \ + --archive --compress --verbose \ + --filter '+ /fonts' \ + --filter '+ /formica.html' \ + --filter '+ /img' \ + --filter '+ /scripts' \ + --filter '+ /styles' \ + --filter '- /*' + printf 'Restarting Helma on ANTVILLE_host… ' + ssh "$ANTVILLE_HOST" sudo /bin/systemctl restart helma + ;; + + 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 From a3fbf72f3855cfa0c69919d126c9661f985e0f85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Fri, 28 Feb 2025 21:58:08 +0100 Subject: [PATCH 26/42] Initial commit --- .github/workflows/build.yml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..9bf8429b --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,34 @@ +name: Build + +on: + push: + paths: + - build.gradle + - settings.gradle + - src/** + - launcher/build.gradle + - launcher/src/** + workflow_dispatch: + +jobs: + build: + runs-on: antville + + strategy: + matrix: + java: [11, 17, 21] + + steps: + - uses: actions/checkout@v4 + + - name: Set up Java + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: ${{ matrix.java }} + + - name: Set up Gradle + uses: gradle/actions/setup-gradle@v3 + + - name: Compile with Gradle + run: ./gradlew :compileJava From bc7894ecc19c54fe91967927c7c2e26180b2c3ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Fri, 28 Feb 2025 22:04:48 +0100 Subject: [PATCH 27/42] Use fully qualified URL for setup-java action --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9bf8429b..18fde086 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,7 +22,7 @@ jobs: - uses: actions/checkout@v4 - name: Set up Java - uses: actions/setup-java@v4 + uses: https://github.com/actions/setup-java@v4 with: distribution: temurin java-version: ${{ matrix.java }} From 4c011f1e1baf35a273add96b4574064995ca59bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Fri, 28 Feb 2025 22:09:55 +0100 Subject: [PATCH 28/42] Gradle is installed on the runner --- .github/workflows/build.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 18fde086..846e2d6f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,8 +27,5 @@ jobs: distribution: temurin java-version: ${{ matrix.java }} - - name: Set up Gradle - uses: gradle/actions/setup-gradle@v3 - - name: Compile with Gradle run: ./gradlew :compileJava From 04b210b464dccb56edb598321ca594a78972913d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Fri, 28 Feb 2025 22:12:30 +0100 Subject: [PATCH 29/42] Leave aside compiling for different Java versions for now --- .github/workflows/build.yml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 846e2d6f..47af9523 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,18 +14,8 @@ jobs: build: runs-on: antville - strategy: - matrix: - java: [11, 17, 21] - steps: - uses: actions/checkout@v4 - - name: Set up Java - uses: https://github.com/actions/setup-java@v4 - with: - distribution: temurin - java-version: ${{ matrix.java }} - - name: Compile with Gradle run: ./gradlew :compileJava From 9b5cc988ddf3ec06d5dd71d6b0053a6649ea9059 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Fri, 28 Feb 2025 22:13:25 +0100 Subject: [PATCH 30/42] Run the build workflow when itself has changed --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 47af9523..69bcdeab 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,6 +3,7 @@ name: Build on: push: paths: + - .github/workflows/build.yml - build.gradle - settings.gradle - src/** From 6fc73d2320afcd4711b17ba9d7262e5df1ff00f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Sat, 1 Mar 2025 01:02:27 +0100 Subject: [PATCH 31/42] Add release notes generated with git-cliff --- .github/workflows/release.yml | 23 ++++++++++++---- cliff.toml | 52 +++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 6 deletions(-) create mode 100644 cliff.toml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f7aa7874..ac8a3d1a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,16 +9,27 @@ permissions: contents: write jobs: - build: + release: runs-on: antville env: GH_TOKEN: ${{ secrets.GH_TOKEN }} LC_TIME: en_US.UTF-8 - TODAY: $(date +'%d %b %Y') 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: Build with Gradle run: ./gradlew assembleDist @@ -29,17 +40,17 @@ jobs: direction: upload url: https://code.host.antville.org token: ${{ github.token }} - title: ${{ env.TODAY }} + title: Helma ${{ github.ref_name }} release-dir: build/distributions - release-notes-assistant: true + 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 "${{ env.TODAY }}" \ - --generate-notes + --title "Helma ${{ github.ref_name }}" \ + --notes "${{ steps.create_release_notes.outputs.release_notes }}" - name: Upload release assets to GitHub run: | diff --git a/cliff.toml b/cliff.toml new file mode 100644 index 00000000..7832044a --- /dev/null +++ b/cliff.toml @@ -0,0 +1,52 @@ +# 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 }}\ + {% 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" }, +] From fc084f6e525e41b20b0c4bbfe002972aa8d3c80a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Sat, 1 Mar 2025 19:21:42 +0100 Subject: [PATCH 32/42] Escape HTML elements in commit messages --- cliff.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cliff.toml b/cliff.toml index 7832044a..755c4ca2 100644 --- a/cliff.toml +++ b/cliff.toml @@ -17,7 +17,7 @@ body = """ * [{{ 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 }}\ + {{ commit.message | split(pat="\\n") | first | upper_first | escape }}\ {% endfor %} {% endfor %} From b7543cf6157b3603bbdf79a61013a6c9d53adb39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Sat, 1 Mar 2025 19:32:06 +0100 Subject: [PATCH 33/42] Bump year of copyright notice --- LICENSE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE.md b/LICENSE.md index df5926e7..65dfb884 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ # License -Copyright (c) 1999-2008 Helma Project. All rights reserved. +Copyright (c) 1999-2025 Helma Project. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions From c42c0a7a17758a06f4f36e83cbb959cbb2dd1b3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Sat, 1 Mar 2025 19:32:20 +0100 Subject: [PATCH 34/42] Update repo URL --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e93af8d1..f8155df0 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ## TL;DR - Make sure you have Java 11 or higher installed -- Download and unpack the [latest release](https://github.com/antville/helma/releases) +- Download and unpack the [latest release](https://code.host.antville.org/antville/helma/releases) - Invoke `./bin/helma`, resp. `./bin/helma.bat`, depending on your platform - Direct your web browser to From 99e8b204fd02c6278bb7f6f0fc0644828f6c3f0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Mon, 7 Apr 2025 01:01:19 +0200 Subject: [PATCH 35/42] Bump Java version --- .java-version | 2 +- README.md | 2 +- src/main/java/helma/main/Server.java | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.java-version b/.java-version index 3b5b5d8f..98d9bcb7 100644 --- a/.java-version +++ b/.java-version @@ -1 +1 @@ -11.0 \ No newline at end of file +17 diff --git a/README.md b/README.md index f8155df0..f6438677 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ## TL;DR -- Make sure you have Java 11 or higher installed +- Make sure you have Java 17 or higher installed - Download and unpack the [latest release](https://code.host.antville.org/antville/helma/releases) - Invoke `./bin/helma`, resp. `./bin/helma.bat`, depending on your platform - Direct your web browser to diff --git a/src/main/java/helma/main/Server.java b/src/main/java/helma/main/Server.java index 88d70db7..d951adae 100644 --- a/src/main/java/helma/main/Server.java +++ b/src/main/java/helma/main/Server.java @@ -21,6 +21,7 @@ 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.*; @@ -152,8 +153,8 @@ public class Server implements Runnable { String javaVersion = System.getProperty("java.version", "0"); int majorVersion = Integer.parseInt(javaVersion.split("\\.")[0]); - if (majorVersion < 11) { - System.err.println("This version of Helma requires Java 11 or greater."); + if (majorVersion < 17) { + System.err.println("This version of Helma requires Java 17 or greater."); if (majorVersion == 0) { // 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."); From 2994a4becc41c97707bda88e08f64e3f8379da2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Mon, 7 Apr 2025 01:06:09 +0200 Subject: [PATCH 36/42] =?UTF-8?q?Disable=20Jetty=E2=80=99s=20session=20coo?= =?UTF-8?q?kies?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This remediates the exception “Shared scheduler not started” and restores the functionality of enabling an app in apps.properties – see https://code.host.antville.org/antville/helma/pulls/103#issuecomment-1825 --- src/main/java/helma/main/ApplicationManager.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/helma/main/ApplicationManager.java b/src/main/java/helma/main/ApplicationManager.java index 7d31b764..02affa2d 100644 --- a/src/main/java/helma/main/ApplicationManager.java +++ b/src/main/java/helma/main/ApplicationManager.java @@ -502,7 +502,9 @@ public class ApplicationManager implements XmlRpcHandler { staticContext.start(); } - appContext = new ServletContextHandler(ServletContextHandler.SESSIONS); + // 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); @@ -544,9 +546,7 @@ public class ApplicationManager implements XmlRpcHandler { // Remap the context paths and start ApplicationManager.this.context.mapContexts(); - // FIXME: Causing java.lang.IllegalStateException: Shared scheduler not started - // Is it necessary, anway? - //this.appContext.start(); + this.appContext.start(); } // register as XML-RPC handler From 36a12effb2ada1f5efe8ab60c35a80dc9e97dac3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Mon, 7 Apr 2025 16:56:05 +0200 Subject: [PATCH 37/42] Bump Jetty versions to 12.0.19 --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 84d471ed..d5f86709 100644 --- a/build.gradle +++ b/build.gradle @@ -67,8 +67,8 @@ dependencies { implementation 'com.sun.mail:javax.mail:1.6.2' implementation 'jakarta.servlet:jakarta.servlet-api:5.0.0' implementation 'org.ccil.cowan.tagsoup:tagsoup:1.2.1' - implementation 'org.eclipse.jetty.ee9:jetty-ee9-servlet:12.0.9' - implementation 'org.eclipse.jetty:jetty-xml:12.0.9' + 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.sejda.imageio:webp-imageio:0.1.6' implementation 'xerces:xercesImpl:2.12.2' From 436862e87a941c4a6f0152d580e650ce1474b61f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Mon, 7 Apr 2025 16:58:35 +0200 Subject: [PATCH 38/42] =?UTF-8?q?Remove=20setting=20of=20=E2=80=9Cempty?= =?UTF-8?q?=E2=80=9D=20base=20resource?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fingers crossed it won’t be missed --- src/main/java/helma/main/ApplicationManager.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/helma/main/ApplicationManager.java b/src/main/java/helma/main/ApplicationManager.java index 02affa2d..17915987 100644 --- a/src/main/java/helma/main/ApplicationManager.java +++ b/src/main/java/helma/main/ApplicationManager.java @@ -494,8 +494,6 @@ public class ApplicationManager implements XmlRpcHandler { ContextHandler staticContext = new ContextHandler(); staticContext.setContextPath(staticMountpoint); - // FIXME: Causing java.lang.IllegalArgumentException: Resource String is invalid - //staticContext.setBaseResourceAsString(""); staticContext.setHandler(rhandler); ApplicationManager.this.context.addHandler(staticContext); From 6723df912e671f5f7da4963c40358e2ae549c271 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Mon, 7 Apr 2025 17:02:11 +0200 Subject: [PATCH 39/42] Resolve FIXME but without fixing the issue I tried to make this work but it might need a more thorough rewrite that I just cannot do. The worst that can happen is stopping and starting Helma apps adding redundant ServletContextHandlers to the ContextHandlerCollection until Helma is restarted. --- src/main/java/helma/main/ApplicationManager.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/helma/main/ApplicationManager.java b/src/main/java/helma/main/ApplicationManager.java index 17915987..e699a8d8 100644 --- a/src/main/java/helma/main/ApplicationManager.java +++ b/src/main/java/helma/main/ApplicationManager.java @@ -563,8 +563,8 @@ public class ApplicationManager implements XmlRpcHandler { // unbind from Jetty HTTP server if (ApplicationManager.this.jetty != null) { if (this.appContext != null) { - // FIXME: Causing incompatible types: ServletContextHandler cannot be converted to Handler - // Is it necessary, anyway? + // 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); this.appContext.stop(); this.appContext.destroy(); From 233886c43330ab84f87e097cdd643b760842eeed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Wed, 9 Apr 2025 22:57:16 +0200 Subject: [PATCH 40/42] Fix compiler warnings with and without Copilot --- .../java/helma/main/launcher/Commandline.java | 4 +- .../main/java/helma/main/launcher/Main.java | 10 +- .../java/helma/extensions/HelmaExtension.java | 2 +- .../helma/extensions/demo/DemoExtension.java | 6 +- .../helma/framework/core/Application.java | 50 +- .../framework/core/RequestEvaluator.java | 4 +- src/main/java/helma/image/ImageGenerator.java | 26 +- src/main/java/helma/image/ImageInfo.java | 596 +++++++++--------- .../image/imageio/gif/GIFImageWriterSpi.java | 7 +- .../java/helma/main/ApplicationManager.java | 35 +- .../java/helma/main/CommandlineRunner.java | 8 +- .../java/helma/main/HelmaSecurityManager.java | 14 +- .../java/helma/main/HelmaShutdownHook.java | 3 - src/main/java/helma/main/JettyServer.java | 7 +- src/main/java/helma/main/Server.java | 20 +- .../java/helma/objectmodel/TransientNode.java | 10 +- .../helma/objectmodel/db/NodeManager.java | 13 +- .../helma/objectmodel/db/SubnodeList.java | 3 +- .../java/helma/objectmodel/db/Transactor.java | 2 - .../helma/scripting/ScriptingException.java | 15 +- ...iledOrInterpretedModuleScriptProvider.java | 14 +- .../helma/scripting/rhino/HopObjectCtor.java | 10 +- .../java/helma/scripting/rhino/JSAdapter.java | 2 +- .../scripting/rhino/JSONModuleSource.java | 2 +- .../helma/scripting/rhino/JavaObject.java | 5 +- .../scripting/rhino/NodeModulesProvider.java | 4 +- .../java/helma/scripting/rhino/RhinoCore.java | 3 +- .../scripting/rhino/SerializationProxy.java | 12 +- .../helma/scripting/rhino/debug/Profiler.java | 4 +- .../helma/scripting/rhino/debug/Tracer.java | 7 +- .../rhino/extensions/DatabaseObject.java | 54 +- .../rhino/extensions/MailObject.java | 2 +- .../scripting/rhino/extensions/XmlObject.java | 4 +- .../helma/servlet/AbstractServletClient.java | 18 +- .../helma/servlet/EmbeddedServletClient.java | 1 - src/main/java/helma/util/CryptResource.java | 1 - 36 files changed, 464 insertions(+), 514 deletions(-) diff --git a/launcher/src/main/java/helma/main/launcher/Commandline.java b/launcher/src/main/java/helma/main/launcher/Commandline.java index 5dc211bd..3f87bc53 100644 --- a/launcher/src/main/java/helma/main/launcher/Commandline.java +++ b/launcher/src/main/java/helma/main/launcher/Commandline.java @@ -41,8 +41,8 @@ public class Commandline { ClassLoader loader = Main.createClassLoader(installDir); // get the main server class - Class clazz = loader.loadClass("helma.main.CommandlineRunner"); - Class[] cargs = new Class[]{args.getClass()}; + Class clazz = loader.loadClass("helma.main.CommandlineRunner"); + Class[] cargs = new Class[]{args.getClass()}; Method main = clazz.getMethod("main", cargs); Object[] nargs = new Object[]{args}; diff --git a/launcher/src/main/java/helma/main/launcher/Main.java b/launcher/src/main/java/helma/main/launcher/Main.java index d7060eda..c0ee09ab 100644 --- a/launcher/src/main/java/helma/main/launcher/Main.java +++ b/launcher/src/main/java/helma/main/launcher/Main.java @@ -35,10 +35,10 @@ import java.util.ArrayList; * be able to set up class and install paths. */ public class Main { - private Class serverClass; + private Class serverClass; private Object server; - private static final Class[] EMPTY_CLASS_ARRAY = new Class[0]; + private static final Class[] EMPTY_CLASS_ARRAY = new Class[0]; private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0]; /** @@ -60,7 +60,7 @@ public class Main { ClassLoader loader = createClassLoader(installDir); // get the main server class serverClass = loader.loadClass("helma.main.Server"); - Class[] cargs = new Class[]{args.getClass()}; + Class[] cargs = new Class[]{args.getClass()}; Method loadServer = serverClass.getMethod("loadServer", cargs); Object[] nargs = new Object[]{args}; // and invoke the static loadServer(String[]) method @@ -111,7 +111,7 @@ public class Main { } } - static void addJars(ArrayList jarlist, File dir) throws MalformedURLException { + static void addJars(ArrayList jarlist, File dir) throws MalformedURLException { File[] files = dir.listFiles(new FilenameFilter() { public boolean accept(File dir, String name) { String n = name.toLowerCase(); @@ -143,7 +143,7 @@ public class Main { // set up the class path File libdir = new File(installDir, "lib"); - ArrayList jarlist = new ArrayList(); + ArrayList jarlist = new ArrayList<>(); // add all jar files from the lib directory addJars(jarlist, libdir); diff --git a/src/main/java/helma/extensions/HelmaExtension.java b/src/main/java/helma/extensions/HelmaExtension.java index 7bb0fa26..13f827d6 100644 --- a/src/main/java/helma/extensions/HelmaExtension.java +++ b/src/main/java/helma/extensions/HelmaExtension.java @@ -59,7 +59,7 @@ public abstract class HelmaExtension { * with pairs of varname and ESObjects. This method should be synchronized, if it * performs any other self-initialization outside the scripting environment. */ - public abstract HashMap initScripting(Application app, ScriptingEngine engine) + public abstract HashMap initScripting(Application app, ScriptingEngine engine) throws ConfigurationException; /** diff --git a/src/main/java/helma/extensions/demo/DemoExtension.java b/src/main/java/helma/extensions/demo/DemoExtension.java index dbc35cae..1c5c14d1 100644 --- a/src/main/java/helma/extensions/demo/DemoExtension.java +++ b/src/main/java/helma/extensions/demo/DemoExtension.java @@ -42,7 +42,7 @@ public class DemoExtension extends HelmaExtension { public void init(Server server) throws ConfigurationException { try { // just a demo with the server class itself (which is always there, obviously) - Class check = Class.forName("helma.main.Server"); + Class.forName("helma.main.Server"); } catch (ClassNotFoundException e) { throw new ConfigurationException("helma-library not present in classpath. make sure helma.jar is included. get it from http://www.helma.org/"); } @@ -87,7 +87,7 @@ public class DemoExtension extends HelmaExtension { * * @throws ConfigurationException ... */ - public HashMap initScripting(Application app, ScriptingEngine engine) + public HashMap initScripting(Application app, ScriptingEngine engine) throws ConfigurationException { if (!(engine instanceof RhinoEngine)) { throw new ConfigurationException("scripting engine " + engine.toString() + @@ -98,7 +98,7 @@ public class DemoExtension extends HelmaExtension { engine.toString()); // initialize prototypes and global vars here - HashMap globals = new HashMap(); + HashMap globals = new HashMap<>(); globals.put("demo", Server.getServer()); diff --git a/src/main/java/helma/framework/core/Application.java b/src/main/java/helma/framework/core/Application.java index be41d21a..b78d519e 100644 --- a/src/main/java/helma/framework/core/Application.java +++ b/src/main/java/helma/framework/core/Application.java @@ -47,7 +47,7 @@ public final class Application implements Runnable { private String name; // application sources - ArrayList repositories; + ArrayList repositories; // properties and db-properties ResourceProperties props; @@ -96,15 +96,15 @@ public final class Application implements Runnable { /** * Collections for evaluator thread pooling */ - protected Stack freeThreads; - protected Vector allThreads; + protected Stack freeThreads; + protected Vector allThreads; boolean running = false; boolean debug; long starttime; - Hashtable dbSources; + Hashtable dbSources; // map of app modules reflected at app.modules - Map modules; + Map modules; // internal worker thread for scheduler, session cleanup etc. Thread worker; @@ -113,10 +113,10 @@ public final class Application implements Runnable { ThreadGroup threadgroup; // threadlocal variable for the current RequestEvaluator - ThreadLocal currentEvaluator = new ThreadLocal(); + ThreadLocal currentEvaluator = new ThreadLocal<>(); // Map of requesttrans -> active requestevaluators - Hashtable activeRequests; + Hashtable activeRequests; String logDir; @@ -165,15 +165,15 @@ public final class Application implements Runnable { private long lastPropertyRead = -1L; // the set of prototype/function pairs which are allowed to be called via XML-RPC - private HashSet xmlrpcAccess; + private HashSet xmlrpcAccess; // the name under which this app serves XML-RPC requests. Defaults to the app name private String xmlrpcHandlerName; // the list of currently active cron jobs - Hashtable activeCronJobs = null; + Hashtable activeCronJobs = null; // the list of custom cron jobs - Hashtable customCronJobs = null; + Hashtable customCronJobs = null; private ResourceComparator resourceComparator; private Resource currentCodeResource; @@ -230,7 +230,7 @@ public final class Application implements Runnable { this.caseInsensitive = "true".equalsIgnoreCase(server.getAppsProperties(name).getProperty("caseInsensitive")); - this.repositories = new ArrayList(); + this.repositories = new ArrayList<>(); this.repositories.addAll(Arrays.asList(repositories)); resourceComparator = new ResourceComparator(this); @@ -304,7 +304,7 @@ public final class Application implements Runnable { updateProperties(); - dbSources = new Hashtable(); + dbSources = new Hashtable<>(); modules = new SystemMap(); } @@ -366,7 +366,9 @@ public final class Application implements Runnable { private void initInternal() throws DatabaseException, IllegalAccessException, - InstantiationException, ClassNotFoundException { + InstantiationException, ClassNotFoundException, + IllegalArgumentException, InvocationTargetException, + NoSuchMethodException, SecurityException { running = true; // create and init type mananger typemgr = new TypeManager(Application.this, ignoreDirs); @@ -381,7 +383,7 @@ public final class Application implements Runnable { } if (Server.getServer() != null) { - Vector extensions = Server.getServer().getExtensions(); + Vector extensions = Server.getServer().getExtensions(); for (int i = 0; i < extensions.size(); i++) { HelmaExtension ext = (HelmaExtension) extensions.get(i); @@ -396,12 +398,12 @@ public final class Application implements Runnable { } // create and init evaluator/thread lists - freeThreads = new Stack(); - allThreads = new Vector(); + freeThreads = new Stack<>(); + allThreads = new Vector<>(); - activeRequests = new Hashtable(); - activeCronJobs = new Hashtable(); - customCronJobs = new Hashtable(); + activeRequests = new Hashtable<>(); + activeCronJobs = new Hashtable<>(); + customCronJobs = new Hashtable<>(); // create the skin manager skinmgr = new SkinManager(Application.this); @@ -442,7 +444,13 @@ public final class Application implements Runnable { // create and init session manager String sessionMgrImpl = props.getProperty("sessionManagerImpl", "helma.framework.core.SessionManager"); - sessionMgr = (SessionManager) Class.forName(sessionMgrImpl).newInstance(); + try { + sessionMgr = (SessionManager) Class.forName(sessionMgrImpl) + .getDeclaredConstructor() + .newInstance(); + } catch (NoSuchMethodException | InvocationTargetException e) { + throw new RuntimeException("Error initializing session manager", e); + } logEvent("Using session manager class " + sessionMgrImpl); sessionMgr.init(Application.this); @@ -879,7 +887,7 @@ public final class Application implements Runnable { } else { String rootClass = classMapping.getProperty("root"); Class c = typemgr.getClassLoader().loadClass(rootClass); - rootObject = c.newInstance(); + rootObject = c.getDeclaredConstructor().newInstance(); } } catch (Exception e) { throw new RuntimeException("Error creating root object: " + diff --git a/src/main/java/helma/framework/core/RequestEvaluator.java b/src/main/java/helma/framework/core/RequestEvaluator.java index e8b6051c..c62f3e7d 100644 --- a/src/main/java/helma/framework/core/RequestEvaluator.java +++ b/src/main/java/helma/framework/core/RequestEvaluator.java @@ -100,7 +100,7 @@ public final class RequestEvaluator implements Runnable { app.setCurrentRequestEvaluator(this); Class clazz = app.getClassLoader().loadClass(engineClassName); - scriptingEngine = (ScriptingEngine) clazz.newInstance(); + scriptingEngine = (ScriptingEngine) clazz.getDeclaredConstructor().newInstance(); scriptingEngine.init(app, this); } catch (Exception x) { Throwable t = x; @@ -566,7 +566,7 @@ public final class RequestEvaluator implements Runnable { int base = 800 * tries; Thread.sleep((long) (base + (Math.random() * base * 2))); } catch (InterruptedException interrupt) { - // we got interrrupted, create minimal error message + // we got interrrupted, create minimal error message res.reportError(interrupt); done = true; // and release resources and thread diff --git a/src/main/java/helma/image/ImageGenerator.java b/src/main/java/helma/image/ImageGenerator.java index c7d85a96..61839013 100644 --- a/src/main/java/helma/image/ImageGenerator.java +++ b/src/main/java/helma/image/ImageGenerator.java @@ -27,7 +27,7 @@ import java.net.URL; /** * Factory class for generating Image objects from various sources. - * + * */ public abstract class ImageGenerator { protected static ImageGenerator generator = null; @@ -72,7 +72,7 @@ public abstract class ImageGenerator { "The imageGenerator class cannot be found: " + className); } try { - generator = (ImageGenerator)generatorClass.newInstance(); + generator = (ImageGenerator) generatorClass.getDeclaredConstructor().newInstance(); } catch (Exception e) { throw new RuntimeException( "The ImageGenerator instance could not be created: " @@ -85,7 +85,7 @@ public abstract class ImageGenerator { /** * @param w ... * @param h ... - * + * * @return ... */ public ImageWrapper createImage(int w, int h) { @@ -95,7 +95,7 @@ public abstract class ImageGenerator { /** * @param src ... - * + * * @return ... * @throws IOException */ @@ -103,10 +103,10 @@ public abstract class ImageGenerator { Image img = read(src); return img != null ? new ImageWrapper(img, this) : null; } - + /** * @param filenamne ... - * + * * @return ... * @throws IOException */ @@ -118,7 +118,7 @@ public abstract class ImageGenerator { /** * @param url ... - * + * * @return ... * @throws MalformedURLException * @throws IOException @@ -144,14 +144,14 @@ public abstract class ImageGenerator { /** * @param iw ... * @param filter ... - * + * * @return ... */ public ImageWrapper createImage(ImageWrapper iw, ImageFilter filter) { // use the ImagFilterOp wrapper for ImageFilters that works directly // on BufferedImages. The filtering is much faster like that. // Attention: needs testing with all the filters! - + return createImage(iw, new ImageFilterOp(filter)); // Image img = ImageWaiter.waitForImage( // Toolkit.getDefaultToolkit().createImage( @@ -162,7 +162,7 @@ public abstract class ImageGenerator { /** * @param iw ... * @param imageOp ... - * + * * @return ... */ public ImageWrapper createImage(ImageWrapper iw, BufferedImageOp imageOp) { @@ -212,7 +212,7 @@ public abstract class ImageGenerator { /** * Saves the image. Image format is deduced from filename. - * + * * @param wrapper * @param filename * @param quality @@ -224,7 +224,7 @@ public abstract class ImageGenerator { /** * Saves the image. Image format is deduced from the dataSource. - * + * * @param wrapper * @param out * @param quality @@ -233,4 +233,4 @@ public abstract class ImageGenerator { */ public abstract void write(ImageWrapper wrapper, OutputStream out, String type, float quality, boolean alpha) throws IOException; -} \ No newline at end of file +} diff --git a/src/main/java/helma/image/ImageInfo.java b/src/main/java/helma/image/ImageInfo.java index 1de5f211..b9a9d324 100644 --- a/src/main/java/helma/image/ImageInfo.java +++ b/src/main/java/helma/image/ImageInfo.java @@ -6,7 +6,7 @@ * A Java class to determine image width, height and color depth for * a number of image file formats. * - * Written by Marco Schmidt + * Written by Marco Schmidt * . * * Contributed to the Public Domain. @@ -24,47 +24,53 @@ import java.net.URL; import java.util.Vector; /** - * Get file format, image resolution, number of bits per pixel and optionally - * number of images, comments and physical resolution from - * JPEG, GIF, BMP, PCX, PNG, IFF, RAS, PBM, PGM, PPM, PSD and SWF files + * Get file format, image resolution, number of bits per pixel and optionally + * number of images, comments and physical resolution from + * JPEG, GIF, BMP, PCX, PNG, IFF, RAS, PBM, PGM, PPM, PSD and SWF files * (or input streams). *

* Use the class like this: + * *

  * ImageInfo ii = new ImageInfo();
  * ii.setInput(in); // in can be InputStream or RandomAccessFile
  * ii.setDetermineImageNumber(true); // default is false
  * ii.setCollectComments(true); // default is false
  * if (!ii.check()) {
- *   System.err.println("Not a supported image file format.");
- *   return;
+ * 	System.err.println("Not a supported image file format.");
+ * 	return;
  * }
- * System.out.println(ii.getFormatName() + ", " + ii.getMimeType() + 
- *   ", " + ii.getWidth() + " x " + ii.getHeight() + " pixels, " + 
- *   ii.getBitsPerPixel() + " bits per pixel, " + ii.getNumberOfImages() +
- *   " image(s), " + ii.getNumberOfComments() + " comment(s).");
- *  // there are other properties, check out the API documentation
+ * System.out.println(ii.getFormatName() + ", " + ii.getMimeType() +
+ * 		", " + ii.getWidth() + " x " + ii.getHeight() + " pixels, " +
+ * 		ii.getBitsPerPixel() + " bits per pixel, " + ii.getNumberOfImages() +
+ * 		" image(s), " + ii.getNumberOfComments() + " comment(s).");
+ * // there are other properties, check out the API documentation
  * 
+ * * You can also use this class as a command line program. * Call it with a number of image file names and URLs as parameters: + * *
  *   java ImageInfo *.jpg *.png *.gif http://somesite.tld/image.jpg
  * 
+ * * or call it without parameters and pipe data to it: + * *
- *   java ImageInfo < image.jpg  
+ *   java ImageInfo < image.jpg
  * 
*

* Known limitations: *

    - *
  • When the determination of the number of images is turned off, GIF bits - * per pixel are only read from the global header. - * For some GIFs, local palettes change this to a typically larger - * value. To be certain to get the correct color depth, call - * setDetermineImageNumber(true) before calling check(). - * The complete scan over the GIF file will take additional time.
  • + *
  • When the determination of the number of images is turned off, GIF bits + * per pixel are only read from the global header. + * For some GIFs, local palettes change this to a typically larger + * value. To be certain to get the correct color depth, call + * setDetermineImageNumber(true) before calling check(). + * The complete scan over the GIF file will take additional time.
  • *
  • Transparency information is not included in the bits per pixel count. - * Actually, it was my decision not to include those bits, so it's a feature! ;-)
  • + * Actually, it was my decision not to include those bits, so it's a feature! + * ;-) *
*

* Requirements: @@ -72,9 +78,12 @@ import java.util.Vector; *

  • Java 1.1 or higher
  • * *

    - * The latest version can be found at http://www.geocities.com/marcoschmidt.geo/image-info.html. + * The latest version can be found at http://www.geocities.com/marcoschmidt.geo/image-info.html. *

    - * Written by Marco Schmidt. + * Written by + * Marco + * Schmidt. *

    * This class is contributed to the Public Domain. * Use it at your own risk. @@ -84,39 +93,57 @@ import java.util.Vector; * History: *

      *
    • 2001-08-24 Initial version.
    • - *
    • 2001-10-13 Added support for the file formats BMP and PCX.
    • - *
    • 2001-10-16 Fixed bug in read(int[], int, int) that returned - *
    • 2002-01-22 Added support for file formats Amiga IFF and Sun Raster (RAS).
    • - *
    • 2002-01-24 Added support for file formats Portable Bitmap / Graymap / Pixmap (PBM, PGM, PPM) and Adobe Photoshop (PSD). - * Added new method getMimeType() to return the MIME type associated with a particular file format.
    • - *
    • 2002-03-15 Added support to recognize number of images in file. Only works with GIF. - * Use {@link #setDetermineImageNumber} with true as argument to identify animated GIFs - * ({@link #getNumberOfImages()} will return a value larger than 1).
    • - *
    • 2002-04-10 Fixed a bug in the feature 'determine number of images in animated GIF' introduced with version 1.1. - * Thanks to Marcelo P. Lima for sending in the bug report. - * Released as 1.1.1.
    • - *
    • 2002-04-18 Added {@link #setCollectComments(boolean)}. - * That new method lets the user specify whether textual comments are to be - * stored in an internal list when encountered in an input image file / stream. - * Added two methods to return the physical width and height of the image in dpi: - * {@link #getPhysicalWidthDpi()} and {@link #getPhysicalHeightDpi()}. - * If the physical resolution could not be retrieved, these methods return -1. - *
    • - *
    • 2002-04-23 Added support for the new properties physical resolution and - * comments for some formats. Released as 1.2.
    • - *
    • 2002-06-17 Added support for SWF, sent in by Michael Aird. - * Changed checkJpeg() so that other APP markers than APP0 will not lead to a failure anymore. - * Released as 1.3.
    • - *
    • 2003-07-28 Bug fix - skip method now takes return values into consideration. - * Less bytes than necessary may have been skipped, leading to flaws in the retrieved information in some cases. - * Thanks to Bernard Bernstein for pointing that out. - * Released as 1.4.
    • - *
    • 2004-02-29 Added support for recognizing progressive JPEG and - * interlaced PNG and GIF. A new method {@link #isProgressive()} returns whether ImageInfo - * has found that the storage type is progressive (or interlaced). - * Thanks to Joe Germuska for suggesting the feature. - * Bug fix: BMP physical resolution is now correctly determined. - * Released as 1.5.
    • + *
    • 2001-10-13 Added support for the file formats BMP and + * PCX.
    • + *
    • 2001-10-16 Fixed bug in read(int[], int, int) that + * returned + *
    • 2002-01-22 Added support for file formats Amiga IFF and + * Sun Raster (RAS).
    • + *
    • 2002-01-24 Added support for file formats Portable + * Bitmap / Graymap / Pixmap (PBM, PGM, PPM) and Adobe Photoshop (PSD). + * Added new method getMimeType() to return the MIME type associated with a + * particular file format.
    • + *
    • 2002-03-15 Added support to recognize number of images + * in file. Only works with GIF. + * Use {@link #setDetermineImageNumber} with true as argument to + * identify animated GIFs + * ({@link #getNumberOfImages()} will return a value larger than + * 1).
    • + *
    • 2002-04-10 Fixed a bug in the feature 'determine number + * of images in animated GIF' introduced with version 1.1. + * Thanks to Marcelo P. Lima for sending in the bug report. + * Released as 1.1.1.
    • + *
    • 2002-04-18 Added {@link #setCollectComments(boolean)}. + * That new method lets the user specify whether textual comments are to be + * stored in an internal list when encountered in an input image file / stream. + * Added two methods to return the physical width and height of the image in + * dpi: + * {@link #getPhysicalWidthDpi()} and {@link #getPhysicalHeightDpi()}. + * If the physical resolution could not be retrieved, these methods return + * -1. + *
    • + *
    • 2002-04-23 Added support for the new properties physical + * resolution and + * comments for some formats. Released as 1.2.
    • + *
    • 2002-06-17 Added support for SWF, sent in by Michael + * Aird. + * Changed checkJpeg() so that other APP markers than APP0 will not lead to a + * failure anymore. + * Released as 1.3.
    • + *
    • 2003-07-28 Bug fix - skip method now takes return values + * into consideration. + * Less bytes than necessary may have been skipped, leading to flaws in the + * retrieved information in some cases. + * Thanks to Bernard Bernstein for pointing that out. + * Released as 1.4.
    • + *
    • 2004-02-29 Added support for recognizing progressive + * JPEG and + * interlaced PNG and GIF. A new method {@link #isProgressive()} returns whether + * ImageInfo + * has found that the storage type is progressive (or interlaced). + * Thanks to Joe Germuska for suggesting the feature. + * Bug fix: BMP physical resolution is now correctly determined. + * Released as 1.5.
    • *
    */ public class ImageInfo { @@ -125,7 +152,7 @@ public class ImageInfo { * ImageInfo can extract physical resolution and comments * from JPEGs (only from APP0 headers). * Only one image can be stored in a file. - * It is determined whether the JPEG stream is progressive + * It is determined whether the JPEG stream is progressive * (see {@link #isProgressive()}). */ public static final int FORMAT_JPEG = 0; @@ -136,8 +163,10 @@ public class ImageInfo { * of images (GIFs with more than one image are animations). * If you know of a place where GIFs store the physical resolution * of an image, please - * send me a mail! - * It is determined whether the GIF stream is interlaced (see {@link #isProgressive()}). + * send me a + * mail! + * It is determined whether the GIF stream is interlaced (see + * {@link #isProgressive()}). */ public static final int FORMAT_GIF = 1; @@ -146,7 +175,8 @@ public class ImageInfo { * PNG only supports one image per file. * Both physical resolution and comments can be stored with PNG, * but ImageInfo is currently not able to extract those. - * It is determined whether the PNG stream is interlaced (see {@link #isProgressive()}). + * It is determined whether the PNG stream is interlaced (see + * {@link #isProgressive()}). */ public static final int FORMAT_PNG = 2; @@ -195,7 +225,7 @@ public class ImageInfo { public static final int COLOR_TYPE_UNKNOWN = -1; public static final int COLOR_TYPE_TRUECOLOR_RGB = 0; public static final int COLOR_TYPE_PALETTED = 1; - public static final int COLOR_TYPE_GRAYSCALE= 2; + public static final int COLOR_TYPE_GRAYSCALE = 2; public static final int COLOR_TYPE_BLACK_AND_WHITE = 3; /** @@ -203,32 +233,30 @@ public class ImageInfo { * The FORMAT_xyz int constants can be used as index values for * this array. */ - private static final String[] FORMAT_NAMES = - {"JPEG", "GIF", "PNG", "BMP", "PCX", - "IFF", "RAS", "PBM", "PGM", "PPM", - "PSD", "SWF"}; + private static final String[] FORMAT_NAMES = { "JPEG", "GIF", "PNG", "BMP", "PCX", + "IFF", "RAS", "PBM", "PGM", "PPM", + "PSD", "SWF" }; /** * The names of the MIME types for all supported file formats. * The FORMAT_xyz int constants can be used as index values for * this array. */ - private static final String[] MIME_TYPE_STRINGS = - {"image/jpeg", "image/gif", "image/png", "image/bmp", "image/pcx", - "image/iff", "image/ras", "image/x-portable-bitmap", "image/x-portable-graymap", "image/x-portable-pixmap", - "image/psd", "application/x-shockwave-flash"}; + private static final String[] MIME_TYPE_STRINGS = { "image/jpeg", "image/gif", "image/png", "image/bmp", + "image/pcx", + "image/iff", "image/ras", "image/x-portable-bitmap", "image/x-portable-graymap", "image/x-portable-pixmap", + "image/psd", "application/x-shockwave-flash" }; private int width; private int height; private int bitsPerPixel; - private int numColors; - private int colorType = COLOR_TYPE_UNKNOWN; + private int numColors; private boolean progressive; private int format; private InputStream in; private DataInput din; private boolean collectComments = true; - private Vector comments; + private Vector comments; private boolean determineNumberOfImages; private int numberOfImages; private int physicalHeightDpi; @@ -238,7 +266,7 @@ public class ImageInfo { private void addComment(String s) { if (comments == null) { - comments = new Vector(); + comments = new Vector(); } comments.addElement(s); } @@ -248,6 +276,7 @@ public class ImageInfo { * using {@link #setInput(InputStream)} or {@link #setInput(DataInput)}. * If true is returned, the file format was known and information * on the file's content can be retrieved using the various getXyz methods. + * * @return if information could be retrieved from input */ public boolean check() { @@ -258,51 +287,32 @@ public class ImageInfo { numberOfImages = 1; physicalHeightDpi = -1; physicalWidthDpi = -1; - numColors = -1; + numColors = -1; comments = null; try { int b1 = read() & 0xff; int b2 = read() & 0xff; if (b1 == 0x47 && b2 == 0x49) { return checkGif(); - } - else - if (b1 == 0x89 && b2 == 0x50) { + } else if (b1 == 0x89 && b2 == 0x50) { return checkPng(); - } - else - if (b1 == 0xff && b2 == 0xd8) { + } else if (b1 == 0xff && b2 == 0xd8) { return checkJpeg(); - } - else - if (b1 == 0x42 && b2 == 0x4d) { + } else if (b1 == 0x42 && b2 == 0x4d) { return checkBmp(); - } - else - if (b1 == 0x0a && b2 < 0x06) { + } else if (b1 == 0x0a && b2 < 0x06) { return checkPcx(); - } - else - if (b1 == 0x46 && b2 == 0x4f) { + } else if (b1 == 0x46 && b2 == 0x4f) { return checkIff(); - } - else - if (b1 == 0x59 && b2 == 0xa6) { + } else if (b1 == 0x59 && b2 == 0xa6) { return checkRas(); - } - else - if (b1 == 0x50 && b2 >= 0x31 && b2 <= 0x36) { + } else if (b1 == 0x50 && b2 >= 0x31 && b2 <= 0x36) { return checkPnm(b2 - '0'); - } - else - if (b1 == 0x38 && b2 == 0x42) { + } else if (b1 == 0x38 && b2 == 0x42) { return checkPsd(); - } - else - if (b1 == 0x46 && b2 == 0x57) { + } else if (b1 == 0x46 && b2 == 0x57) { return checkSwf(); - } - else { + } else { return false; } } catch (IOException ioe) { @@ -322,15 +332,15 @@ public class ImageInfo { } bitsPerPixel = getShortLittleEndian(a, 26); if (bitsPerPixel != 1 && bitsPerPixel != 4 && - bitsPerPixel != 8 && bitsPerPixel != 16 && - bitsPerPixel != 24 && bitsPerPixel != 32) { - return false; + bitsPerPixel != 8 && bitsPerPixel != 16 && + bitsPerPixel != 24 && bitsPerPixel != 32) { + return false; } - int x = (int)(getIntLittleEndian(a, 36) * 0.0254); + int x = (int) (getIntLittleEndian(a, 36) * 0.0254); if (x > 0) { setPhysicalWidthDpi(x); } - int y = (int)(getIntLittleEndian(a, 40) * 0.0254); + int y = (int) (getIntLittleEndian(a, 40) * 0.0254); if (y > 0) { setPhysicalHeightDpi(y); } @@ -339,14 +349,14 @@ public class ImageInfo { } private boolean checkGif() throws IOException { - final byte[] GIF_MAGIC_87A = {0x46, 0x38, 0x37, 0x61}; - final byte[] GIF_MAGIC_89A = {0x46, 0x38, 0x39, 0x61}; + final byte[] GIF_MAGIC_87A = { 0x46, 0x38, 0x37, 0x61 }; + final byte[] GIF_MAGIC_89A = { 0x46, 0x38, 0x39, 0x61 }; byte[] a = new byte[11]; // 4 from the GIF signature + 7 from the global header if (read(a) != 11) { return false; } if ((!equals(a, 0, GIF_MAGIC_89A, 0, 4)) && - (!equals(a, 0, GIF_MAGIC_87A, 0, 4))) { + (!equals(a, 0, GIF_MAGIC_87A, 0, 4))) { return false; } format = FORMAT_GIF; @@ -357,20 +367,18 @@ public class ImageInfo { progressive = (flags & 0x02) != 0; // skip global color palette if ((flags & 0x80) != 0) { - numColors = (1 << ((flags & 7) + 1)); + numColors = (1 << ((flags & 7) + 1)); skip(numColors * 3); } - if (!determineNumberOfImages) { - return true; - } + if (!determineNumberOfImages) { + return true; + } numberOfImages = 0; int blockType; - do - { + do { blockType = read(); - switch(blockType) - { - case(0x2c): // image separator + switch (blockType) { + case (0x2c): // image separator { if (read(a, 0, 9) != 9) { return false; @@ -381,37 +389,32 @@ public class ImageInfo { bitsPerPixel = localBitsPerPixel; } if ((flags & 0x80) != 0) { - int localNumColors = 1 << localBitsPerPixel; - skip(localNumColors * 3); - if (localNumColors > numColors) { - numColors = localNumColors; - } + int localNumColors = 1 << localBitsPerPixel; + skip(localNumColors * 3); + if (localNumColors > numColors) { + numColors = localNumColors; + } } skip(1); // initial code length int n; - do - { + do { n = read(); if (n > 0) { skip(n); - } - else - if (n == -1) { + } else if (n == -1) { return false; } - } - while (n > 0); + } while (n > 0); numberOfImages++; break; } - case(0x21): // extension + case (0x21): // extension { int extensionType = read(); if (collectComments && extensionType == 0xfe) { StringBuffer sb = new StringBuffer(); int n; - do - { + do { n = read(); if (n == -1) { return false; @@ -422,57 +425,50 @@ public class ImageInfo { if (ch == -1) { return false; } - sb.append((char)ch); + sb.append((char) ch); } } - } - while (n > 0); + } while (n > 0); } else { int n; - do - { + do { n = read(); if (n > 0) { skip(n); - } - else - if (n == -1) { + } else if (n == -1) { return false; } - } - while (n > 0); + } while (n > 0); } break; } - case(0x3b): // end of file + case (0x3b): // end of file { break; } - default: - { + default: { return false; } } - } - while (blockType != 0x3b); + } while (blockType != 0x3b); return true; } private boolean checkIff() throws IOException { byte[] a = new byte[10]; - // read remaining 2 bytes of file id, 4 bytes file size + // read remaining 2 bytes of file id, 4 bytes file size // and 4 bytes IFF subformat if (read(a, 0, 10) != 10) { return false; } - final byte[] IFF_RM = {0x52, 0x4d}; + final byte[] IFF_RM = { 0x52, 0x4d }; if (!equals(a, 0, IFF_RM, 0, 2)) { return false; } int type = getIntBigEndian(a, 6); if (type != 0x494c424d && // type must be ILBM... - type != 0x50424d20) { // ...or PBM - return false; + type != 0x50424d20) { // ...or PBM + return false; } // loop chunks to find BMHD chunk do { @@ -510,32 +506,28 @@ public class ImageInfo { if ((marker & 0xff00) != 0xff00) { return false; // not a valid marker } - if (marker == 0xffe0) { // APPx + if (marker == 0xffe0) { // APPx if (size < 14) { return false; // APPx header must be >= 14 bytes } if (read(data, 0, 12) != 12) { return false; } - final byte[] APP0_ID = {0x4a, 0x46, 0x49, 0x46, 0x00}; + final byte[] APP0_ID = { 0x4a, 0x46, 0x49, 0x46, 0x00 }; if (equals(APP0_ID, 0, data, 0, 5)) { - //System.out.println("data 7=" + data[7]); + // System.out.println("data 7=" + data[7]); if (data[7] == 1) { setPhysicalWidthDpi(getShortBigEndian(data, 8)); setPhysicalHeightDpi(getShortBigEndian(data, 10)); - } - else - if (data[7] == 2) { + } else if (data[7] == 2) { int x = getShortBigEndian(data, 8); int y = getShortBigEndian(data, 10); - setPhysicalWidthDpi((int)(x * 2.54f)); - setPhysicalHeightDpi((int)(y * 2.54f)); + setPhysicalWidthDpi((int) (x * 2.54f)); + setPhysicalHeightDpi((int) (y * 2.54f)); } } skip(size - 14); - } - else - if (collectComments && size > 2 && marker == 0xfffe) { // comment + } else if (collectComments && size > 2 && marker == 0xfffe) { // comment size -= 2; byte[] chars = new byte[size]; if (read(chars, 0, size) != size) { @@ -544,16 +536,14 @@ public class ImageInfo { String comment = new String(chars, "iso-8859-1"); comment = comment.trim(); addComment(comment); - } - else - if (marker >= 0xffc0 && marker <= 0xffcf && marker != 0xffc4 && marker != 0xffc8) { + } else if (marker >= 0xffc0 && marker <= 0xffcf && marker != 0xffc4 && marker != 0xffc8) { if (read(data, 0, 6) != 6) { return false; } format = FORMAT_JPEG; bitsPerPixel = (data[0] & 0xff) * (data[5] & 0xff); progressive = marker == 0xffc2 || marker == 0xffc6 || - marker == 0xffca || marker == 0xffce; + marker == 0xffca || marker == 0xffce; width = getShortBigEndian(data, 3); height = getShortBigEndian(data, 1); return true; @@ -585,11 +575,10 @@ public class ImageInfo { int bits = a[1]; int planes = a[63]; if (planes == 1 && - (bits == 1 || bits == 2 || bits == 4 || bits == 8)) { + (bits == 1 || bits == 2 || bits == 4 || bits == 8)) { // paletted bitsPerPixel = bits; - } else - if (planes == 3 && bits == 8) { + } else if (planes == 3 && bits == 8) { // RGB truecolor bitsPerPixel = 24; } else { @@ -602,7 +591,7 @@ public class ImageInfo { } private boolean checkPng() throws IOException { - final byte[] PNG_MAGIC = {0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a}; + final byte[] PNG_MAGIC = { 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a }; byte[] a = new byte[27]; if (read(a) != 27) { return false; @@ -626,12 +615,11 @@ public class ImageInfo { if (id < 1 || id > 6) { return false; } - final int[] PNM_FORMATS = {FORMAT_PBM, FORMAT_PGM, FORMAT_PPM}; + final int[] PNM_FORMATS = { FORMAT_PBM, FORMAT_PGM, FORMAT_PPM }; format = PNM_FORMATS[(id - 1) % 3]; boolean hasPixelResolution = false; String s; - while (true) - { + while (true) { s = readLine(); if (s != null) { s = s.trim(); @@ -670,9 +658,7 @@ public class ImageInfo { return true; } hasPixelResolution = true; - } - else - { + } else { int maxSample; try { maxSample = Integer.parseInt(s); @@ -701,7 +687,7 @@ public class ImageInfo { if (read(a) != a.length) { return false; } - final byte[] PSD_MAGIC = {0x50, 0x53}; + final byte[] PSD_MAGIC = { 0x50, 0x53 }; if (!equals(a, 0, PSD_MAGIC, 0, 2)) { return false; } @@ -719,7 +705,7 @@ public class ImageInfo { if (read(a) != a.length) { return false; } - final byte[] RAS_MAGIC = {0x6a, (byte)0x95}; + final byte[] RAS_MAGIC = { 0x6a, (byte) 0x95 }; if (!equals(a, 0, RAS_MAGIC, 0, 2)) { return false; } @@ -732,19 +718,18 @@ public class ImageInfo { // Written by Michael Aird. private boolean checkSwf() throws IOException { - //get rid of the last byte of the signature, the byte of the version and 4 bytes of the size + // get rid of the last byte of the signature, the byte of the version and 4 + // bytes of the size byte[] a = new byte[6]; if (read(a) != a.length) { return false; } format = FORMAT_SWF; - int bitSize = (int)readUBits( 5 ); - int minX = (int)readSBits( bitSize ); - int maxX = (int)readSBits( bitSize ); - int minY = (int)readSBits( bitSize ); - int maxY = (int)readSBits( bitSize ); - width = maxX/20; //cause we're in twips - height = maxY/20; //cause we're in twips + int bitSize = (int) readUBits(5); + int maxX = (int) readSBits(bitSize); + int maxY = (int) readSBits(bitSize); + width = maxX / 20; // cause we're in twips + height = maxY / 20; // cause we're in twips setPhysicalWidthDpi(72); setPhysicalHeightDpi(72); return (width > 0 && height > 0); @@ -774,39 +759,44 @@ public class ImageInfo { return true; } - /** - * If {@link #check()} was successful, returns the image's number of bits per pixel. + /** + * If {@link #check()} was successful, returns the image's number of bits per + * pixel. * Does not include transparency information like the alpha channel. + * * @return number of bits per image pixel */ public int getBitsPerPixel() { return bitsPerPixel; } - /** - * @return number of colors, for palette based images - */ - public int getNumColors() { - return numColors; - } + /** + * @return number of colors, for palette based images + */ + public int getNumColors() { + return numColors; + } - /** + /** * Returns the index'th comment retrieved from the image. - * @throws IllegalArgumentException if index is smaller than 0 or larger than or equal - * to the number of comments retrieved + * + * @throws IllegalArgumentException if index is smaller than 0 or larger than or + * equal + * to the number of comments retrieved * @see #getNumberOfComments */ public String getComment(int index) { if (comments == null || index < 0 || index >= comments.size()) { throw new IllegalArgumentException("Not a valid comment index: " + index); } - return (String)comments.elementAt(index); + return (String) comments.elementAt(index); } /** * If {@link #check()} was successful, returns the image format as one * of the FORMAT_xyz constants from this class. * Use {@link #getFormatName()} to get a textual description of the file format. + * * @return file format as a FORMAT_xyz constant */ public int getFormat() { @@ -816,6 +806,7 @@ public class ImageInfo { /** * If {@link #check()} was successful, returns the image format's name. * Use {@link #getFormat()} to get a unique number. + * * @return file format name */ public String getFormatName() { @@ -826,9 +817,10 @@ public class ImageInfo { } } - /** + /** * If {@link #check()} was successful, returns one the image's vertical * resolution in pixels. + * * @return image height in pixels */ public int getHeight() { @@ -836,30 +828,28 @@ public class ImageInfo { } private int getIntBigEndian(byte[] a, int offs) { - return - (a[offs] & 0xff) << 24 | - (a[offs + 1] & 0xff) << 16 | - (a[offs + 2] & 0xff) << 8 | - a[offs + 3] & 0xff; + return (a[offs] & 0xff) << 24 | + (a[offs + 1] & 0xff) << 16 | + (a[offs + 2] & 0xff) << 8 | + a[offs + 3] & 0xff; } private int getIntLittleEndian(byte[] a, int offs) { - return - (a[offs + 3] & 0xff) << 24 | - (a[offs + 2] & 0xff) << 16 | - (a[offs + 1] & 0xff) << 8 | - a[offs] & 0xff; + return (a[offs + 3] & 0xff) << 24 | + (a[offs + 2] & 0xff) << 16 | + (a[offs + 1] & 0xff) << 8 | + a[offs] & 0xff; } - /** + /** * If {@link #check()} was successful, returns a String with the * MIME type of the format. + * * @return MIME type, e.g. image/jpeg */ public String getMimeType() { if (format >= 0 && format < MIME_TYPE_STRINGS.length) { - if (format == FORMAT_JPEG && progressive) - { + if (format == FORMAT_JPEG && progressive) { return "image/pjpeg"; } return MIME_TYPE_STRINGS[format]; @@ -869,15 +859,16 @@ public class ImageInfo { } /** - * If {@link #check()} was successful and {@link #setCollectComments(boolean)} was called with - * true as argument, returns the number of comments retrieved + * If {@link #check()} was successful and {@link #setCollectComments(boolean)} + * was called with + * true as argument, returns the number of comments retrieved * from the input image stream / file. * Any number >= 0 and smaller than this number of comments is then a * valid argument for the {@link #getComment(int)} method. + * * @return number of comments retrieved from input image */ - public int getNumberOfComments() - { + public int getNumberOfComments() { if (comments == null) { return 0; } else { @@ -889,11 +880,12 @@ public class ImageInfo { * Returns the number of images in the examined file. * Assumes that setDetermineImageNumber(true); was called before * a successful call to {@link #check()}. - * This value can currently be only different from 1 for GIF images. + * This value can currently be only different from 1 for GIF + * images. + * * @return number of images in file */ - public int getNumberOfImages() - { + public int getNumberOfImages() { return numberOfImages; } @@ -901,6 +893,7 @@ public class ImageInfo { * Returns the physical height of this image in dots per inch (dpi). * Assumes that {@link #check()} was successful. * Returns -1 on failure. + * * @return physical height (in dpi) * @see #getPhysicalWidthDpi() * @see #getPhysicalHeightInch() @@ -910,8 +903,10 @@ public class ImageInfo { } /** - * If {@link #check()} was successful, returns the physical width of this image in dpi (dots per inch) + * If {@link #check()} was successful, returns the physical width of this image + * in dpi (dots per inch) * or -1 if no value could be found. + * * @return physical height (in dpi) * @see #getPhysicalHeightDpi() * @see #getPhysicalWidthDpi() @@ -921,15 +916,17 @@ public class ImageInfo { int h = getHeight(); int ph = getPhysicalHeightDpi(); if (h > 0 && ph > 0) { - return ((float)h) / ((float)ph); + return ((float) h) / ((float) ph); } else { return -1.0f; } } /** - * If {@link #check()} was successful, returns the physical width of this image in dpi (dots per inch) + * If {@link #check()} was successful, returns the physical width of this image + * in dpi (dots per inch) * or -1 if no value could be found. + * * @return physical width (in dpi) * @see #getPhysicalHeightDpi() * @see #getPhysicalWidthInch() @@ -943,6 +940,7 @@ public class ImageInfo { * Returns the physical width of an image in inches, or * -1.0f if width information is not available. * Assumes that {@link #check} has been called successfully. + * * @return physical width in inches or -1.0f on failure * @see #getPhysicalWidthDpi * @see #getPhysicalHeightInch @@ -951,25 +949,25 @@ public class ImageInfo { int w = getWidth(); int pw = getPhysicalWidthDpi(); if (w > 0 && pw > 0) { - return ((float)w) / ((float)pw); + return ((float) w) / ((float) pw); } else { return -1.0f; } } private int getShortBigEndian(byte[] a, int offs) { - return - (a[offs] & 0xff) << 8 | - (a[offs + 1] & 0xff); + return (a[offs] & 0xff) << 8 | + (a[offs + 1] & 0xff); } private int getShortLittleEndian(byte[] a, int offs) { return (a[offs] & 0xff) | (a[offs + 1] & 0xff) << 8; } - /** + /** * If {@link #check()} was successful, returns one the image's horizontal * resolution in pixels. + * * @return image width in pixels */ public int getWidth() { @@ -977,20 +975,22 @@ public class ImageInfo { } /** - * Returns whether the image is stored in a progressive (also called: interlaced) way. + * Returns whether the image is stored in a progressive (also called: + * interlaced) way. + * * @return true for progressive/interlaced, false otherwise */ - public boolean isProgressive() - { + public boolean isProgressive() { return progressive; } /** - * To use this class as a command line application, give it either + * To use this class as a command line application, give it either * some file names as parameters (information on them will be * printed to standard output, one line per file) or call * it with no parameters. It will then check data given to it * via standard input. + * * @param args the program arguments which must be file names */ public static void main(String[] args) { @@ -1034,18 +1034,17 @@ public class ImageInfo { private static void printCompact(String sourceName, ImageInfo imageInfo) { System.out.println( - imageInfo.getFormatName() + ";" + - imageInfo.getMimeType() + ";" + - imageInfo.getWidth() + ";" + - imageInfo.getHeight() + ";" + - imageInfo.getBitsPerPixel() + ";" + - imageInfo.getNumberOfImages() + ";" + - imageInfo.getPhysicalWidthDpi() + ";" + - imageInfo.getPhysicalHeightDpi() + ";" + - imageInfo.getPhysicalWidthInch() + ";" + - imageInfo.getPhysicalHeightInch() + ";" + - imageInfo.isProgressive() - ); + imageInfo.getFormatName() + ";" + + imageInfo.getMimeType() + ";" + + imageInfo.getWidth() + ";" + + imageInfo.getHeight() + ";" + + imageInfo.getBitsPerPixel() + ";" + + imageInfo.getNumberOfImages() + ";" + + imageInfo.getPhysicalWidthDpi() + ";" + + imageInfo.getPhysicalHeightDpi() + ";" + + imageInfo.getPhysicalWidthInch() + ";" + + imageInfo.getPhysicalHeightInch() + ";" + + imageInfo.isProgressive()); } private static void printLine(int indentLevels, String text, float value, float minValidValue) { @@ -1133,20 +1132,19 @@ public class ImageInfo { int value = read(); finished = (value == -1 || value == 10); if (!finished) { - sb.append((char)value); + sb.append((char) value); } } while (!finished); return sb.toString(); } - private long readUBits( int numBits ) throws IOException - { + private long readUBits(int numBits) throws IOException { if (numBits == 0) { return 0; } int bitsLeft = numBits; long result = 0; - if (bitPos == 0) { //no value in the buffer - read a byte + if (bitPos == 0) { // no value in the buffer - read a byte if (in != null) { bitBuf = in.read(); } else { @@ -1154,64 +1152,46 @@ public class ImageInfo { } bitPos = 8; } - - while( true ) - { - int shift = bitsLeft - bitPos; - if( shift > 0 ) - { - // Consume the entire buffer - result |= bitBuf << shift; - bitsLeft -= bitPos; - // Get the next byte from the input stream - if (in != null) { - bitBuf = in.read(); - } else { - bitBuf = din.readByte(); - } - bitPos = 8; - } - else - { - // Consume a portion of the buffer - result |= bitBuf >> -shift; - bitPos -= bitsLeft; - bitBuf &= 0xff >> (8 - bitPos); // mask off the consumed bits + while (true) { + int shift = bitsLeft - bitPos; + if (shift > 0) { + // Consume the entire buffer + result |= bitBuf << shift; + bitsLeft -= bitPos; - return result; - } - } - } - - /** - * Read a signed value from the given number of bits - */ - private int readSBits( int numBits ) throws IOException - { - // Get the number as an unsigned value. - long uBits = readUBits( numBits ); + // Get the next byte from the input stream + if (in != null) { + bitBuf = in.read(); + } else { + bitBuf = din.readByte(); + } + bitPos = 8; + } else { + // Consume a portion of the buffer + result |= bitBuf >> -shift; + bitPos -= bitsLeft; + bitBuf &= 0xff >> (8 - bitPos); // mask off the consumed bits - // Is the number negative? - if( ( uBits & (1L << (numBits - 1))) != 0 ) - { - // Yes. Extend the sign. - uBits |= -1L << numBits; - } - - return (int)uBits; - } - - private void synchBits() - { - bitBuf = 0; - bitPos = 0; + return result; + } + } } - private String readLine(int firstChar) throws IOException { - StringBuffer result = new StringBuffer(); - result.append((char)firstChar); - return readLine(result); + /** + * Read a signed value from the given number of bits + */ + private int readSBits(int numBits) throws IOException { + // Get the number as an unsigned value. + long uBits = readUBits(numBits); + + // Is the number negative? + if ((uBits & (1L << (numBits - 1))) != 0) { + // Yes. Extend the sign. + uBits |= -1L << numBits; + } + + return (int) uBits; } private static void run(String sourceName, InputStream in, ImageInfo imageInfo, boolean verbose) { @@ -1227,12 +1207,12 @@ public class ImageInfo { * Specify whether textual comments are supposed to be extracted from input. * Default is false. * If enabled, comments will be added to an internal list. + * * @param newValue if true, this class will read comments * @see #getNumberOfComments * @see #getComment */ - public void setCollectComments(boolean newValue) - { + public void setCollectComments(boolean newValue) { collectComments = newValue; } @@ -1244,21 +1224,22 @@ public class ImageInfo { * task. * Not all file formats support more than one image. * If this method is called with true as argument, - * the actual number of images can be queried via + * the actual number of images can be queried via * {@link #getNumberOfImages()} after a successful call to * {@link #check()}. + * * @param newValue will the number of images be determined? * @see #getNumberOfImages */ - public void setDetermineImageNumber(boolean newValue) - { + public void setDetermineImageNumber(boolean newValue) { determineNumberOfImages = newValue; } /** - * Set the input stream to the argument stream (or file). + * Set the input stream to the argument stream (or file). * Note that {@link java.io.RandomAccessFile} implements * {@link java.io.DataInput}. + * * @param dataInput the input stream to read from */ public void setInput(DataInput dataInput) { @@ -1268,6 +1249,7 @@ public class ImageInfo { /** * Set the input stream to the argument stream (or file). + * * @param inputStream the input stream to read from */ public void setInput(InputStream inputStream) { diff --git a/src/main/java/helma/image/imageio/gif/GIFImageWriterSpi.java b/src/main/java/helma/image/imageio/gif/GIFImageWriterSpi.java index 778b7ce7..d2f349c0 100644 --- a/src/main/java/helma/image/imageio/gif/GIFImageWriterSpi.java +++ b/src/main/java/helma/image/imageio/gif/GIFImageWriterSpi.java @@ -24,9 +24,10 @@ import java.io.*; import java.util.*; import javax.imageio.*; import javax.imageio.spi.*; +import javax.imageio.stream.ImageOutputStream; public class GIFImageWriterSpi extends ImageWriterSpi { - + public GIFImageWriterSpi() { super( "Helma Object Publisher, http://helma.org/", @@ -35,7 +36,7 @@ public class GIFImageWriterSpi extends ImageWriterSpi { new String[] {"gif", "GIF"}, new String[] {"image/gif", "image/x-gif"}, "helma.image.imageio.gif.GIFImageWriter", - STANDARD_OUTPUT_TYPE, + new Class[] {ImageOutputStream.class}, null, false, null, null, null, null, false, null, null, null, null @@ -55,4 +56,4 @@ public class GIFImageWriterSpi extends ImageWriterSpi { // FIXME handle # colors return true; } -} \ No newline at end of file +} diff --git a/src/main/java/helma/main/ApplicationManager.java b/src/main/java/helma/main/ApplicationManager.java index 2deb88ee..5ca5927f 100644 --- a/src/main/java/helma/main/ApplicationManager.java +++ b/src/main/java/helma/main/ApplicationManager.java @@ -36,9 +36,9 @@ import helma.util.StringUtils; * This class is responsible for starting and stopping Helma applications. */ public class ApplicationManager implements XmlRpcHandler { - private Hashtable descriptors; - private Hashtable applications; - private Hashtable xmlrpcHandlers; + private Hashtable descriptors; + private Hashtable applications; + private Hashtable xmlrpcHandlers; private ResourceProperties props; private Server server; private long lastModified; @@ -54,9 +54,9 @@ public class ApplicationManager implements XmlRpcHandler { public ApplicationManager(ResourceProperties props, Server server) { this.props = props; this.server = server; - this.descriptors = new Hashtable(); - this.applications = new Hashtable(); - this.xmlrpcHandlers = new Hashtable(); + this.descriptors = new Hashtable(); + this.applications = new Hashtable(); + this.xmlrpcHandlers = new Hashtable(); this.lastModified = 0; this.jetty = server.jetty; } @@ -68,7 +68,7 @@ public class ApplicationManager implements XmlRpcHandler { protected void checkForChanges() { if (this.props.lastModified() > this.lastModified && this.server.getApplicationsOption() == null) { try { - for (Enumeration e = this.props.keys(); e.hasMoreElements();) { + for (Enumeration e = this.props.keys(); e.hasMoreElements();) { String appName = (String) e.nextElement(); if ((appName.indexOf(".") == -1) && //$NON-NLS-1$ @@ -80,7 +80,7 @@ public class ApplicationManager implements XmlRpcHandler { } // then stop deleted ones - for (Enumeration e = this.descriptors.elements(); e.hasMoreElements();) { + for (Enumeration e = this.descriptors.elements(); e.hasMoreElements();) { AppDescriptor appDesc = (AppDescriptor) e.nextElement(); // check if application has been removed and should be stopped @@ -146,7 +146,7 @@ public class ApplicationManager implements XmlRpcHandler { desc.start(); } } else { - for (Enumeration e = this.props.keys(); e.hasMoreElements();) { + for (Enumeration e = this.props.keys(); e.hasMoreElements();) { String appName = (String) e.nextElement(); if (appName.indexOf(".") == -1) { //$NON-NLS-1$ @@ -162,7 +162,7 @@ public class ApplicationManager implements XmlRpcHandler { } } - for (Enumeration e = this.descriptors.elements(); e.hasMoreElements();) { + for (Enumeration e = this.descriptors.elements(); e.hasMoreElements();) { AppDescriptor appDesc = (AppDescriptor) e.nextElement(); appDesc.bind(); } @@ -178,7 +178,7 @@ public class ApplicationManager implements XmlRpcHandler { * Stop all running applications. */ public void stopAll() { - for (Enumeration en = this.descriptors.elements(); en.hasMoreElements();) { + for (Enumeration en = this.descriptors.elements(); en.hasMoreElements();) { try { AppDescriptor appDesc = (AppDescriptor) en.nextElement(); @@ -206,7 +206,7 @@ public class ApplicationManager implements XmlRpcHandler { /** * Implements org.apache.xmlrpc.XmlRpcHandler.execute() */ - public Object execute(String method, Vector params) + public Object execute(String method, @SuppressWarnings("rawtypes") Vector params) throws Exception { int dot = method.indexOf("."); //$NON-NLS-1$ @@ -365,8 +365,8 @@ public class ApplicationManager implements XmlRpcHandler { this.ignoreDirs = conf.getProperty("ignore"); //$NON-NLS-1$ // read and configure app repositories - ArrayList repositoryList = new ArrayList(); - Class[] parameters = { String.class }; + ArrayList repositoryList = new ArrayList<>(); + Class[] parameters = { String.class }; for (int i = 0; true; i++) { String repositoryArgs = conf.getProperty("repository." + i); //$NON-NLS-1$ @@ -491,15 +491,16 @@ public class ApplicationManager implements XmlRpcHandler { rhandler.setResourceBase(staticContent.getPath()); rhandler.setWelcomeFiles(staticHome); - staticContext = ApplicationManager.this.context.addContext(staticMountpoint, ""); //$NON-NLS-1$ + staticContext = new ContextHandler(staticMountpoint); + ApplicationManager.this.context.addHandler(staticContext); staticContext.setHandler(rhandler); staticContext.start(); } appContext = new ServletContextHandler(context, pathPattern, true, true); - Class servletClass = servletClassName == null ? - EmbeddedServletClient.class : Class.forName(servletClassName); + Class servletClass = servletClassName == null ? + EmbeddedServletClient.class : Class.forName(servletClassName).asSubclass(EmbeddedServletClient.class); ServletHolder holder = new ServletHolder(servletClass); holder.setInitParameter("application", appName); appContext.addServlet(holder, "/*"); diff --git a/src/main/java/helma/main/CommandlineRunner.java b/src/main/java/helma/main/CommandlineRunner.java index aeaaee1f..eac3e922 100644 --- a/src/main/java/helma/main/CommandlineRunner.java +++ b/src/main/java/helma/main/CommandlineRunner.java @@ -41,10 +41,10 @@ public class CommandlineRunner { ServerConfig config = new ServerConfig(); String commandStr = null; - Vector funcArgs = new Vector(); - + Vector funcArgs = new Vector<>(); + // get possible environment setting for helma home - if (System.getProperty("helma.home")!=null) { + if (System.getProperty("helma.home") != null) { config.setHomeDir(new File(System.getProperty("helma.home"))); } @@ -110,7 +110,7 @@ public class CommandlineRunner { server.shutdown(); } - + /** * print the usage hints and prefix them with a message. diff --git a/src/main/java/helma/main/HelmaSecurityManager.java b/src/main/java/helma/main/HelmaSecurityManager.java index aebf5400..61dc57c1 100644 --- a/src/main/java/helma/main/HelmaSecurityManager.java +++ b/src/main/java/helma/main/HelmaSecurityManager.java @@ -30,11 +30,13 @@ import java.util.HashSet; * a utility method getApplication that can be used to determine * the name of the application trying to execute the action in question, if any. */ +@Deprecated +@SuppressWarnings("removal") public class HelmaSecurityManager extends SecurityManager { // The set of actions forbidden to application code. - // We are pretty permissive, forbidding only System.exit() + // We are pretty permissive, forbidding only System.exit() // and setting the security manager. - private final static HashSet forbidden = new HashSet(); + private final static HashSet forbidden = new HashSet<>(); static { forbidden.add("exitVM"); @@ -49,7 +51,7 @@ public class HelmaSecurityManager extends SecurityManager { public void checkPermission(Permission p) { if (p instanceof RuntimePermission) { if (forbidden.contains(p.getName())) { - Class[] classes = getClassContext(); + Class[] classes = getClassContext(); for (int i = 0; i < classes.length; i++) { if (classes[i].getClassLoader() instanceof AppClassLoader) { @@ -98,7 +100,7 @@ public class HelmaSecurityManager extends SecurityManager { * @param status ... */ public void checkExit(int status) { - Class[] classes = getClassContext(); + Class[] classes = getClassContext(); for (int i = 0; i < classes.length; i++) { if (classes[i].getClassLoader() instanceof AppClassLoader) { @@ -287,7 +289,7 @@ public class HelmaSecurityManager extends SecurityManager { * @param clazz ... * @param which ... */ - public void checkMemberAccess(Class clazz, int which) { + public void checkMemberAccess(Class clazz, int which) { } /** @@ -304,7 +306,7 @@ public class HelmaSecurityManager extends SecurityManager { * does not belong to any application. */ protected String getApplication() { - Class[] classes = getClassContext(); + Class[] classes = getClassContext(); for (int i = 0; i < classes.length; i++) { if (classes[i].getClassLoader() instanceof AppClassLoader) { diff --git a/src/main/java/helma/main/HelmaShutdownHook.java b/src/main/java/helma/main/HelmaShutdownHook.java index f3eff903..696f1176 100644 --- a/src/main/java/helma/main/HelmaShutdownHook.java +++ b/src/main/java/helma/main/HelmaShutdownHook.java @@ -16,9 +16,6 @@ package helma.main; -import helma.util.*; -import org.apache.commons.logging.LogFactory; - /** * ShutdownHook that shuts down all running Helma applications on exit. */ diff --git a/src/main/java/helma/main/JettyServer.java b/src/main/java/helma/main/JettyServer.java index 88ba8c10..6c777224 100644 --- a/src/main/java/helma/main/JettyServer.java +++ b/src/main/java/helma/main/JettyServer.java @@ -21,6 +21,7 @@ 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.xml.XmlConfiguration; import java.net.URL; @@ -47,7 +48,7 @@ public class JettyServer { http = new org.eclipse.jetty.server.Server(); try { - XmlConfiguration config = new XmlConfiguration(url); + XmlConfiguration config = new XmlConfiguration(Resource.newResource(url)); config.configure(http); } catch (IOException e) { @@ -59,7 +60,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 @@ -73,7 +74,7 @@ public class JettyServer { connector.setHost(webPort.getAddress().getHostAddress()); connector.setPort(webPort.getPort()); connector.setIdleTimeout(30000); - connector.setSoLingerTime(-1); + // Removed deprecated method setSoLingerTime connector.setAcceptorPriorityDelta(0); connector.setAcceptQueueSize(0); diff --git a/src/main/java/helma/main/Server.java b/src/main/java/helma/main/Server.java index 88d70db7..62fabf3b 100644 --- a/src/main/java/helma/main/Server.java +++ b/src/main/java/helma/main/Server.java @@ -29,8 +29,6 @@ import java.io.*; import java.util.*; import java.net.*; -import helma.util.ResourceProperties; - /** * Helma server main class. */ @@ -67,14 +65,14 @@ public class Server implements Runnable { // explicitly listed hosts. public boolean paranoid; private ApplicationManager appManager; - private Vector extensions; + private Vector extensions; private Thread mainThread; // configuration ServerConfig config; // map of server-wide database sources - Hashtable dbSources; + Hashtable dbSources; // the embedded web server // protected Serve websrv; @@ -432,10 +430,10 @@ public class Server implements Runnable { // logger.debug("TimeZone = " + // TimeZone.getDefault().getDisplayName(Locale.getDefault())); - dbSources = new Hashtable(); + dbSources = new Hashtable(); // try to load the extensions - extensions = new Vector(); + extensions = new Vector(); if (sysProps.getProperty("extensions") != null) { initExtensions(); } @@ -452,8 +450,8 @@ public class Server implements Runnable { String extClassName = tok.nextToken().trim(); try { - Class extClass = Class.forName(extClassName); - HelmaExtension ext = (HelmaExtension) extClass.newInstance(); + Class extClass = Class.forName(extClassName).asSubclass(HelmaExtension.class); + HelmaExtension ext = (HelmaExtension) extClass.getDeclaredConstructor().newInstance(); ext.init(this); extensions.add(ext); logger.info("Loaded: " + extClassName); @@ -572,7 +570,9 @@ public class Server implements Runnable { String secManClass = sysProps.getProperty("securityManager"); if (secManClass != null) { + @SuppressWarnings("removal") SecurityManager secMan = (SecurityManager) Class.forName(secManClass) + .getDeclaredConstructor() .newInstance(); System.setSecurityManager(secMan); @@ -764,7 +764,7 @@ public class Server implements Runnable { * * @return ... */ - public Vector getExtensions() { + public Vector getExtensions() { return extensions; } @@ -805,5 +805,3 @@ public class Server implements Runnable { return new InetSocketAddress(addr, port); } } - - diff --git a/src/main/java/helma/objectmodel/TransientNode.java b/src/main/java/helma/objectmodel/TransientNode.java index e1410e17..011f8a35 100644 --- a/src/main/java/helma/objectmodel/TransientNode.java +++ b/src/main/java/helma/objectmodel/TransientNode.java @@ -18,7 +18,6 @@ package helma.objectmodel; import helma.framework.IPathElement; import helma.framework.core.Application; -import helma.framework.core.RequestEvaluator; import helma.objectmodel.db.DbMapping; import helma.objectmodel.db.Relation; import helma.objectmodel.db.Node; @@ -64,7 +63,8 @@ public class TransientNode implements INode, Serializable { created = lastmodified = System.currentTimeMillis(); this.app=app; } - + + @SuppressWarnings("unused") private TransientNode() { app=null; } @@ -75,7 +75,7 @@ public class TransientNode implements INode, Serializable { public TransientNode(Application app, String n) { id = generateID(); name = (n == null || n.length() == 0) ? id : n; - // HACK - decrease creation and last-modified timestamp by 1 so we notice + // HACK - decrease creation and last-modified timestamp by 1 so we notice // modifications that take place immediately after object creation created = lastmodified = System.currentTimeMillis() - 1; this.app = app; @@ -378,7 +378,7 @@ public class TransientNode implements INode, Serializable { } private TransientProperty getProperty(String propname) { - TransientProperty prop = (propMap == null) ? null + TransientProperty prop = (propMap == null) ? null : (TransientProperty) propMap.get(correctPropertyName(propname)); // check if we have to create a virtual node @@ -601,7 +601,7 @@ public class TransientNode implements INode, Serializable { public synchronized void clearCacheNode() { cacheNode = null; } - + private String correctPropertyName(String propname) { return app.correctPropertyName(propname); } diff --git a/src/main/java/helma/objectmodel/db/NodeManager.java b/src/main/java/helma/objectmodel/db/NodeManager.java index 83187981..5ba0edcd 100644 --- a/src/main/java/helma/objectmodel/db/NodeManager.java +++ b/src/main/java/helma/objectmodel/db/NodeManager.java @@ -22,6 +22,7 @@ import helma.objectmodel.*; import helma.objectmodel.dom.XmlDatabase; import java.io.*; +import java.lang.reflect.InvocationTargetException; import java.math.BigDecimal; import java.sql.*; import java.util.*; @@ -59,19 +60,25 @@ public final class NodeManager { * Initialize the NodeManager for the given dbHome and * application properties. An embedded database will be * created in dbHome if one doesn't already exist. + * @throws SecurityException + * @throws NoSuchMethodException + * @throws InvocationTargetException + * @throws IllegalArgumentException */ public void init(File dbHome, Properties props) throws DatabaseException, ClassNotFoundException, - IllegalAccessException, InstantiationException { + IllegalAccessException, InstantiationException, + IllegalArgumentException, InvocationTargetException, + NoSuchMethodException, SecurityException { String cacheImpl = props.getProperty("cacheimpl", "helma.util.CacheMap"); - cache = (ObjectCache) Class.forName(cacheImpl).newInstance(); + cache = (ObjectCache) Class.forName(cacheImpl).getDeclaredConstructor().newInstance(); cache.init(app); String idgenImpl = props.getProperty("idGeneratorImpl"); if (idgenImpl != null) { - idgen = (IDGenerator) Class.forName(idgenImpl).newInstance(); + idgen = (IDGenerator) Class.forName(idgenImpl).getDeclaredConstructor().newInstance(); idgen.init(app); } diff --git a/src/main/java/helma/objectmodel/db/SubnodeList.java b/src/main/java/helma/objectmodel/db/SubnodeList.java index bec94317..f69fd10b 100644 --- a/src/main/java/helma/objectmodel/db/SubnodeList.java +++ b/src/main/java/helma/objectmodel/db/SubnodeList.java @@ -32,11 +32,12 @@ public class SubnodeList implements Serializable { transient protected long lastSubnodeFetch = 0; transient protected long lastSubnodeChange = 0; - + /** * Hide/disable zero argument constructor for subclasses */ + @SuppressWarnings("unused") private SubnodeList() {} /** diff --git a/src/main/java/helma/objectmodel/db/Transactor.java b/src/main/java/helma/objectmodel/db/Transactor.java index 74f5e5c7..5e324c0e 100644 --- a/src/main/java/helma/objectmodel/db/Transactor.java +++ b/src/main/java/helma/objectmodel/db/Transactor.java @@ -463,8 +463,6 @@ public class Transactor { node.clearWriteLock(); } - long now = System.currentTimeMillis(); - // set last subnode change times in parent nodes for (Iterator i = parentNodes.iterator(); i.hasNext(); ) { Node node = (Node) i.next(); diff --git a/src/main/java/helma/scripting/ScriptingException.java b/src/main/java/helma/scripting/ScriptingException.java index 2505891b..3a3d7b36 100644 --- a/src/main/java/helma/scripting/ScriptingException.java +++ b/src/main/java/helma/scripting/ScriptingException.java @@ -23,12 +23,12 @@ import java.io.*; /** * The base class for wrapped exceptions thrown by invocation of the scripting engine. * If the wrapped exception is a RhinoException, the script stack trace will be - * prepended to the actual java stack trace in stack dumps. + * prepended to the actual java stack trace in stack dumps. */ public class ScriptingException extends Exception { private static final long serialVersionUID = -7191341724784015678L; - + String scriptStack = null; /** @@ -48,14 +48,7 @@ public class ScriptingException extends Exception { */ private void setScriptStack(Throwable cause) { if (cause instanceof RhinoException) { - FilenameFilter filter = new FilenameFilter() { - public boolean accept(File dir, String name) { - return name.endsWith(".js") || - name.endsWith(".hac") || - name.endsWith(".hsp"); - } - }; - scriptStack = ((RhinoException) cause).getScriptStackTrace(filter); + scriptStack = ((RhinoException) cause).getScriptStackTrace(); } } @@ -106,4 +99,4 @@ public class ScriptingException extends Exception { } } -} \ No newline at end of file +} diff --git a/src/main/java/helma/scripting/rhino/CompiledOrInterpretedModuleScriptProvider.java b/src/main/java/helma/scripting/rhino/CompiledOrInterpretedModuleScriptProvider.java index cec1e359..9aa8b0fd 100644 --- a/src/main/java/helma/scripting/rhino/CompiledOrInterpretedModuleScriptProvider.java +++ b/src/main/java/helma/scripting/rhino/CompiledOrInterpretedModuleScriptProvider.java @@ -48,11 +48,11 @@ public class CompiledOrInterpretedModuleScriptProvider extends StrongCachingModu // unlikely, but possible exception during loading the module script without compilation Exception exception; // get the application's optimization level - int optimizationLevel = cx.getOptimizationLevel(); - + boolean interpretedMode = cx.isInterpretedMode(); + try { // set the optimization level to not compile, but interpret - cx.setOptimizationLevel(-1); + cx.setInterpretedMode(true); // load the module script with the newly set optimization level ModuleScript moduleScript = super.getModuleScript(cx, moduleId, moduleUri, baseUri, paths); // return the module script @@ -62,12 +62,12 @@ public class CompiledOrInterpretedModuleScriptProvider extends StrongCachingModu exception = e; } finally { // re-set the optimization - cx.setOptimizationLevel(optimizationLevel); + cx.setInterpretedMode(interpretedMode); } - - // re-throw the exception catched when trying to load the module script without compilation + + // re-throw the exception catched when trying to load the module script without compilation throw exception; } } -} \ No newline at end of file +} diff --git a/src/main/java/helma/scripting/rhino/HopObjectCtor.java b/src/main/java/helma/scripting/rhino/HopObjectCtor.java index 9bcbafb9..8f0bfcf5 100644 --- a/src/main/java/helma/scripting/rhino/HopObjectCtor.java +++ b/src/main/java/helma/scripting/rhino/HopObjectCtor.java @@ -39,7 +39,7 @@ public class HopObjectCtor extends FunctionObject { static Method hopObjCtor; - static long collectionId = 0; + static long collectionId = 0; static { try { @@ -51,7 +51,7 @@ public class HopObjectCtor extends FunctionObject { } static final int attr = DONTENUM | PERMANENT; - + /** * Create and install a HopObject constructor. * Part of this is copied from o.m.j.FunctionObject.addAsConstructor(). @@ -149,7 +149,7 @@ public class HopObjectCtor extends FunctionObject { private static final long serialVersionUID = -8041352998956882647L; public GetById(Scriptable scope) { - ScriptRuntime.setFunctionProtoAndParent(this, scope); + ScriptRuntime.setFunctionProtoAndParent(this, Context.getCurrentContext(), scope); } /** @@ -187,7 +187,7 @@ public class HopObjectCtor extends FunctionObject { } public int getArity() { - return 1; + return 1; } public int getLength() { @@ -201,7 +201,7 @@ public class HopObjectCtor extends FunctionObject { private static final long serialVersionUID = -4046933261468527204L; public HopCollection(Scriptable scope) { - ScriptRuntime.setFunctionProtoAndParent(this, scope); + ScriptRuntime.setFunctionProtoAndParent(this, Context.getCurrentContext(), scope); } public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { diff --git a/src/main/java/helma/scripting/rhino/JSAdapter.java b/src/main/java/helma/scripting/rhino/JSAdapter.java index 8ddcd9ec..62c02826 100644 --- a/src/main/java/helma/scripting/rhino/JSAdapter.java +++ b/src/main/java/helma/scripting/rhino/JSAdapter.java @@ -67,7 +67,7 @@ import org.mozilla.javascript.*; * @author A. Sundararajan * @since 1.6 */ -public final class JSAdapter implements Scriptable, Function { +public final class JSAdapter implements Function { private JSAdapter(Scriptable obj) { setAdaptee(obj); } diff --git a/src/main/java/helma/scripting/rhino/JSONModuleSource.java b/src/main/java/helma/scripting/rhino/JSONModuleSource.java index 89a3c480..dc5b623a 100644 --- a/src/main/java/helma/scripting/rhino/JSONModuleSource.java +++ b/src/main/java/helma/scripting/rhino/JSONModuleSource.java @@ -22,7 +22,7 @@ public class JSONModuleSource extends ModuleSource { content.append("module.exports = "); //$NON-NLS-1$ try { - content.append(IOUtils.toString(this.getUri().toURL().openStream())); + content.append(IOUtils.toString(this.getUri().toURL().openStream(), "UTF-8")); } catch (IOException e) { content.append("null"); //$NON-NLS-1$ } diff --git a/src/main/java/helma/scripting/rhino/JavaObject.java b/src/main/java/helma/scripting/rhino/JavaObject.java index b2f92076..2670858a 100644 --- a/src/main/java/helma/scripting/rhino/JavaObject.java +++ b/src/main/java/helma/scripting/rhino/JavaObject.java @@ -17,7 +17,6 @@ package helma.scripting.rhino; import helma.framework.core.*; -import helma.framework.ResponseTrans; import helma.framework.repository.Resource; import org.mozilla.javascript.*; import java.lang.reflect.Method; @@ -83,7 +82,7 @@ public class JavaObject extends NativeJavaObject { Skin skin = engine.toSkin(skinobj, protoName); if (skin != null) { - skin.render(engine.reval, javaObject, + skin.render(engine.reval, javaObject, (paramobj == Undefined.instance) ? null : paramobj); } @@ -148,7 +147,7 @@ public class JavaObject extends NativeJavaObject { return overload.containsKey(name) || super.has(name, start); } - /** + /** * Get a named property from this object. */ public Object get(String name, Scriptable start) { diff --git a/src/main/java/helma/scripting/rhino/NodeModulesProvider.java b/src/main/java/helma/scripting/rhino/NodeModulesProvider.java index 4ee114de..504e68a8 100644 --- a/src/main/java/helma/scripting/rhino/NodeModulesProvider.java +++ b/src/main/java/helma/scripting/rhino/NodeModulesProvider.java @@ -140,8 +140,8 @@ public class NodeModulesProvider extends UrlModuleSourceProvider { // check if the there is a "package.json" file in the directory if (packageFile.exists() && packageFile.isFile()) { // parse the JSON file - JsonObject json = new JsonParser() - .parse(new String(Files.readAllBytes(packageFile.toPath()))).getAsJsonObject(); + JsonObject json = JsonParser + .parseString(new String(Files.readAllBytes(packageFile.toPath()))).getAsJsonObject(); // check if the JSON file defines a main JS file if (json.has("main")) { //$NON-NLS-1$ // get the main JS file, removing the filename extension diff --git a/src/main/java/helma/scripting/rhino/RhinoCore.java b/src/main/java/helma/scripting/rhino/RhinoCore.java index a4ef63a2..bf90fac2 100644 --- a/src/main/java/helma/scripting/rhino/RhinoCore.java +++ b/src/main/java/helma/scripting/rhino/RhinoCore.java @@ -44,7 +44,6 @@ import org.mozilla.javascript.Undefined; import org.mozilla.javascript.WrapFactory; import org.mozilla.javascript.Wrapper; import org.mozilla.javascript.commonjs.module.RequireBuilder; -import org.mozilla.javascript.commonjs.module.provider.StrongCachingModuleScriptProvider; import org.mozilla.javascript.tools.debugger.ScopeProvider; import java.io.*; @@ -1233,7 +1232,7 @@ public final class RhinoCore implements ScopeProvider { protected void onContextCreated(Context cx) { cx.setWrapFactory(wrapper); - cx.setOptimizationLevel(optLevel); + cx.setInterpretedMode(optLevel < 0); cx.setInstructionObserverThreshold(10000); if (Context.isValidLanguageVersion(languageVersion)) { cx.setLanguageVersion(languageVersion); diff --git a/src/main/java/helma/scripting/rhino/SerializationProxy.java b/src/main/java/helma/scripting/rhino/SerializationProxy.java index 75d93d56..b558a32b 100644 --- a/src/main/java/helma/scripting/rhino/SerializationProxy.java +++ b/src/main/java/helma/scripting/rhino/SerializationProxy.java @@ -25,7 +25,7 @@ import java.io.Serializable; /** * Serialization proxy/placeholder interface. This is used for - * for various Helma and Rhino related classes.. + * for various Helma and Rhino related classes.. */ public interface SerializationProxy extends Serializable { public Object getObject(RhinoEngine engine); @@ -49,19 +49,21 @@ class ScriptBeanProxy implements SerializationProxy { * @return the object represented by this proxy */ public Object getObject(RhinoEngine engine) { + Object object = null; + try { - Object object = engine.global.get(name, engine.global); + object = engine.global.get(name, engine.global); } catch (Exception e) { System.out.println(name); } - - return engine.global.get(name, engine.global); + + return object; } } /** * Serialization proxy for the application object. - * + * * @author Daniel Ruthardt * @since 20170918 */ diff --git a/src/main/java/helma/scripting/rhino/debug/Profiler.java b/src/main/java/helma/scripting/rhino/debug/Profiler.java index cd6d5c96..37a8a7ab 100644 --- a/src/main/java/helma/scripting/rhino/debug/Profiler.java +++ b/src/main/java/helma/scripting/rhino/debug/Profiler.java @@ -151,7 +151,9 @@ public class Profiler implements Debugger { name.substring(prefixLength) }; formatter.format("%1$7d ms %2$5d ms %3$6d %4$s%n", args); - return formatter.toString(); + String result = formatter.toString(); + formatter.close(); + return result; } } } diff --git a/src/main/java/helma/scripting/rhino/debug/Tracer.java b/src/main/java/helma/scripting/rhino/debug/Tracer.java index 81a9d29a..18881307 100644 --- a/src/main/java/helma/scripting/rhino/debug/Tracer.java +++ b/src/main/java/helma/scripting/rhino/debug/Tracer.java @@ -13,11 +13,10 @@ * $Revision$ * $Date$ */ - + package helma.scripting.rhino.debug; import helma.framework.ResponseTrans; -import helma.util.HtmlEncoder; import org.mozilla.javascript.*; import org.mozilla.javascript.debug.*; import java.util.ArrayList; @@ -85,7 +84,7 @@ public class Tracer implements Debugger { * Called when execution is ready to start bytecode interpretation * for entered a particular function or script. */ - public void onEnter(Context cx, Scriptable activation, + public void onEnter(Context cx, Scriptable activation, Scriptable thisObj, Object[] args) { time = System.currentTimeMillis(); @@ -128,7 +127,7 @@ public class Tracer implements Debugger { // Simplify Trace by dropping fast invocations. May be useful when looking // looking for bottlenecks, but not when trying to find out wtf is going on // if (time <= 1) - // return; + // return; StringBuffer b = new StringBuffer("Trace: "); for (int i = 0; i < depth; i++) b.append(". "); diff --git a/src/main/java/helma/scripting/rhino/extensions/DatabaseObject.java b/src/main/java/helma/scripting/rhino/extensions/DatabaseObject.java index 0a5c180e..1dc9a3b8 100644 --- a/src/main/java/helma/scripting/rhino/extensions/DatabaseObject.java +++ b/src/main/java/helma/scripting/rhino/extensions/DatabaseObject.java @@ -25,6 +25,7 @@ import java.util.Enumeration; import java.util.Vector; import java.io.IOException; import java.io.Reader; +import java.lang.reflect.InvocationTargetException; import java.math.BigDecimal; import java.sql.*; @@ -62,9 +63,13 @@ public class DatabaseObject { * Create a new database object based on a driver name, with driver on the classpath * * @param driverName The class name of the JDBC driver + * @throws SecurityException + * @throws NoSuchMethodException + * @throws InvocationTargetException + * @throws IllegalArgumentException */ - DatabaseObject(String driverName) { + DatabaseObject(String driverName) throws IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { this.driverName = driverName; try { Class driverClass = Class.forName(driverName); @@ -73,7 +78,7 @@ public class DatabaseObject { // System.err.println("##Bad class " + driverClass); lastError = new RuntimeException("Class " + driverClass + " is not a JDBC driver"); } - driverClass.newInstance(); // may be needed by some drivers, harmless for others + driverClass.getDeclaredConstructor().newInstance(); // may be needed by some drivers, harmless for others } catch (ClassNotFoundException e) { // System.err.println("##Cannot find driver class: " + e); // e.printStackTrace(); @@ -448,51 +453,6 @@ public class DatabaseObject { return null; } - /* FIXME: dunno if this method is still used somewhere - public Object getProperty(String propertyName, int hash) { - //System.err.println(" &&& Getting property '" + propertyName + "'"); - - // Length property is firsy checked - - // First return system or or prototype properties - if (propertyName.equals("length")) { - return Integer.valueOf(colNames.size()); - } else { - if (resultSet == null) { - lastError = new SQLException("Attempt to access a released result set"); - return null; - } - if (!firstRowSeen) { - lastError = new SQLException("Attempt to access data before the first row is read"); - return null; - } - try { - int index = -1; // indicates not a valid index value - try { - char c = propertyName.charAt(0); - if ('0' <= c && c <= '9') { - index = Integer.parseInt(propertyName); - } - } catch (NumberFormatException e) { - } catch (StringIndexOutOfBoundsException e) { // for charAt - } - if (index>=0) { - return getProperty(index); - } - Object value = resultSet.getObject(propertyName); - // IServer.getLogger().log("&& @VALUE : " + value); - lastError = null; - return value; - } catch (SQLException e) { - // System.err.println("##Cannot get property '" + propertyName + "' " + e); - // e.printStackTrace(); - lastError = e; - } - } - return null; - } - */ - public Object getProperty(int index) { if (!firstRowSeen) { lastError = new SQLException("Attempt to access data before the first row is read"); diff --git a/src/main/java/helma/scripting/rhino/extensions/MailObject.java b/src/main/java/helma/scripting/rhino/extensions/MailObject.java index 2b2eaf07..724c455e 100644 --- a/src/main/java/helma/scripting/rhino/extensions/MailObject.java +++ b/src/main/java/helma/scripting/rhino/extensions/MailObject.java @@ -39,7 +39,7 @@ import javax.mail.internet.MimeUtility; * A JavaScript wrapper around a JavaMail message class to send * mail via SMTP from Helma */ -public class MailObject extends ScriptableObject implements Serializable { +public class MailObject extends ScriptableObject { private static final long serialVersionUID = -4834981850233741039L; diff --git a/src/main/java/helma/scripting/rhino/extensions/XmlObject.java b/src/main/java/helma/scripting/rhino/extensions/XmlObject.java index 3f7d4a0e..2ed32b46 100644 --- a/src/main/java/helma/scripting/rhino/extensions/XmlObject.java +++ b/src/main/java/helma/scripting/rhino/extensions/XmlObject.java @@ -126,7 +126,9 @@ public class XmlObject { writer.setDatabaseMode(dbmode); writer.write(node); writer.flush(); - return out.toString("UTF-8"); + String result = out.toString("UTF-8"); + writer.close(); + return result; } /** diff --git a/src/main/java/helma/servlet/AbstractServletClient.java b/src/main/java/helma/servlet/AbstractServletClient.java index 3e3b3f59..720ee620 100644 --- a/src/main/java/helma/servlet/AbstractServletClient.java +++ b/src/main/java/helma/servlet/AbstractServletClient.java @@ -217,7 +217,7 @@ public abstract class AbstractServletClient extends HttpServlet { parseParameters(request, reqtrans, encoding); // read file uploads - List uploads = null; + List uploads = null; ServletRequestContext reqcx = new ServletRequestContext(request); if (ServletFileUpload.isMultipartContent(reqcx)) { @@ -637,7 +637,7 @@ public abstract class AbstractServletClient extends HttpServlet { * Put name and value pair in map. When name already exist, add value * to array of values. */ - private static void putMapEntry(Map map, String name, String value) { + private static void putMapEntry(Map map, String name, String value) { String[] newValues = null; String[] oldValues = (String[]) map.get(name); @@ -653,9 +653,9 @@ public abstract class AbstractServletClient extends HttpServlet { map.put(name, newValues); } - protected List parseUploads(ServletRequestContext reqcx, RequestTrans reqtrans, - final UploadStatus uploadStatus, String encoding) - throws FileUploadException, UnsupportedEncodingException { + protected List parseUploads(ServletRequestContext reqcx, RequestTrans reqtrans, + final UploadStatus uploadStatus, String encoding) + throws FileUploadException, UnsupportedEncodingException { // handle file upload DiskFileItemFactory factory = new DiskFileItemFactory(); FileUpload upload = new FileUpload(factory); @@ -672,8 +672,8 @@ public abstract class AbstractServletClient extends HttpServlet { }); } - List uploads = upload.parseRequest(reqcx); - Iterator it = uploads.iterator(); + List uploads = upload.parseRequest(reqcx); + Iterator it = uploads.iterator(); while (it.hasNext()) { FileItem item = (FileItem) it.next(); @@ -705,7 +705,7 @@ public abstract class AbstractServletClient extends HttpServlet { return; } - HashMap parameters = new HashMap(); + HashMap parameters = new HashMap<>(); // Parse any query string parameters from the request if (queryString != null) { @@ -764,7 +764,7 @@ public abstract class AbstractServletClient extends HttpServlet { * * @exception UnsupportedEncodingException if the data is malformed */ - public static void parseParameters(Map map, byte[] data, String encoding, boolean isPost) + public static void parseParameters(Map map, byte[] data, String encoding, boolean isPost) throws UnsupportedEncodingException { if ((data != null) && (data.length > 0)) { int ix = 0; diff --git a/src/main/java/helma/servlet/EmbeddedServletClient.java b/src/main/java/helma/servlet/EmbeddedServletClient.java index 80b3cd6e..5723cb65 100644 --- a/src/main/java/helma/servlet/EmbeddedServletClient.java +++ b/src/main/java/helma/servlet/EmbeddedServletClient.java @@ -16,7 +16,6 @@ package helma.servlet; -import helma.framework.*; import helma.framework.core.Application; import helma.main.*; import javax.servlet.*; diff --git a/src/main/java/helma/util/CryptResource.java b/src/main/java/helma/util/CryptResource.java index 7a7d5ab4..c882a85e 100644 --- a/src/main/java/helma/util/CryptResource.java +++ b/src/main/java/helma/util/CryptResource.java @@ -23,7 +23,6 @@ import java.util.StringTokenizer; import org.apache.commons.codec.digest.DigestUtils; -import helma.framework.repository.Resource; import helma.framework.repository.Resource; /** From 10b0e0e2b72003d99150a7d4950fa1895eacba82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20Sch=C3=A4fer?= Date: Wed, 9 Apr 2025 23:09:23 +0200 Subject: [PATCH 41/42] Remove Eclipse-specific markers (comments) --- .../main/java/helma/main/launcher/Main.java | 8 +- .../java/helma/framework/RequestTrans.java | 6 +- .../java/helma/framework/ResponseTrans.java | 2 +- .../java/helma/main/ApplicationManager.java | 98 +++++++++---------- .../scripting/rhino/JSONModuleSource.java | 12 +-- .../scripting/rhino/NodeModulesProvider.java | 14 +-- .../helma/servlet/AbstractServletClient.java | 6 +- 7 files changed, 73 insertions(+), 73 deletions(-) diff --git a/launcher/src/main/java/helma/main/launcher/Main.java b/launcher/src/main/java/helma/main/launcher/Main.java index c0ee09ab..13e36c02 100644 --- a/launcher/src/main/java/helma/main/launcher/Main.java +++ b/launcher/src/main/java/helma/main/launcher/Main.java @@ -115,13 +115,13 @@ public class Main { File[] files = dir.listFiles(new FilenameFilter() { public boolean accept(File dir, String name) { String n = name.toLowerCase(); - return n.endsWith(".jar") || n.endsWith(".zip"); //$NON-NLS-1$//$NON-NLS-2$ + return n.endsWith(".jar") || n.endsWith(".zip"); } }); if (files != null) { for (int i = 0; i < files.length; i++) { - jarlist.add(new URL("file:" + files[i].getAbsolutePath())); //$NON-NLS-1$ + jarlist.add(new URL("file:" + files[i].getAbsolutePath())); } } } @@ -149,7 +149,7 @@ public class Main { addJars(jarlist, libdir); // add all jar files from the lib/ext directory - addJars(jarlist, new File(libdir, "ext")); //$NON-NLS-1$ + addJars(jarlist, new File(libdir, "ext")); URL[] urls = new URL[jarlist.size()]; @@ -199,7 +199,7 @@ public class Main { // try to get Helma installation directory if (installDir == null) { URL launcherUrl = ClassLoader.getSystemClassLoader() - .getResource("helma/main/launcher/Main.class"); //$NON-NLS-1$ + .getResource("helma/main/launcher/Main.class"); // this is a JAR URL of the form // jar:!/{entry} diff --git a/src/main/java/helma/framework/RequestTrans.java b/src/main/java/helma/framework/RequestTrans.java index 83665ba8..9a46e0bd 100644 --- a/src/main/java/helma/framework/RequestTrans.java +++ b/src/main/java/helma/framework/RequestTrans.java @@ -603,11 +603,11 @@ public class RequestTrans implements Serializable { StringTokenizer tok; - if (auth.startsWith("Basic ")) { //$NON-NLS-1$ + if (auth.startsWith("Basic ")) { tok = new StringTokenizer(new String(Base64.decodeBase64(auth.substring(6))), - ":"); //$NON-NLS-1$ + ":"); } else { - tok = new StringTokenizer(new String(Base64.decodeBase64(auth)), ":"); //$NON-NLS-1$ + tok = new StringTokenizer(new String(Base64.decodeBase64(auth)), ":"); } try { diff --git a/src/main/java/helma/framework/ResponseTrans.java b/src/main/java/helma/framework/ResponseTrans.java index af65a4c1..0853b7ee 100644 --- a/src/main/java/helma/framework/ResponseTrans.java +++ b/src/main/java/helma/framework/ResponseTrans.java @@ -714,7 +714,7 @@ public final class ResponseTrans extends Writer implements Serializable { // if (contentType != null) // digest.update (contentType.getBytes()); byte[] b = this.digest.digest(this.response); - this.etag = "\"" + new String(Base64.encodeBase64(b)) + "\""; //$NON-NLS-1$ //$NON-NLS-2$ + this.etag = "\"" + new String(Base64.encodeBase64(b)) + "\""; // only set response to 304 not modified if no cookies were set if (reqtrans.hasETag(etag) && countCookies() == 0) { response = new byte[0]; diff --git a/src/main/java/helma/main/ApplicationManager.java b/src/main/java/helma/main/ApplicationManager.java index 5ca5927f..7a8977bf 100644 --- a/src/main/java/helma/main/ApplicationManager.java +++ b/src/main/java/helma/main/ApplicationManager.java @@ -71,7 +71,7 @@ public class ApplicationManager implements XmlRpcHandler { for (Enumeration e = this.props.keys(); e.hasMoreElements();) { String appName = (String) e.nextElement(); - if ((appName.indexOf(".") == -1) && //$NON-NLS-1$ + if ((appName.indexOf(".") == -1) && (this.applications.get(appName) == null)) { AppDescriptor appDesc = new AppDescriptor(appName); appDesc.start(); @@ -149,7 +149,7 @@ public class ApplicationManager implements XmlRpcHandler { for (Enumeration e = this.props.keys(); e.hasMoreElements();) { String appName = (String) e.nextElement(); - if (appName.indexOf(".") == -1) { //$NON-NLS-1$ + if (appName.indexOf(".") == -1) { String appValue = this.props.getProperty(appName); if (appValue != null && appValue.length() > 0) { @@ -208,7 +208,7 @@ public class ApplicationManager implements XmlRpcHandler { */ public Object execute(String method, @SuppressWarnings("rawtypes") Vector params) throws Exception { - int dot = method.indexOf("."); //$NON-NLS-1$ + int dot = method.indexOf("."); if (dot == -1) { throw new Exception("Method name \"" + method + @@ -224,7 +224,7 @@ public class ApplicationManager implements XmlRpcHandler { Application app = (Application) this.xmlrpcHandlers.get(handler); if (app == null) { - app = (Application) this.xmlrpcHandlers.get("*"); //$NON-NLS-1$ + app = (Application) this.xmlrpcHandlers.get("*"); // use the original method name, the handler is resolved within the app. method2 = method; } @@ -239,32 +239,32 @@ public class ApplicationManager implements XmlRpcHandler { private String getMountpoint(String mountpoint) { mountpoint = mountpoint.trim(); - if ("".equals(mountpoint)) { //$NON-NLS-1$ - return "/"; //$NON-NLS-1$ - } else if (!mountpoint.startsWith("/")) { //$NON-NLS-1$ - return "/" + mountpoint; //$NON-NLS-1$ + if ("".equals(mountpoint)) { + return "/"; + } else if (!mountpoint.startsWith("/")) { + return "/" + mountpoint; } return mountpoint; } private String joinMountpoint(String prefix, String suffix) { - if (prefix.endsWith("/") || suffix.startsWith("/")) { //$NON-NLS-1$//$NON-NLS-2$ + if (prefix.endsWith("/") || suffix.startsWith("/")) { return prefix+suffix; } - return prefix+"/"+suffix; //$NON-NLS-1$ + return prefix+"/"+suffix; } private String getPathPattern(String mountpoint) { - if (!mountpoint.startsWith("/")) { //$NON-NLS-1$ - mountpoint = "/"+mountpoint; //$NON-NLS-1$ + if (!mountpoint.startsWith("/")) { + mountpoint = "/"+mountpoint; } - if ("/".equals(mountpoint)) { //$NON-NLS-1$ - return "/"; //$NON-NLS-1$ + if ("/".equals(mountpoint)) { + return "/"; } - if (mountpoint.endsWith("/")) { //$NON-NLS-1$ + if (mountpoint.endsWith("/")) { return mountpoint.substring(0, mountpoint.length()-1); } @@ -335,56 +335,56 @@ public class ApplicationManager implements XmlRpcHandler { AppDescriptor(String name) { ResourceProperties conf = ApplicationManager.this.props.getSubProperties(name + '.'); this.appName = name; - this.mountpoint = getMountpoint(conf.getProperty("mountpoint", this.appName)); //$NON-NLS-1$ + this.mountpoint = getMountpoint(conf.getProperty("mountpoint", this.appName)); this.pathPattern = getPathPattern(this.mountpoint); - this.staticDir = conf.getProperty("static"); //$NON-NLS-1$ - this.staticMountpoint = getPathPattern(conf.getProperty("staticMountpoint", //$NON-NLS-1$ - joinMountpoint(this.mountpoint, "static"))); //$NON-NLS-1$ - this.staticIndex = "true".equalsIgnoreCase(conf.getProperty("staticIndex")); //$NON-NLS-1$//$NON-NLS-2$ - String home = conf.getProperty("staticHome"); //$NON-NLS-1$ + this.staticDir = conf.getProperty("static"); + this.staticMountpoint = getPathPattern(conf.getProperty("staticMountpoint", + joinMountpoint(this.mountpoint, "static"))); + this.staticIndex = "true".equalsIgnoreCase(conf.getProperty("staticIndex")); + String home = conf.getProperty("staticHome"); if (home == null) { - this.staticHome = new String[] {"index.html", "index.htm"}; //$NON-NLS-1$ //$NON-NLS-2$ + this.staticHome = new String[] {"index.html", "index.htm"}; } else { - this.staticHome = StringUtils.split(home, ","); //$NON-NLS-1$ + this.staticHome = StringUtils.split(home, ","); } - this.protectedStaticDir = conf.getProperty("protectedStatic"); //$NON-NLS-1$ + this.protectedStaticDir = conf.getProperty("protectedStatic"); - this.cookieDomain = conf.getProperty("cookieDomain"); //$NON-NLS-1$ - this.sessionCookieName = conf.getProperty("sessionCookieName"); //$NON-NLS-1$ - this.protectedSessionCookie = conf.getProperty("protectedSessionCookie"); //$NON-NLS-1$ - this.uploadLimit = conf.getProperty("uploadLimit"); //$NON-NLS-1$ - this.uploadSoftfail = conf.getProperty("uploadSoftfail"); //$NON-NLS-1$ - this.debug = conf.getProperty("debug"); //$NON-NLS-1$ - String appDirName = conf.getProperty("appdir"); //$NON-NLS-1$ + this.cookieDomain = conf.getProperty("cookieDomain"); + this.sessionCookieName = conf.getProperty("sessionCookieName"); + this.protectedSessionCookie = conf.getProperty("protectedSessionCookie"); + this.uploadLimit = conf.getProperty("uploadLimit"); + this.uploadSoftfail = conf.getProperty("uploadSoftfail"); + this.debug = conf.getProperty("debug"); + String appDirName = conf.getProperty("appdir"); this.appDir = (appDirName == null) ? null : getAbsoluteFile(appDirName); - String dbDirName = conf.getProperty("dbdir"); //$NON-NLS-1$ + String dbDirName = conf.getProperty("dbdir"); this.dbDir = (dbDirName == null) ? null : getAbsoluteFile(dbDirName); - this.servletClassName = conf.getProperty("servletClass"); //$NON-NLS-1$ + this.servletClassName = conf.getProperty("servletClass"); // got ignore dirs - this.ignoreDirs = conf.getProperty("ignore"); //$NON-NLS-1$ + this.ignoreDirs = conf.getProperty("ignore"); // read and configure app repositories ArrayList repositoryList = new ArrayList<>(); Class[] parameters = { String.class }; for (int i = 0; true; i++) { - String repositoryArgs = conf.getProperty("repository." + i); //$NON-NLS-1$ + String repositoryArgs = conf.getProperty("repository." + i); if (repositoryArgs != null) { // lookup repository implementation - String repositoryImpl = conf.getProperty("repository." + i + //$NON-NLS-1$ - ".implementation"); //$NON-NLS-1$ + String repositoryImpl = conf.getProperty("repository." + i + + ".implementation"); if (repositoryImpl == null) { // implementation not set manually, have to guess it - if (repositoryArgs.endsWith(".zip")) { //$NON-NLS-1$ + if (repositoryArgs.endsWith(".zip")) { repositoryArgs = findResource(repositoryArgs); - repositoryImpl = "helma.framework.repository.ZipRepository"; //$NON-NLS-1$ - } else if (repositoryArgs.endsWith(".js")) { //$NON-NLS-1$ + repositoryImpl = "helma.framework.repository.ZipRepository"; + } else if (repositoryArgs.endsWith(".js")) { repositoryArgs = findResource(repositoryArgs); - repositoryImpl = "helma.framework.repository.SingleFileRepository"; //$NON-NLS-1$ + repositoryImpl = "helma.framework.repository.SingleFileRepository"; } else { repositoryArgs = findResource(repositoryArgs); - repositoryImpl = "helma.framework.repository.FileRepository"; //$NON-NLS-1$ + repositoryImpl = "helma.framework.repository.FileRepository"; } } @@ -506,27 +506,27 @@ public class ApplicationManager implements XmlRpcHandler { appContext.addServlet(holder, "/*"); if (this.cookieDomain != null) { - holder.setInitParameter("cookieDomain", this.cookieDomain); //$NON-NLS-1$ + holder.setInitParameter("cookieDomain", this.cookieDomain); } if (this.sessionCookieName != null) { - holder.setInitParameter("sessionCookieName", this.sessionCookieName); //$NON-NLS-1$ + holder.setInitParameter("sessionCookieName", this.sessionCookieName); } if (this.protectedSessionCookie != null) { - holder.setInitParameter("protectedSessionCookie", this.protectedSessionCookie); //$NON-NLS-1$ + holder.setInitParameter("protectedSessionCookie", this.protectedSessionCookie); } if (this.uploadLimit != null) { - holder.setInitParameter("uploadLimit", this.uploadLimit); //$NON-NLS-1$ + holder.setInitParameter("uploadLimit", this.uploadLimit); } if (this.uploadSoftfail != null) { - holder.setInitParameter("uploadSoftfail", this.uploadSoftfail); //$NON-NLS-1$ + holder.setInitParameter("uploadSoftfail", this.uploadSoftfail); } if (this.debug != null) { - holder.setInitParameter("debug", this.debug); //$NON-NLS-1$ + holder.setInitParameter("debug", this.debug); } if (protectedStaticDir != null) { @@ -584,7 +584,7 @@ public class ApplicationManager implements XmlRpcHandler { @Override public String toString() { - return "[AppDescriptor "+this.app+"]"; //$NON-NLS-1$ //$NON-NLS-2$ + return "[AppDescriptor "+this.app+"]"; } } } diff --git a/src/main/java/helma/scripting/rhino/JSONModuleSource.java b/src/main/java/helma/scripting/rhino/JSONModuleSource.java index dc5b623a..43ca208b 100644 --- a/src/main/java/helma/scripting/rhino/JSONModuleSource.java +++ b/src/main/java/helma/scripting/rhino/JSONModuleSource.java @@ -19,16 +19,16 @@ public class JSONModuleSource extends ModuleSource { @Override public Reader getReader() { StringBuffer content = new StringBuffer(); - content.append("module.exports = "); //$NON-NLS-1$ - + content.append("module.exports = "); + try { content.append(IOUtils.toString(this.getUri().toURL().openStream(), "UTF-8")); } catch (IOException e) { - content.append("null"); //$NON-NLS-1$ + content.append("null"); } - - content.append(";"); //$NON-NLS-1$ - + + content.append(";"); + return new StringReader(content.toString()); } diff --git a/src/main/java/helma/scripting/rhino/NodeModulesProvider.java b/src/main/java/helma/scripting/rhino/NodeModulesProvider.java index 504e68a8..b23b824f 100644 --- a/src/main/java/helma/scripting/rhino/NodeModulesProvider.java +++ b/src/main/java/helma/scripting/rhino/NodeModulesProvider.java @@ -73,7 +73,7 @@ public class NodeModulesProvider extends UrlModuleSourceProvider { // check if the file exists and is a file if (file.exists() && file.isFile()) { // check if the file is a JSON file - if (file.getAbsolutePath().toLowerCase().endsWith(".json")) { //$NON-NLS-1$ + if (file.getAbsolutePath().toLowerCase().endsWith(".json")) { // return a JSON module source return new JSONModuleSource(null, file.toURI(), base, validator); } else { @@ -83,7 +83,7 @@ public class NodeModulesProvider extends UrlModuleSourceProvider { } // lets assume the module is a JS file - file = new File(new File(uri).getPath() + ".js"); //$NON-NLS-1$ + file = new File(new File(uri).getPath() + ".js"); // check if a file.js exists and is a file if (file.exists() && file.isFile()) { // do what would have been done anyways @@ -91,7 +91,7 @@ public class NodeModulesProvider extends UrlModuleSourceProvider { } // lets assume the module is a JSON file - file = new File(new File(uri).getPath() + ".json"); //$NON-NLS-1$ + file = new File(new File(uri).getPath() + ".json"); // check if a file.json exists and is a file if (file.exists() && file.isFile()) { // return a JSON module source @@ -135,7 +135,7 @@ public class NodeModulesProvider extends UrlModuleSourceProvider { ModuleSource moduleSource; // lets assume that there is a "package.json" file in the directory - File packageFile = new File(directory, "package.json"); //$NON-NLS-1$ + File packageFile = new File(directory, "package.json"); // check if the there is a "package.json" file in the directory if (packageFile.exists() && packageFile.isFile()) { @@ -143,9 +143,9 @@ public class NodeModulesProvider extends UrlModuleSourceProvider { JsonObject json = JsonParser .parseString(new String(Files.readAllBytes(packageFile.toPath()))).getAsJsonObject(); // check if the JSON file defines a main JS file - if (json.has("main")) { //$NON-NLS-1$ + if (json.has("main")) { // get the main JS file, removing the filename extension - String main = FilenameUtils.removeExtension(json.get("main").getAsString()); //$NON-NLS-1$ + String main = FilenameUtils.removeExtension(json.get("main").getAsString()); // load as file moduleSource = this.loadAsFile(new File(directory, main).toURI(), base, validator); @@ -158,7 +158,7 @@ public class NodeModulesProvider extends UrlModuleSourceProvider { } // load as index - moduleSource = this.loadAsFile(new File(directory, "index").toURI(), base, validator); //$NON-NLS-1$ + moduleSource = this.loadAsFile(new File(directory, "index").toURI(), base, validator); // check if something was loaded if (moduleSource != null) { // return the loaded module source diff --git a/src/main/java/helma/servlet/AbstractServletClient.java b/src/main/java/helma/servlet/AbstractServletClient.java index 720ee620..15483425 100644 --- a/src/main/java/helma/servlet/AbstractServletClient.java +++ b/src/main/java/helma/servlet/AbstractServletClient.java @@ -517,9 +517,9 @@ public abstract class AbstractServletClient extends HttpServlet { checksum[i] = (byte) (n); n >>>= 8; } - String etag = "\"" + new String(Base64.encodeBase64(checksum)) + "\""; //$NON-NLS-1$//$NON-NLS-2$ - res.setHeader("ETag", etag); //$NON-NLS-1$ - String etagHeader = req.getHeader("If-None-Match"); //$NON-NLS-1$ + String etag = "\"" + new String(Base64.encodeBase64(checksum)) + "\""; + res.setHeader("ETag", etag); + String etagHeader = req.getHeader("If-None-Match"); if (etagHeader != null) { StringTokenizer st = new StringTokenizer(etagHeader, ", \r\n"); while (st.hasMoreTokens()) { From df28d406457290d9f02310e0a5177fe7cff9e77c Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Thu, 22 May 2025 14:14:07 +0000 Subject: [PATCH 42/42] Update dependency gradle to v8.14.1 --- gradle/wrapper/gradle-wrapper.jar | Bin 43583 -> 43764 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 6 +++--- gradlew.bat | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index a4b76b9530d66f5e68d973ea569d8e19de379189..1b33c55baabb587c669f562ae36f953de2481846 100644 GIT binary patch delta 34943 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

    Gn4F+B~IivciU9^t0j-Mgao3 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 DDw4oP diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e18bc253..002b867c 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.12.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index f3b75f3b..23d15a93 100755 --- a/gradlew +++ b/gradlew @@ -114,7 +114,7 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar +CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. @@ -205,7 +205,7 @@ fi DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# * DEFAULT_JVM_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 +213,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/gradlew.bat b/gradlew.bat index 9b42019c..5eed7ee8 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -70,11 +70,11 @@ goto fail :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar +set CLASSPATH= @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* +"%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" %* :end @rem End local scope for the variables with windows NT shell