Compare commits

..

46 commits

Author SHA1 Message Date
745ce5bfca
Merge branch 'main' into renovate/major-jetty-packages 2025-04-11 13:22:10 +02:00
0cf9cf7af2
Merge remote-tracking branch 'origin/main' into renovate/major-jetty-packages
All checks were successful
Build / build (push) Successful in 10s
2025-04-10 23:32:50 +02:00
6723df912e
Resolve FIXME but without fixing the issue
All checks were successful
Build / build (push) Successful in 19s
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.
2025-04-07 17:16:59 +02:00
436862e87a
Remove setting of “empty” base resource
Fingers crossed it won’t be missed
2025-04-07 17:16:58 +02:00
36a12effb2
Bump Jetty versions to 12.0.19 2025-04-07 17:16:57 +02:00
2994a4becc
Disable Jetty’s session cookies
This remediates the exception “Shared scheduler not started” and restores the functionality of enabling an app in apps.properties – see #103 (comment)
2025-04-07 17:16:53 +02:00
99e8b204fd
Bump Java version 2025-04-07 17:05:43 +02:00
c42c0a7a17
Update repo URL 2025-04-07 17:05:43 +02:00
b7543cf615
Bump year of copyright notice 2025-04-07 17:05:42 +02:00
fc084f6e52
Escape HTML elements in commit messages 2025-04-07 17:05:41 +02:00
6fc73d2320
Add release notes generated with git-cliff 2025-04-07 17:05:40 +02:00
9b5cc988dd
Run the build workflow when itself has changed 2025-04-07 17:05:40 +02:00
04b210b464
Leave aside compiling for different Java versions for now 2025-04-07 17:05:39 +02:00
4c011f1e1b
Gradle is installed on the runner 2025-04-07 17:05:38 +02:00
bc7894ecc1
Use fully qualified URL for setup-java action 2025-04-07 17:05:38 +02:00
a3fbf72f38
Initial commit 2025-04-07 17:05:37 +02:00
de2150693f
Add deploy script usable with rsync and a restricted SSH key 2025-04-07 17:05:36 +02:00
cd8baa4ac1
Merge branch 'main' into renovate/major-jetty-packages 2025-02-15 20:43:32 +01:00
11b226b272
Merge branch 'main' into origin/renovate/major-jetty-packages
# Conflicts:
#	build.gradle
2025-01-10 20:09:53 +01:00
1925916220
Bump required minimum Java version to 17 2024-06-15 18:27:30 +02:00
b1296fb093
Merge branch 'helma-🐜' into renovate/major-jetty-packages
# Conflicts:
#	.github/workflows/release.yml
2024-06-15 18:18:25 +02:00
51bc14c3a4
Remove over-complicated launch and tasks configuration in favor of Gradle plugin
Starting run/debug tasks with the plugin works out of the box;
one just has to avoid the default “Run and Debug” button.
2024-06-15 18:01:30 +02:00
3e5af064b0
Add setup for Gradle debugging in VS Codium 2024-06-15 18:00:59 +02:00
6401300189
Decouple update task from install
Now that Gradle runs Helma with the configured dependencies, updating the installation directory has become less crucial
2024-06-15 13:02:11 +02:00
45b19e3217
Reorder the tasks 2024-06-15 12:38:04 +02:00
ffb259a01c
Slightly modify the version string (still a date representation) 2024-06-15 12:37:16 +02:00
84abcde037
Update run configuration to always use the correct dependencies 2024-06-15 12:34:51 +02:00
9e870e6cd3
Slightly modify the format of the build date 2024-06-15 12:34:50 +02:00
1e32c8eb89
Merge remote-tracking branch 'origin/helma-🐜' into renovate/major-jetty-packages 2024-05-30 19:22:11 +02:00
9143faf35e
Add release action 2024-05-30 19:10:13 +02:00
79d83389f2
Use canonical paths when creating static contexts 2024-05-25 18:28:43 +02:00
90e45c9115
Migrate to Apache file upload API 2 w/ Jakarta 2024-05-25 18:28:43 +02:00
3dbcd792a6
Migrate to Jakarte servlet API 5 2024-05-25 18:28:42 +02:00
973b3493cb
Prevent java.lang.IllegalArgumentException: Resource String is invalid 2024-05-25 18:28:42 +02:00
441d952b35
Prevent incompatible types: ServletContextHandler cannot be converted to Handler 2024-05-25 18:28:42 +02:00
d67d0235bd
Prevent java.lang.IllegalStateException: Shared scheduler not started 2024-05-25 18:28:41 +02:00
fa59a27858
Fix creation of protected context 2024-05-25 18:28:41 +02:00
304ea4e456
Fix creation of app context 2024-05-25 18:28:40 +02:00
0165e7c80e
Fix setup of static resource 2024-05-25 18:28:40 +02:00
62630ae068
Modernize for loop 2024-05-25 18:28:39 +02:00
26975a109a
Method ServerConnector.setSoLingerTime() was removed 2024-05-25 18:28:39 +02:00
a943124d45
Fix creating Jetty server from config file 2024-05-25 18:28:38 +02:00
c087cb731e
Use correct version of Jetty servlet 2024-05-25 18:28:38 +02:00
c5f68013b1 Merge branch 'helma-🐜' into renovate/major-jetty-packages 2024-05-19 02:56:40 +02:00
ed575bc4c5
Update README.md
Minor edits
2024-05-19 02:05:49 +02:00
renovate[bot]
1cf738767c
Update Jetty packages 2024-05-18 15:30:36 +00:00
16 changed files with 99 additions and 76 deletions

22
.gitignore vendored
View file

@ -1,7 +1,5 @@
# Generally ignore hidden files
.*
# Manage some Codium configuration
.vscode/*
!.vscode
!.vscode/extensions.json
@ -9,22 +7,22 @@
!.vscode/settings.json
!.vscode/tasks.json
# Ignore files created during build or run
build
/apps
/bin
/backups
build
/db
/docs
/extras
/lib
/licenses
/log
# Ignore files managed in src/dist
/*.properties
/apps
/db
/extras
/launcher.jar
/static
# Manage Gradle configuration
/*.properties
!/gradle.properties
/launcher.jar
/passwd
/start.*

View file

@ -1 +1 @@
11.0
17

View file

@ -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 <http://localhost:8080>

View file

@ -18,8 +18,8 @@ allprojects {
apply plugin: 'java'
java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
repositories {
@ -51,14 +51,15 @@ configurations {
dependencies {
implementation 'com.google.code.gson:gson:2.12.1'
implementation 'commons-codec:commons-codec:1.18.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.5'
implementation 'commons-net:commons-net:3.11.1'
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:jetty-servlet:9.4.57.v20241219'
implementation 'org.eclipse.jetty:jetty-xml:9.4.57.v20241219'
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'

View file

@ -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

View file

@ -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);
}
/**

View file

@ -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;

View file

@ -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;

View file

@ -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.*;

View file

@ -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.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
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,23 +483,29 @@ public class ApplicationManager implements XmlRpcHandler {
// if there is a static direcory specified, mount it
if (this.staticDir != null) {
String staticPath = getAbsoluteFile(this.staticDir).getCanonicalPath();
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.setHandler(rhandler);
ApplicationManager.this.context.addHandler(staticContext);
staticContext.start();
}
appContext = new ServletContextHandler(context, pathPattern, true, true);
// I hope I am correct assuming Helma does not need Jettys 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);
Class servletClass = servletClassName == null ?
EmbeddedServletClient.class : Class.forName(servletClassName);
ServletHolder holder = new ServletHolder(servletClass);
@ -529,10 +537,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).getCanonicalPath();
appContext.setBaseResourceAsString(protectedContent);
getLogger().info("Serving protected static from " + protectedContent);
}
// Remap the context paths and start
@ -556,7 +563,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);
// 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();
this.appContext = null;

View file

@ -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
@ -73,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);
@ -100,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();
}
}
}
}

View file

@ -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.");

View file

@ -22,18 +22,29 @@ package helma.servlet;
import helma.framework.*;
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 javax.servlet.*;
import javax.servlet.http.*;
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
@ -218,9 +229,9 @@ 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)) {
if (JakartaServletFileUpload.isMultipartContent(reqcx)) {
// get session for upload progress monitoring
UploadStatus uploadStatus = getApplication().getUploadStatus(reqtrans);
try {
@ -228,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 {
@ -653,12 +664,12 @@ 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 {
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);
@ -681,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);
}

View file

@ -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

View file

@ -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);

View file

@ -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;
}
}