This is an automated email from the ASF dual-hosted git repository.

hansva pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git


The following commit(s) were added to refs/heads/main by this push:
     new 00729a9593 [SONAR] cleanup some security warnings and hardening (#6837)
00729a9593 is described below

commit 00729a95933c7681db630eb698b428b31c03cc2c
Author: Hans Van Akelyen <[email protected]>
AuthorDate: Thu Mar 19 20:32:09 2026 +0100

    [SONAR] cleanup some security warnings and hardening (#6837)
    
    Add deprecation to some endpoints
    
    cleanup and jetty fix
---
 docker/integration-tests/unit-tests.Dockerfile     |   1 -
 .../modules/ROOT/pages/hop-server/rest-api.adoc    |  34 ++++-
 engine/pom.xml                                     |   8 +-
 .../java/org/apache/hop/www/AddExportServlet.java  |  12 +-
 .../org/apache/hop/www/AddPipelineServlet.java     |  18 ++-
 .../org/apache/hop/www/AddWorkflowServlet.java     |  20 ++-
 .../org/apache/hop/www/BaseHopServerPlugin.java    |  18 +--
 .../java/org/apache/hop/www/BaseHttpServlet.java   | 147 ++++++++++++---------
 .../java/org/apache/hop/www/BodyHttpServlet.java   |  14 +-
 .../apache/hop/www/DeleteExecutionInfoServlet.java |  18 ++-
 .../org/apache/hop/www/ExecPipelineServlet.java    |  55 +++++---
 .../org/apache/hop/www/ExecWorkflowServlet.java    |  55 +++++---
 .../apache/hop/www/GetExecutionInfoServlet.java    |   5 +-
 .../apache/hop/www/GetPipelineStatusServlet.java   |  22 ++-
 .../java/org/apache/hop/www/GetRootServlet.java    |   5 +-
 .../java/org/apache/hop/www/GetStatusServlet.java  |  16 +--
 .../apache/hop/www/GetWorkflowStatusServlet.java   |  21 ++-
 .../java/org/apache/hop/www/HopServerServlet.java  |  21 ++-
 .../org/apache/hop/www/PausePipelineServlet.java   |  22 ++-
 .../hop/www/PrepareExecutionPipelineServlet.java   |  68 ++++++----
 .../hop/www/RegisterExecutionInfoServlet.java      |  10 +-
 .../org/apache/hop/www/RemovePipelineServlet.java  |  31 ++---
 .../org/apache/hop/www/RemoveWorkflowServlet.java  |  29 ++--
 .../org/apache/hop/www/SniffTransformServlet.java  |  86 ++++++++----
 .../hop/www/StartExecutionPipelineServlet.java     |  42 ++++--
 .../org/apache/hop/www/StartPipelineServlet.java   |  33 +++--
 .../org/apache/hop/www/StartWorkflowServlet.java   |  34 +++--
 .../org/apache/hop/www/StopPipelineServlet.java    |  17 ++-
 .../org/apache/hop/www/StopWorkflowServlet.java    |  19 ++-
 .../main/java/org/apache/hop/www/WebResult.java    |  10 ++
 .../main/java/org/apache/hop/www/WebServer.java    |   2 +-
 .../java/org/apache/hop/www/WebServiceServlet.java |  10 +-
 lib/pom.xml                                        |  36 ++++-
 plugins/engines/beam/src/assembly/assembly.xml     |   1 +
 .../apache/hop/metadata/rest/RestConnection.java   |   4 +-
 plugins/tech/google/pom.xml                        |   2 -
 pom.xml                                            |  34 +++++
 37 files changed, 663 insertions(+), 317 deletions(-)

diff --git a/docker/integration-tests/unit-tests.Dockerfile 
b/docker/integration-tests/unit-tests.Dockerfile
index dad75bdb1e..c33853b125 100644
--- a/docker/integration-tests/unit-tests.Dockerfile
+++ b/docker/integration-tests/unit-tests.Dockerfile
@@ -90,7 +90,6 @@ RUN chown -R ${JENKINS_USER}:${JENKINS_GROUP} 
${DEPLOYMENT_PATH}/hop \
 # Download Additional drivers/dependencies
 ADD --chown=${JENKINS_USER}:${JENKINS_GROUP} 
https://repo1.maven.org/maven2/com/vertica/jdbc/vertica-jdbc/23.4.0-0/vertica-jdbc-23.4.0-0.jar
 /opt/hop/lib/jdbc/vertica-jdbc-23.4.0-0.jar
 ADD --chown=${JENKINS_USER}:${JENKINS_GROUP} 
https://repo1.maven.org/maven2/com/mysql/mysql-connector-j/9.2.0/mysql-connector-j-9.2.0.jar
 /opt/hop/lib/jdbc/mysql-connector-j-9.2.0.jar
-ADD --chown=${JENKINS_USER}:${JENKINS_GROUP} 
https://repo1.maven.org/maven2/org/openjdk/nashorn/nashorn-core/15.4/nashorn-core-15.4.jar
 /opt/hop/plugins/transforms/script/lib/nashorn-core-15.4.jar
 
 # make volume available so that hop pipeline and workflow files can be 
provided easily
 VOLUME ["/files"]
diff --git a/docs/hop-user-manual/modules/ROOT/pages/hop-server/rest-api.adoc 
b/docs/hop-user-manual/modules/ROOT/pages/hop-server/rest-api.adoc
index d5a7362f9a..72c5b658e8 100644
--- a/docs/hop-user-manual/modules/ROOT/pages/hop-server/rest-api.adoc
+++ b/docs/hop-user-manual/modules/ROOT/pages/hop-server/rest-api.adoc
@@ -24,7 +24,14 @@ Hop Server has a rich set of web services that can be used 
to query and manage t
 
 TIP: when specified, the id for a workflow or pipeline represents one 
execution of a workflow or pipeline on the server.
 
-== addExport
+== addExport _(deprecated)_
+
+[WARNING]
+====
+*Deprecated since 2.18.0.* Use `registerPackage` (`hop/registerPackage`) 
instead.
+The remote pipeline and workflow engines call `registerPackage` for all export 
operations.
+This endpoint will be removed in a future release.
+====
 
 name::
 addExport
@@ -32,6 +39,7 @@ addExport
 description::
 Upload a resources export file.
 Add a zipped pipeline or workflow to the body payload as a binary file.
+*Deprecated* — use `registerPackage` instead.
 
 endPoint::
 GET `hop/addExport`
@@ -56,13 +64,20 @@ A zip file with the export is created on the server's file 
system.
 </webresult>
 ----
 
-== addPipeline
+== addPipeline _(deprecated)_
+
+[WARNING]
+====
+*Deprecated since 2.18.0.* Use <<Register Pipeline,`registerPipeline`>> 
(`hop/registerPipeline`) instead.
+This endpoint is no longer used by the remote pipeline engine and will be 
removed in a future release.
+====
 
 name::
 addPipeline
 
 description::
-Add a pipeline for execution
+Add a pipeline for execution.
+*Deprecated* — use `registerPipeline` instead.
 
 endPoint::
 GET `hop/addPipeline`
@@ -75,15 +90,22 @@ example request::
 `+http://localhost:8081/hop/addPipeline/xml=Y+` with XML payload
 
 result::
-- 
+-
+
+== addWorkflow _(deprecated)_
 
-== addWorkflow
+[WARNING]
+====
+*Deprecated since 2.18.0.* Use <<Register Workflow,`registerWorkflow`>> 
(`hop/registerWorkflow`) instead.
+This endpoint is no longer used by the remote workflow engine and will be 
removed in a future release.
+====
 
 name::
 addWorkflow
 
 description::
-Add a workflow for execution
+Add a workflow for execution.
+*Deprecated* — use `registerWorkflow` instead.
 
 endPoint::
 GET `hop/addWorkflow`
diff --git a/engine/pom.xml b/engine/pom.xml
index f5bad594c8..1633115e2b 100644
--- a/engine/pom.xml
+++ b/engine/pom.xml
@@ -120,10 +120,6 @@
                 </exclusion>
             </exclusions>
         </dependency>
-        <dependency>
-            <groupId>org.eclipse.jetty</groupId>
-            <artifactId>jetty-jaas</artifactId>
-        </dependency>
         <dependency>
             <groupId>org.eclipse.jetty</groupId>
             <artifactId>jetty-security</artifactId>
@@ -199,6 +195,10 @@
                 </exclusion>
             </exclusions>
         </dependency>
+        <dependency>
+            <groupId>org.jspecify</groupId>
+            <artifactId>jspecify</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.mozilla</groupId>
             <artifactId>rhino-all</artifactId>
diff --git a/engine/src/main/java/org/apache/hop/www/AddExportServlet.java 
b/engine/src/main/java/org/apache/hop/www/AddExportServlet.java
index 94fe726c3b..2f20374d71 100644
--- a/engine/src/main/java/org/apache/hop/www/AddExportServlet.java
+++ b/engine/src/main/java/org/apache/hop/www/AddExportServlet.java
@@ -56,8 +56,13 @@ import org.w3c.dom.Document;
  * a zip file. It ends up in a temporary file.
  *
  * <p>The servlet returns the name of the file stored.
+ *
+ * @deprecated Use {@link RegisterPackageServlet} ({@code 
/hop/registerPackage}) instead. The remote
+ *     pipeline and workflow engines call {@code registerPackage} for all 
export operations. This
+ *     endpoint will be removed in a future release.
  */
-@HopServerServlet(id = "addExport", name = "Upload a resources export file")
+@Deprecated(since = "2.18.0")
+@HopServerServlet(id = "addExport", name = "Upload a resources export file 
(deprecated)")
 public class AddExportServlet extends BaseHttpServlet implements 
IHopServerPlugin {
   public static final String PARAMETER_LOAD = "load";
   public static final String PARAMETER_TYPE = "type";
@@ -84,7 +89,10 @@ public class AddExportServlet extends BaseHttpServlet 
implements IHopServerPlugi
       logDebug("Addition of export requested");
     }
 
-    PrintWriter out = response.getWriter();
+    PrintWriter out = getSafeWriter(response);
+    if (out == null) {
+      return;
+    }
     InputStream in = request.getInputStream(); // read from the client
     if (log.isDetailed()) {
       logDetailed("Encoding: " + request.getCharacterEncoding());
diff --git a/engine/src/main/java/org/apache/hop/www/AddPipelineServlet.java 
b/engine/src/main/java/org/apache/hop/www/AddPipelineServlet.java
index 343d404df0..988599f3fa 100644
--- a/engine/src/main/java/org/apache/hop/www/AddPipelineServlet.java
+++ b/engine/src/main/java/org/apache/hop/www/AddPipelineServlet.java
@@ -42,7 +42,13 @@ import org.apache.hop.pipeline.PipelineMeta;
 import org.apache.hop.pipeline.engine.IPipelineEngine;
 import org.apache.hop.pipeline.engine.PipelineEngineFactory;
 
-@HopServerServlet(id = "addPipeline", name = "Add a pipeline for execution")
+/**
+ * @deprecated Use {@link RegisterPipelineServlet} ({@code 
/hop/registerPipeline}) instead. This
+ *     endpoint is no longer called by the remote pipeline engine and will be 
removed in a future
+ *     release.
+ */
+@Deprecated(since = "2.18.0")
+@HopServerServlet(id = "addPipeline", name = "Add a pipeline for execution 
(deprecated)")
 public class AddPipelineServlet extends BaseHttpServlet implements 
IHopServerPlugin {
   @Serial private static final long serialVersionUID = -6850701762586992604L;
 
@@ -67,8 +73,14 @@ public class AddPipelineServlet extends BaseHttpServlet 
implements IHopServerPlu
 
     boolean useXML = "Y".equalsIgnoreCase(request.getParameter("xml"));
 
-    PrintWriter out = response.getWriter();
-    BufferedReader in = request.getReader();
+    PrintWriter out = getSafeWriter(response);
+    if (out == null) {
+      return;
+    }
+    BufferedReader in = getSafeReader(request, response);
+    if (in == null) {
+      return;
+    }
     if (log.isDetailed()) {
       logDetailed("Encoding: " + request.getCharacterEncoding());
     }
diff --git a/engine/src/main/java/org/apache/hop/www/AddWorkflowServlet.java 
b/engine/src/main/java/org/apache/hop/www/AddWorkflowServlet.java
index b8a7d8d474..12ceb0ebd2 100644
--- a/engine/src/main/java/org/apache/hop/www/AddWorkflowServlet.java
+++ b/engine/src/main/java/org/apache/hop/www/AddWorkflowServlet.java
@@ -42,7 +42,13 @@ import org.apache.hop.workflow.action.ActionMeta;
 import org.apache.hop.workflow.engine.IWorkflowEngine;
 import org.apache.hop.workflow.engine.WorkflowEngineFactory;
 
-@HopServerServlet(id = "addWorkflow", name = "Add a workflow to the server")
+/**
+ * @deprecated Use {@link RegisterWorkflowServlet} ({@code 
/hop/registerWorkflow}) instead. This
+ *     endpoint is no longer called by the remote workflow engine and will be 
removed in a future
+ *     release.
+ */
+@Deprecated(since = "2.18.0")
+@HopServerServlet(id = "addWorkflow", name = "Add a workflow to the server 
(deprecated)")
 public class AddWorkflowServlet extends BaseHttpServlet implements 
IHopServerPlugin {
   @Serial private static final long serialVersionUID = -6850701762586992604L;
 
@@ -67,8 +73,14 @@ public class AddWorkflowServlet extends BaseHttpServlet 
implements IHopServerPlu
 
     boolean useXML = "Y".equalsIgnoreCase(request.getParameter("xml"));
 
-    PrintWriter out = response.getWriter();
-    BufferedReader in = request.getReader(); // read from the client
+    PrintWriter out = getSafeWriter(response);
+    if (out == null) {
+      return;
+    }
+    BufferedReader in = getSafeReader(request, response);
+    if (in == null) {
+      return;
+    }
     if (log.isDetailed()) {
       logDetailed("Encoding: " + request.getCharacterEncoding());
     }
@@ -196,7 +208,7 @@ public class AddWorkflowServlet extends BaseHttpServlet 
implements IHopServerPlu
 
   protected String[] getAllArgumentStrings(Map<String, String> arguments) {
     if (Utils.isEmpty(arguments)) {
-      return null;
+      return new String[0];
     }
 
     String[] argNames = arguments.keySet().toArray(new 
String[arguments.size()]);
diff --git a/engine/src/main/java/org/apache/hop/www/BaseHopServerPlugin.java 
b/engine/src/main/java/org/apache/hop/www/BaseHopServerPlugin.java
index 1dacab357b..d1a95c954b 100644
--- a/engine/src/main/java/org/apache/hop/www/BaseHopServerPlugin.java
+++ b/engine/src/main/java/org/apache/hop/www/BaseHopServerPlugin.java
@@ -19,6 +19,7 @@ package org.apache.hop.www;
 
 import com.google.common.collect.FluentIterable;
 import com.google.common.collect.ImmutableMultimap;
+import jakarta.servlet.ServletException;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import java.io.IOException;
@@ -40,8 +41,15 @@ public abstract class BaseHopServerPlugin extends 
BaseHttpServlet
    */
   @Deprecated(since = "2.0")
   @Override
-  public void doGet(HttpServletRequest req, final HttpServletResponse resp) 
throws IOException {
-    service(req, resp);
+  public void doGet(HttpServletRequest req, final HttpServletResponse resp)
+      throws ServletException, IOException {
+    try {
+      service(req, resp);
+    } catch (IOException e) {
+      logError("I/O error servicing request for " + getContextPath(), e);
+      sendSafeError(
+          resp, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Unable to 
process server request.");
+    }
   }
 
   @Override
@@ -57,12 +65,6 @@ public abstract class BaseHopServerPlugin extends 
BaseHttpServlet
     handleRequest(new HopServerRequestImpl(req, resp));
   }
 
-  @Override
-  public abstract void handleRequest(IHopServerRequest request) throws 
IOException;
-
-  @Override
-  public abstract String getContextPath();
-
   @Override
   public String getService() {
     return getContextPath() + " (" + toString() + ")";
diff --git a/engine/src/main/java/org/apache/hop/www/BaseHttpServlet.java 
b/engine/src/main/java/org/apache/hop/www/BaseHttpServlet.java
index c8a840ac66..913b0f8b25 100644
--- a/engine/src/main/java/org/apache/hop/www/BaseHttpServlet.java
+++ b/engine/src/main/java/org/apache/hop/www/BaseHttpServlet.java
@@ -21,8 +21,12 @@ import jakarta.servlet.ServletException;
 import jakarta.servlet.http.HttpServlet;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
+import java.io.BufferedReader;
 import java.io.IOException;
+import java.io.PrintWriter;
 import java.io.Serial;
+import lombok.Getter;
+import lombok.Setter;
 import org.apache.hop.core.Const;
 import org.apache.hop.core.logging.ILogChannel;
 import org.apache.hop.core.logging.LogChannel;
@@ -33,15 +37,17 @@ import org.apache.http.entity.ContentType;
 public class BaseHttpServlet extends HttpServlet {
   @Serial protected static final long serialVersionUID = -1348342810327662788L;
 
-  protected PipelineMap pipelineMap;
-  protected WorkflowMap workflowMap;
-  protected HopServerConfig serverConfig;
+  @Setter @Getter protected PipelineMap pipelineMap;
+
+  @Setter @Getter protected WorkflowMap workflowMap;
+
+  @Setter @Getter protected HopServerConfig serverConfig;
   protected IVariables variables;
   protected boolean supportGraphicEnvironment;
 
-  private boolean jettyMode = false;
+  @Setter @Getter private boolean jettyMode = false;
 
-  protected ILogChannel log = new LogChannel("Servlet");
+  @Setter @Getter protected ILogChannel log = new LogChannel("Servlet");
 
   public String convertContextPath(String contextPath) {
     if (jettyMode) {
@@ -105,19 +111,90 @@ public class BaseHttpServlet extends HttpServlet {
   @Override
   protected void doPut(HttpServletRequest request, HttpServletResponse 
response)
       throws ServletException, IOException {
-    doGet(request, response);
+    try {
+      doGet(request, response);
+    } catch (Exception e) {
+      logError("Error handling PUT request", e);
+      sendSafeError(
+          response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Unable to 
process PUT request.");
+    }
   }
 
   @Override
   protected void doPost(HttpServletRequest request, HttpServletResponse 
response)
       throws ServletException, IOException {
-    doGet(request, response);
+    try {
+      doGet(request, response);
+    } catch (Exception e) {
+      logError("Error handling POST request", e);
+      sendSafeError(
+          response,
+          HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+          "Unable to process POST request.");
+    }
   }
 
   @Override
   protected void doDelete(HttpServletRequest req, HttpServletResponse resp)
       throws ServletException, IOException {
-    doGet(req, resp);
+    try {
+      doGet(req, resp);
+    } catch (Exception e) {
+      logError("Error handling DELETE request", e);
+      sendSafeError(
+          resp, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Unable to 
process DELETE request.");
+    }
+  }
+
+  protected boolean isJsonRequest(HttpServletRequest request) {
+    return "Y".equalsIgnoreCase(request.getParameter("json"));
+  }
+
+  protected void setResponseFormat(HttpServletResponse response, boolean 
useXml, boolean useJson) {
+    if (useXml) {
+      response.setContentType("text/xml");
+      response.setCharacterEncoding(Const.XML_ENCODING);
+    } else if (useJson) {
+      response.setContentType("application/json");
+      response.setCharacterEncoding(Const.XML_ENCODING);
+    } else {
+      response.setContentType("text/html;charset=UTF-8");
+    }
+  }
+
+  protected PrintWriter getSafeWriter(HttpServletResponse response) {
+    try {
+      return response.getWriter();
+    } catch (IOException e) {
+      log.logError("Failed to obtain response writer", e);
+      sendSafeError(
+          response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Unable to 
process request.");
+      return null;
+    }
+  }
+
+  protected BufferedReader getSafeReader(HttpServletRequest request, 
HttpServletResponse response) {
+    try {
+      return request.getReader();
+    } catch (IOException e) {
+      log.logError("Failed to obtain request reader", e);
+      sendSafeError(
+          response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Unable to 
process request.");
+      return null;
+    }
+  }
+
+  protected void sendSafeError(HttpServletResponse response, int status, 
String message) {
+    if (response.isCommitted()) {
+      response.setStatus(status);
+      return;
+    }
+    try {
+      response.sendError(status, message);
+    } catch (IOException e) {
+      log.logError("Failed to send error response (" + status + "): " + 
message, e);
+      response.setStatus(status);
+    }
   }
 
   public PipelineMap getPipelineMap() {
@@ -134,14 +211,6 @@ public class BaseHttpServlet extends HttpServlet {
     return workflowMap;
   }
 
-  public boolean isJettyMode() {
-    return jettyMode;
-  }
-
-  public void setJettyMode(boolean jettyMode) {
-    this.jettyMode = jettyMode;
-  }
-
   public void logMinimal(String s) {
     log.logMinimal(s);
   }
@@ -189,52 +258,6 @@ public class BaseHttpServlet extends HttpServlet {
     this.variables = serverConfig.getVariables();
   }
 
-  /**
-   * @param pipelineMap The pipelineMap to set
-   */
-  public void setPipelineMap(PipelineMap pipelineMap) {
-    this.pipelineMap = pipelineMap;
-  }
-
-  /**
-   * @param workflowMap The workflowMap to set
-   */
-  public void setWorkflowMap(WorkflowMap workflowMap) {
-    this.workflowMap = workflowMap;
-  }
-
-  /**
-   * Gets serverConfig
-   *
-   * @return value of serverConfig
-   */
-  public HopServerConfig getServerConfig() {
-    return serverConfig;
-  }
-
-  /**
-   * @param serverConfig The serverConfig to set
-   */
-  public void setServerConfig(HopServerConfig serverConfig) {
-    this.serverConfig = serverConfig;
-  }
-
-  /**
-   * Gets log
-   *
-   * @return value of log
-   */
-  public ILogChannel getLog() {
-    return log;
-  }
-
-  /**
-   * @param log The log to set
-   */
-  public void setLog(ILogChannel log) {
-    this.log = log;
-  }
-
   private String getContentEncoding(String contentTypeValue) {
     ContentType contentType = ContentType.parse(contentTypeValue);
     if ("text/xml".equals(contentType.getMimeType())) {
diff --git a/engine/src/main/java/org/apache/hop/www/BodyHttpServlet.java 
b/engine/src/main/java/org/apache/hop/www/BodyHttpServlet.java
index 09c24c397b..0e54a97207 100644
--- a/engine/src/main/java/org/apache/hop/www/BodyHttpServlet.java
+++ b/engine/src/main/java/org/apache/hop/www/BodyHttpServlet.java
@@ -52,32 +52,42 @@ public abstract class BodyHttpServlet extends 
BaseHttpServlet implements IHopSer
     }
 
     boolean useXML = useXML(request);
+    boolean useJson = isJsonRequest(request);
     PrintWriter out = new PrintWriter(response.getOutputStream());
 
     try {
 
       if (useXML) {
         startXml(response, out);
+      } else if (useJson) {
+        response.setContentType("application/json");
+        response.setCharacterEncoding(Const.XML_ENCODING);
       } else {
         beginHtml(response, out);
       }
 
       WebResult result = generateBody(request, response, useXML, variables);
       if (result != null) {
-        out.println(result.getXml());
+        if (useJson) {
+          out.println(result.getJson());
+        } else {
+          out.println(result.getXml());
+        }
       }
 
     } catch (Exception e) {
       String st = ExceptionUtils.getFullStackTrace(e);
       if (useXML) {
         out.println(new WebResult(WebResult.STRING_ERROR, st).getXml());
+      } else if (useJson) {
+        out.println(new WebResult(WebResult.STRING_ERROR, st).getJson());
       } else {
         out.println("<p><pre>");
         out.println(Encode.forHtml(st));
         out.println("</pre>");
       }
     } finally {
-      if (!useXML) {
+      if (!useXML && !useJson) {
         endHtml(out);
       }
       out.flush();
diff --git 
a/engine/src/main/java/org/apache/hop/www/DeleteExecutionInfoServlet.java 
b/engine/src/main/java/org/apache/hop/www/DeleteExecutionInfoServlet.java
index b2e7784dbc..07edd37eea 100644
--- a/engine/src/main/java/org/apache/hop/www/DeleteExecutionInfoServlet.java
+++ b/engine/src/main/java/org/apache/hop/www/DeleteExecutionInfoServlet.java
@@ -34,7 +34,13 @@ import org.apache.hop.execution.ExecutionInfoLocation;
 import org.apache.hop.metadata.api.IHopMetadataSerializer;
 import org.apache.hop.metadata.serializer.multi.MultiMetadataProvider;
 
-@HopServerServlet(id = "registerExecInfo", name = "Register execution 
information")
+/**
+ * @deprecated Execution deletion is now handled by {@link 
GetExecutionInfoServlet} ({@code
+ *     /hop/getExecInfo}) via its {@code DELETE} type parameter. This endpoint 
has no callers in the
+ *     application and will be removed in a future release.
+ */
+@Deprecated(since = "2.18.0")
+@HopServerServlet(id = "deleteExecInfo", name = "Delete execution information 
(deprecated)")
 public class DeleteExecutionInfoServlet extends BaseHttpServlet implements 
IHopServerPlugin {
   @Serial private static final long serialVersionUID = -1901302231769020201L;
 
@@ -67,8 +73,14 @@ public class DeleteExecutionInfoServlet extends 
BaseHttpServlet implements IHopS
     //
     String locationName = 
StringEscapeUtils.escapeHtml(request.getParameter(PARAMETER_LOCATION));
 
-    PrintWriter out = response.getWriter();
-    BufferedReader in = request.getReader();
+    PrintWriter out = getSafeWriter(response);
+    if (out == null) {
+      return;
+    }
+    BufferedReader in = getSafeReader(request, response);
+    if (in == null) {
+      return;
+    }
 
     response.setContentType("text/xml");
     out.print(XmlHandler.getXmlHeader());
diff --git a/engine/src/main/java/org/apache/hop/www/ExecPipelineServlet.java 
b/engine/src/main/java/org/apache/hop/www/ExecPipelineServlet.java
index 669da1782e..05cf7075f2 100644
--- a/engine/src/main/java/org/apache/hop/www/ExecPipelineServlet.java
+++ b/engine/src/main/java/org/apache/hop/www/ExecPipelineServlet.java
@@ -42,6 +42,7 @@ import org.apache.hop.pipeline.PipelineMeta;
 import org.apache.hop.pipeline.config.PipelineRunConfiguration;
 import org.apache.hop.pipeline.engine.IPipelineEngine;
 import org.apache.hop.pipeline.engine.PipelineEngineFactory;
+import org.owasp.encoder.Encode;
 
 @HopServerServlet(id = "execPipeline", name = "Execute pipeline from file 
path")
 public class ExecPipelineServlet extends BaseHttpServlet implements 
IHopServerPlugin {
@@ -97,23 +98,34 @@ public class ExecPipelineServlet extends BaseHttpServlet 
implements IHopServerPl
     String levelOption = request.getParameter(LEVEL);
     String runConfigOption = request.getParameter(RUN_CONFIG);
 
+    boolean useJson = isJsonRequest(request);
+
     response.setStatus(HttpServletResponse.SC_OK);
 
-    String encoding = System.getProperty("HOP_DEFAULT_SERVLET_ENCODING", null);
-    if (encoding != null && !Utils.isEmpty(encoding.trim())) {
-      response.setCharacterEncoding(encoding);
-      response.setContentType("text/html; charset=" + encoding);
-    } else {
-      response.setContentType("text/xml");
+    if (useJson) {
+      response.setContentType("application/json");
       response.setCharacterEncoding(Const.XML_ENCODING);
+    } else {
+      String encoding = System.getProperty("HOP_DEFAULT_SERVLET_ENCODING", 
null);
+      if (encoding != null && !Utils.isEmpty(encoding.trim())) {
+        response.setCharacterEncoding(encoding);
+        response.setContentType("text/html; charset=" + encoding);
+      } else {
+        response.setContentType("text/xml");
+        response.setCharacterEncoding(Const.XML_ENCODING);
+      }
     }
 
-    PrintWriter out = response.getWriter();
+    PrintWriter out = getSafeWriter(response);
+    if (out == null) {
+      return;
+    }
 
     if (pipelineOption == null) {
       response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
-      out.println(
-          new WebResult(WebResult.STRING_ERROR, "Missing mandatory parameter: 
" + PIPELINE));
+      WebResult missing =
+          new WebResult(WebResult.STRING_ERROR, "Missing mandatory parameter: 
" + PIPELINE);
+      out.println(useJson ? missing.getJson() : missing.getXml());
       return;
     }
 
@@ -229,30 +241,39 @@ public class ExecPipelineServlet extends BaseHttpServlet 
implements IHopServerPl
       // Check for errors
       if (pipeline.isFinished() && pipeline.getErrors() > 0) {
         response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
-        out.println(new WebResult(WebResult.STRING_ERROR, "Error executing 
pipeline: " + logging));
+        WebResult err =
+            new WebResult(WebResult.STRING_ERROR, "Error executing pipeline: " 
+ logging);
+        out.println(useJson ? err.getJson() : err.getXml());
       } else {
-        out.println(new WebResult(WebResult.STRING_OK, "Pipeline executed 
successfully"));
+        WebResult ok = new WebResult(WebResult.STRING_OK, "Pipeline executed 
successfully");
+        out.println(useJson ? ok.getJson() : ok.getXml());
       }
       out.flush();
 
     } catch (Exception ex) {
       if (ex.getMessage() != null && 
ex.getMessage().contains(UNABLE_TO_FIND_PIPELINE)) {
         response.setStatus(HttpServletResponse.SC_NOT_FOUND);
-        out.println(
+        String safePipelineOption =
+            pipelineOption != null ? Encode.forHtml(pipelineOption) : "null";
+        String safeResolved =
+            pipelineOption != null ? 
Encode.forHtml(variables.resolve(pipelineOption)) : "null";
+        WebResult notFound =
             new WebResult(
                 WebResult.STRING_ERROR,
                 "Unable to find pipeline: "
-                    + pipelineOption
+                    + safePipelineOption
                     + " (resolved: "
-                    + (pipelineOption != null ? 
variables.resolve(pipelineOption) : "null")
-                    + ")"));
+                    + safeResolved
+                    + ")");
+        out.println(useJson ? notFound.getJson() : notFound.getXml());
       } else {
         String logging = Const.getStackTracker(ex);
         response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
-        out.println(
+        WebResult err =
             new WebResult(
                 WebResult.STRING_ERROR,
-                "Unexpected error executing pipeline: " + Const.CR + logging));
+                "Unexpected error executing pipeline: " + Const.CR + logging);
+        out.println(useJson ? err.getJson() : err.getXml());
       }
     }
   }
diff --git a/engine/src/main/java/org/apache/hop/www/ExecWorkflowServlet.java 
b/engine/src/main/java/org/apache/hop/www/ExecWorkflowServlet.java
index 8a24f81d08..cdd24d784e 100644
--- a/engine/src/main/java/org/apache/hop/www/ExecWorkflowServlet.java
+++ b/engine/src/main/java/org/apache/hop/www/ExecWorkflowServlet.java
@@ -42,6 +42,7 @@ import org.apache.hop.workflow.WorkflowMeta;
 import org.apache.hop.workflow.config.WorkflowRunConfiguration;
 import org.apache.hop.workflow.engine.IWorkflowEngine;
 import org.apache.hop.workflow.engine.WorkflowEngineFactory;
+import org.owasp.encoder.Encode;
 
 @HopServerServlet(id = "execWorkflow", name = "Execute workflow from file 
path")
 public class ExecWorkflowServlet extends BaseHttpServlet implements 
IHopServerPlugin {
@@ -97,23 +98,34 @@ public class ExecWorkflowServlet extends BaseHttpServlet 
implements IHopServerPl
     String levelOption = request.getParameter(LEVEL);
     String runConfigOption = request.getParameter(RUN_CONFIG);
 
+    boolean useJson = isJsonRequest(request);
+
     response.setStatus(HttpServletResponse.SC_OK);
 
-    String encoding = System.getProperty("HOP_DEFAULT_SERVLET_ENCODING", null);
-    if (encoding != null && !Utils.isEmpty(encoding.trim())) {
-      response.setCharacterEncoding(encoding);
-      response.setContentType("text/html; charset=" + encoding);
-    } else {
-      response.setContentType("text/xml");
+    if (useJson) {
+      response.setContentType("application/json");
       response.setCharacterEncoding(Const.XML_ENCODING);
+    } else {
+      String encoding = System.getProperty("HOP_DEFAULT_SERVLET_ENCODING", 
null);
+      if (encoding != null && !Utils.isEmpty(encoding.trim())) {
+        response.setCharacterEncoding(encoding);
+        response.setContentType("text/html; charset=" + encoding);
+      } else {
+        response.setContentType("text/xml");
+        response.setCharacterEncoding(Const.XML_ENCODING);
+      }
     }
 
-    PrintWriter out = response.getWriter();
+    PrintWriter out = getSafeWriter(response);
+    if (out == null) {
+      return;
+    }
 
     if (workflowOption == null) {
       response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
-      out.println(
-          new WebResult(WebResult.STRING_ERROR, "Missing mandatory parameter: 
" + WORKFLOW));
+      WebResult missing =
+          new WebResult(WebResult.STRING_ERROR, "Missing mandatory parameter: 
" + WORKFLOW);
+      out.println(useJson ? missing.getJson() : missing.getXml());
       return;
     }
 
@@ -227,30 +239,39 @@ public class ExecWorkflowServlet extends BaseHttpServlet 
implements IHopServerPl
       Result result = workflow.getResult();
       if (workflow.isFinished() && (result == null || result.getNrErrors() > 
0)) {
         response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
-        out.println(new WebResult(WebResult.STRING_ERROR, "Error executing 
workflow: " + logging));
+        WebResult err =
+            new WebResult(WebResult.STRING_ERROR, "Error executing workflow: " 
+ logging);
+        out.println(useJson ? err.getJson() : err.getXml());
       } else {
-        out.println(new WebResult(WebResult.STRING_OK, "Workflow executed 
successfully"));
+        WebResult ok = new WebResult(WebResult.STRING_OK, "Workflow executed 
successfully");
+        out.println(useJson ? ok.getJson() : ok.getXml());
       }
       out.flush();
 
     } catch (Exception ex) {
       if (ex.getMessage() != null && 
ex.getMessage().contains(UNABLE_TO_FIND_WORKFLOW)) {
         response.setStatus(HttpServletResponse.SC_NOT_FOUND);
-        out.println(
+        String safeWorkflowOption =
+            workflowOption != null ? Encode.forHtml(workflowOption) : "null";
+        String safeResolved =
+            workflowOption != null ? 
Encode.forHtml(variables.resolve(workflowOption)) : "null";
+        WebResult notFound =
             new WebResult(
                 WebResult.STRING_ERROR,
                 "Unable to find workflow: "
-                    + workflowOption
+                    + safeWorkflowOption
                     + " (resolved: "
-                    + (workflowOption != null ? 
variables.resolve(workflowOption) : "null")
-                    + ")"));
+                    + safeResolved
+                    + ")");
+        out.println(useJson ? notFound.getJson() : notFound.getXml());
       } else {
         String logging = Const.getStackTracker(ex);
         response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
-        out.println(
+        WebResult err =
             new WebResult(
                 WebResult.STRING_ERROR,
-                "Unexpected error executing workflow: " + Const.CR + logging));
+                "Unexpected error executing workflow: " + Const.CR + logging);
+        out.println(useJson ? err.getJson() : err.getXml());
       }
     }
   }
diff --git 
a/engine/src/main/java/org/apache/hop/www/GetExecutionInfoServlet.java 
b/engine/src/main/java/org/apache/hop/www/GetExecutionInfoServlet.java
index 019475357e..396e6733b9 100644
--- a/engine/src/main/java/org/apache/hop/www/GetExecutionInfoServlet.java
+++ b/engine/src/main/java/org/apache/hop/www/GetExecutionInfoServlet.java
@@ -91,7 +91,10 @@ public class GetExecutionInfoServlet extends BaseHttpServlet 
implements IHopServ
     response.setContentType("application/json");
     response.setCharacterEncoding(Const.XML_ENCODING);
 
-    PrintWriter out = response.getWriter();
+    PrintWriter out = getSafeWriter(response);
+    if (out == null) {
+      return;
+    }
 
     // The type of information to request
     //
diff --git 
a/engine/src/main/java/org/apache/hop/www/GetPipelineStatusServlet.java 
b/engine/src/main/java/org/apache/hop/www/GetPipelineStatusServlet.java
index d6b28001b3..61ffdddd11 100644
--- a/engine/src/main/java/org/apache/hop/www/GetPipelineStatusServlet.java
+++ b/engine/src/main/java/org/apache/hop/www/GetPipelineStatusServlet.java
@@ -101,17 +101,7 @@ public class GetPipelineStatusServlet extends 
BaseHttpServlet implements IHopSer
 
     response.setStatus(HttpServletResponse.SC_OK);
 
-    if (useXml) {
-      response.setContentType("text/xml");
-      response.setCharacterEncoding(Const.XML_ENCODING);
-    }
-    if (useJson) {
-      response.setContentType("application/json");
-      response.setCharacterEncoding(Const.XML_ENCODING);
-    } else {
-      response.setCharacterEncoding("UTF-8");
-      response.setContentType("text/html;charset=UTF-8");
-    }
+    setResponseFormat(response, useXml, useJson);
 
     // ID is optional...
     //
@@ -201,7 +191,10 @@ public class GetPipelineStatusServlet extends 
BaseHttpServlet implements IHopSer
           throw new ServletException("Unable to get the pipeline status in XML 
or JSON format", e);
         }
       } else {
-        PrintWriter out = response.getWriter();
+        PrintWriter out = getSafeWriter(response);
+        if (out == null) {
+          return;
+        }
 
         int lastLineNr = HopLogStore.getLastBufferLineNr();
         int tableBorder = 0;
@@ -578,7 +571,10 @@ public class GetPipelineStatusServlet extends 
BaseHttpServlet implements IHopSer
         out.println("</HTML>");
       }
     } else {
-      PrintWriter out = response.getWriter();
+      PrintWriter out = getSafeWriter(response);
+      if (out == null) {
+        return;
+      }
       if (useXml) {
         out.println(
             new WebResult(
diff --git a/engine/src/main/java/org/apache/hop/www/GetRootServlet.java 
b/engine/src/main/java/org/apache/hop/www/GetRootServlet.java
index e9c2cfe8a1..47e5d70c0c 100644
--- a/engine/src/main/java/org/apache/hop/www/GetRootServlet.java
+++ b/engine/src/main/java/org/apache/hop/www/GetRootServlet.java
@@ -49,7 +49,10 @@ public class GetRootServlet extends BaseHttpServlet 
implements IHopServerPlugin
     response.setContentType("text/html;charset=UTF-8");
     response.setStatus(HttpServletResponse.SC_OK);
 
-    PrintWriter out = response.getWriter();
+    PrintWriter out = getSafeWriter(response);
+    if (out == null) {
+      return;
+    }
 
     out.println("<HTML>");
     out.println(
diff --git a/engine/src/main/java/org/apache/hop/www/GetStatusServlet.java 
b/engine/src/main/java/org/apache/hop/www/GetStatusServlet.java
index 3a96ea76de..d774013db6 100644
--- a/engine/src/main/java/org/apache/hop/www/GetStatusServlet.java
+++ b/engine/src/main/java/org/apache/hop/www/GetStatusServlet.java
@@ -106,18 +106,12 @@ public class GetStatusServlet extends BaseHttpServlet 
implements IHopServerPlugi
     boolean useJson = "Y".equalsIgnoreCase(request.getParameter("json"));
     boolean useLightTheme = 
"Y".equalsIgnoreCase(request.getParameter("useLightTheme"));
 
-    if (useXml) {
-      response.setContentType("text/xml");
-      response.setCharacterEncoding(Const.XML_ENCODING);
-    }
-    if (useJson) {
-      response.setContentType("application/json");
-      response.setCharacterEncoding(Const.XML_ENCODING);
-    } else {
-      response.setContentType("text/html;charset=UTF-8");
-    }
+    setResponseFormat(response, useXml, useJson);
 
-    PrintWriter out = response.getWriter();
+    PrintWriter out = getSafeWriter(response);
+    if (out == null) {
+      return;
+    }
 
     List<HopServerObjectEntry> pipelineEntries = 
getPipelineMap().getPipelineObjects();
     List<HopServerObjectEntry> actions = getWorkflowMap().getWorkflowObjects();
diff --git 
a/engine/src/main/java/org/apache/hop/www/GetWorkflowStatusServlet.java 
b/engine/src/main/java/org/apache/hop/www/GetWorkflowStatusServlet.java
index bf8b47e227..ec3a5c2985 100644
--- a/engine/src/main/java/org/apache/hop/www/GetWorkflowStatusServlet.java
+++ b/engine/src/main/java/org/apache/hop/www/GetWorkflowStatusServlet.java
@@ -92,16 +92,7 @@ public class GetWorkflowStatusServlet extends 
BaseHttpServlet implements IHopSer
 
     response.setStatus(HttpServletResponse.SC_OK);
 
-    if (useXml) {
-      response.setContentType("text/xml");
-      response.setCharacterEncoding(Const.XML_ENCODING);
-    }
-    if (useJson) {
-      response.setContentType("application/json");
-      response.setCharacterEncoding(Const.XML_ENCODING);
-    } else {
-      response.setContentType("text/html;charset=UTF-8");
-    }
+    setResponseFormat(response, useXml, useJson);
 
     // ID is optional...
     //
@@ -206,7 +197,10 @@ public class GetWorkflowStatusServlet extends 
BaseHttpServlet implements IHopSer
         }
       } else {
 
-        PrintWriter out = response.getWriter();
+        PrintWriter out = getSafeWriter(response);
+        if (out == null) {
+          return;
+        }
 
         int lastLineNr = HopLogStore.getLastBufferLineNr();
         int tableBorder = 0;
@@ -436,7 +430,10 @@ public class GetWorkflowStatusServlet extends 
BaseHttpServlet implements IHopSer
         out.println("</HTML>");
       }
     } else {
-      PrintWriter out = response.getWriter();
+      PrintWriter out = getSafeWriter(response);
+      if (out == null) {
+        return;
+      }
       if (useXml) {
         out.println(
             new WebResult(
diff --git a/engine/src/main/java/org/apache/hop/www/HopServerServlet.java 
b/engine/src/main/java/org/apache/hop/www/HopServerServlet.java
index bc569032b2..ee44a0e646 100644
--- a/engine/src/main/java/org/apache/hop/www/HopServerServlet.java
+++ b/engine/src/main/java/org/apache/hop/www/HopServerServlet.java
@@ -53,7 +53,26 @@ public class HopServerServlet extends HttpServlet {
   @Override
   public void doPost(HttpServletRequest req, HttpServletResponse resp)
       throws ServletException, IOException {
-    doGet(req, resp);
+    try {
+      doGet(req, resp);
+    } catch (Exception e) {
+      log.logError("Error handling HopServer POST request", e);
+      sendSafeError(
+          resp, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Unable to 
process server request.");
+    }
+  }
+
+  private void sendSafeError(HttpServletResponse response, int status, String 
message) {
+    if (response.isCommitted()) {
+      response.setStatus(status);
+      return;
+    }
+    try {
+      response.sendError(status, message);
+    } catch (IOException e) {
+      log.logError("Failed to send error response (" + status + "): " + 
message, e);
+      response.setStatus(status);
+    }
   }
 
   @Override
diff --git a/engine/src/main/java/org/apache/hop/www/PausePipelineServlet.java 
b/engine/src/main/java/org/apache/hop/www/PausePipelineServlet.java
index cf08f27f8c..f54f7cc263 100644
--- a/engine/src/main/java/org/apache/hop/www/PausePipelineServlet.java
+++ b/engine/src/main/java/org/apache/hop/www/PausePipelineServlet.java
@@ -61,19 +61,23 @@ public class PausePipelineServlet extends BaseHttpServlet 
implements IHopServerP
     String pipelineName = request.getParameter("name");
     String id = request.getParameter("id");
     boolean useXML = "Y".equalsIgnoreCase(request.getParameter("xml"));
+    boolean useJson = isJsonRequest(request);
 
     response.setStatus(HttpServletResponse.SC_OK);
 
-    response.setCharacterEncoding("UTF-8");
-
-    PrintWriter out = response.getWriter();
+    PrintWriter out = getSafeWriter(response);
+    if (out == null) {
+      return;
+    }
     try {
       if (useXML) {
         response.setContentType("text/xml");
         response.setCharacterEncoding(Const.XML_ENCODING);
         out.print(XmlHandler.getXmlHeader(Const.XML_ENCODING));
+      } else if (useJson) {
+        response.setContentType("application/json");
+        response.setCharacterEncoding(Const.XML_ENCODING);
       } else {
-
         response.setContentType("text/html;charset=UTF-8");
         out.println("<HTML>");
         out.println("<HEAD>");
@@ -133,6 +137,8 @@ public class PausePipelineServlet extends BaseHttpServlet 
implements IHopServerP
 
         if (useXML) {
           out.println(new WebResult(WebResult.STRING_OK, message).getXml());
+        } else if (useJson) {
+          out.println(new WebResult(WebResult.STRING_OK, message).getJson());
         } else {
           out.println("<H1>" + Encode.forHtml(message) + "</H1>");
           out.println(
@@ -152,6 +158,8 @@ public class PausePipelineServlet extends BaseHttpServlet 
implements IHopServerP
 
         if (useXML) {
           out.println(new WebResult(WebResult.STRING_ERROR, message).getXml());
+        } else if (useJson) {
+          out.println(new WebResult(WebResult.STRING_ERROR, 
message).getJson());
         } else {
           out.println("<H1>" + Encode.forHtml(message) + "</H1>");
           out.println(
@@ -166,16 +174,18 @@ public class PausePipelineServlet extends BaseHttpServlet 
implements IHopServerP
     } catch (Exception ex) {
       if (useXML) {
         out.println(new WebResult(WebResult.STRING_ERROR, 
Const.getStackTracker(ex)).getXml());
+      } else if (useJson) {
+        out.println(new WebResult(WebResult.STRING_ERROR, 
Const.getStackTracker(ex)).getJson());
       } else {
         out.println("<p>");
         out.println("<pre>");
-        out.println(Const.getStackTracker(ex));
+        out.println(Encode.forHtml(Const.getStackTracker(ex)));
         out.println("</pre>");
         response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
       }
     }
 
-    if (!useXML) {
+    if (!useXML && !useJson) {
       out.println("<p>");
       out.println("</BODY>");
       out.println("</HTML>");
diff --git 
a/engine/src/main/java/org/apache/hop/www/PrepareExecutionPipelineServlet.java 
b/engine/src/main/java/org/apache/hop/www/PrepareExecutionPipelineServlet.java
index 64bfac145a..93ba820393 100644
--- 
a/engine/src/main/java/org/apache/hop/www/PrepareExecutionPipelineServlet.java
+++ 
b/engine/src/main/java/org/apache/hop/www/PrepareExecutionPipelineServlet.java
@@ -72,16 +72,22 @@ public class PrepareExecutionPipelineServlet extends 
BaseHttpServlet implements
     String pipelineName = request.getParameter("name");
     String id = request.getParameter("id");
     boolean useXML = "Y".equalsIgnoreCase(request.getParameter("xml"));
+    boolean useJson = isJsonRequest(request);
 
     response.setStatus(HttpServletResponse.SC_OK);
 
-    PrintWriter out = response.getWriter();
+    PrintWriter out = getSafeWriter(response);
+    if (out == null) {
+      return;
+    }
     if (useXML) {
       response.setContentType("text/xml");
+      response.setCharacterEncoding(Const.XML_ENCODING);
       out.print(XmlHandler.getXmlHeader(Const.XML_ENCODING));
+    } else if (useJson) {
+      response.setContentType("application/json");
+      response.setCharacterEncoding(Const.XML_ENCODING);
     } else {
-
-      response.setCharacterEncoding("UTF-8");
       response.setContentType("text/html;charset=UTF-8");
 
       out.println("<HTML>");
@@ -140,8 +146,9 @@ public class PrepareExecutionPipelineServlet extends 
BaseHttpServlet implements
 
           if (useXML) {
             out.println(WebResult.OK.getXml());
+          } else if (useJson) {
+            out.println(WebResult.OK.getJson());
           } else {
-
             out.println(
                 CONST_HEADER_OPEN
                     + Encode.forHtml(
@@ -164,19 +171,20 @@ public class PrepareExecutionPipelineServlet extends 
BaseHttpServlet implements
               HopLogStore.getAppender()
                   .getBuffer(pipeline.getLogChannel().getLogChannelId(), true)
                   .toString();
+          String errorMsg =
+              BaseMessages.getString(
+                  PKG,
+                  "PrepareExecutionPipelineServlet.Error.PipelineInitFailed",
+                  Const.CR
+                      + logText
+                      + Const.CR
+                      + Const.getSimpleStackTrace(e)
+                      + Const.CR
+                      + Const.getStackTracker(e));
           if (useXML) {
-            out.println(
-                new WebResult(
-                    WebResult.STRING_ERROR,
-                    BaseMessages.getString(
-                        PKG,
-                        
"PrepareExecutionPipelineServlet.Error.PipelineInitFailed",
-                        Const.CR
-                            + logText
-                            + Const.CR
-                            + Const.getSimpleStackTrace(e)
-                            + Const.CR
-                            + Const.getStackTracker(e))));
+            out.println(new WebResult(WebResult.STRING_ERROR, 
errorMsg).getXml());
+          } else if (useJson) {
+            out.println(new WebResult(WebResult.STRING_ERROR, 
errorMsg).getJson());
           } else {
             out.println(
                 CONST_HEADER_OPEN
@@ -204,12 +212,13 @@ public class PrepareExecutionPipelineServlet extends 
BaseHttpServlet implements
           }
         }
       } else {
+        String notFoundMsg =
+            BaseMessages.getString(
+                PKG, "PipelineStatusServlet.Log.CoundNotFindSpecPipeline", 
pipelineName);
         if (useXML) {
-          out.println(
-              new WebResult(
-                  WebResult.STRING_ERROR,
-                  BaseMessages.getString(
-                      PKG, 
"PipelineStatusServlet.Log.CoundNotFindSpecPipeline", pipelineName)));
+          out.println(new WebResult(WebResult.STRING_ERROR, 
notFoundMsg).getXml());
+        } else if (useJson) {
+          out.println(new WebResult(WebResult.STRING_ERROR, 
notFoundMsg).getJson());
         } else {
           out.println(
               CONST_HEADER_OPEN
@@ -227,14 +236,15 @@ public class PrepareExecutionPipelineServlet extends 
BaseHttpServlet implements
         }
       }
     } catch (Exception ex) {
+      String errorMsg =
+          BaseMessages.getString(
+              PKG,
+              "PrepareExecutionPipelineServlet.Error.UnexpectedError",
+              Const.CR + Const.getStackTracker(ex));
       if (useXML) {
-        out.println(
-            new WebResult(
-                WebResult.STRING_ERROR,
-                BaseMessages.getString(
-                    PKG,
-                    "PrepareExecutionPipelineServlet.Error.UnexpectedError",
-                    Const.CR + Const.getStackTracker(ex))));
+        out.println(new WebResult(WebResult.STRING_ERROR, errorMsg).getXml());
+      } else if (useJson) {
+        out.println(new WebResult(WebResult.STRING_ERROR, errorMsg).getJson());
       } else {
         out.println("<p>");
         out.println("<pre>");
@@ -244,7 +254,7 @@ public class PrepareExecutionPipelineServlet extends 
BaseHttpServlet implements
       }
     }
 
-    if (!useXML) {
+    if (!useXML && !useJson) {
       out.println("<p>");
       out.println("</BODY>");
       out.println("</HTML>");
diff --git 
a/engine/src/main/java/org/apache/hop/www/RegisterExecutionInfoServlet.java 
b/engine/src/main/java/org/apache/hop/www/RegisterExecutionInfoServlet.java
index f9fe921d5f..c738a7215d 100644
--- a/engine/src/main/java/org/apache/hop/www/RegisterExecutionInfoServlet.java
+++ b/engine/src/main/java/org/apache/hop/www/RegisterExecutionInfoServlet.java
@@ -75,8 +75,14 @@ public class RegisterExecutionInfoServlet extends 
BaseHttpServlet implements IHo
     //
     String locationName = 
StringEscapeUtils.escapeHtml(request.getParameter(PARAMETER_LOCATION));
 
-    PrintWriter out = response.getWriter();
-    BufferedReader in = request.getReader();
+    PrintWriter out = getSafeWriter(response);
+    if (out == null) {
+      return;
+    }
+    BufferedReader in = getSafeReader(request, response);
+    if (in == null) {
+      return;
+    }
 
     response.setContentType("text/xml");
     out.print(XmlHandler.getXmlHeader());
diff --git a/engine/src/main/java/org/apache/hop/www/RemovePipelineServlet.java 
b/engine/src/main/java/org/apache/hop/www/RemovePipelineServlet.java
index d6427117ac..c461e34e03 100644
--- a/engine/src/main/java/org/apache/hop/www/RemovePipelineServlet.java
+++ b/engine/src/main/java/org/apache/hop/www/RemovePipelineServlet.java
@@ -60,20 +60,16 @@ public class RemovePipelineServlet extends BaseHttpServlet 
implements IHopServer
     String pipelineName = request.getParameter("name");
     String id = request.getParameter("id");
     boolean useXML = "Y".equalsIgnoreCase(request.getParameter("xml"));
+    boolean useJson = isJsonRequest(request);
 
     response.setStatus(HttpServletResponse.SC_OK);
+    setResponseFormat(response, useXML, useJson);
 
-    if (useXML) {
-      response.setContentType("text/xml");
-      response.setCharacterEncoding(Const.XML_ENCODING);
-    } else {
-      response.setContentType("text/html;charset=UTF-8");
+    PrintWriter out = getSafeWriter(response);
+    if (out == null) {
+      return;
     }
 
-    response.setCharacterEncoding("UTF-8");
-
-    PrintWriter out = response.getWriter();
-
     // ID is optional...
     //
     IPipelineEngine<PipelineMeta> pipeline;
@@ -101,13 +97,11 @@ public class RemovePipelineServlet extends BaseHttpServlet 
implements IHopServer
       getPipelineMap().removePipeline(entry);
 
       if (useXML) {
-        response.setContentType("text/xml");
-        response.setCharacterEncoding(Const.XML_ENCODING);
         out.print(XmlHandler.getXmlHeader(Const.XML_ENCODING));
         out.print(WebResult.OK.getXml());
+      } else if (useJson) {
+        out.println(WebResult.OK.getJson());
       } else {
-        response.setContentType("text/html;charset=UTF-8");
-
         out.println("<HTML>");
         out.println("<HEAD>");
         out.println(
@@ -136,12 +130,13 @@ public class RemovePipelineServlet extends 
BaseHttpServlet implements IHopServer
         out.println("</HTML>");
       }
     } else {
+      String notFoundMsg =
+          BaseMessages.getString(
+              PKG, "PipelineStatusServlet.Log.CoundNotFindSpecPipeline", 
pipelineName);
       if (useXML) {
-        out.println(
-            new WebResult(
-                WebResult.STRING_ERROR,
-                BaseMessages.getString(
-                    PKG, "PipelineStatusServlet.Log.CoundNotFindSpecPipeline", 
pipelineName)));
+        out.println(new WebResult(WebResult.STRING_ERROR, 
notFoundMsg).getXml());
+      } else if (useJson) {
+        out.println(new WebResult(WebResult.STRING_ERROR, 
notFoundMsg).getJson());
       } else {
         out.println(
             "<H1>"
diff --git a/engine/src/main/java/org/apache/hop/www/RemoveWorkflowServlet.java 
b/engine/src/main/java/org/apache/hop/www/RemoveWorkflowServlet.java
index 897f26a05a..424b7a6afb 100644
--- a/engine/src/main/java/org/apache/hop/www/RemoveWorkflowServlet.java
+++ b/engine/src/main/java/org/apache/hop/www/RemoveWorkflowServlet.java
@@ -61,18 +61,16 @@ public class RemoveWorkflowServlet extends BaseHttpServlet 
implements IHopServer
     String workflowName = request.getParameter("name");
     String id = request.getParameter("id");
     boolean useXML = "Y".equalsIgnoreCase(request.getParameter("xml"));
+    boolean useJson = isJsonRequest(request);
 
     response.setStatus(HttpServletResponse.SC_OK);
+    setResponseFormat(response, useXML, useJson);
 
-    if (useXML) {
-      response.setContentType("text/xml");
-      response.setCharacterEncoding(Const.XML_ENCODING);
-    } else {
-      response.setContentType("text/html;charset=UTF-8");
+    PrintWriter out = getSafeWriter(response);
+    if (out == null) {
+      return;
     }
 
-    PrintWriter out = response.getWriter();
-
     // ID is optional...
     //
     IWorkflowEngine<WorkflowMeta> workflow;
@@ -99,13 +97,11 @@ public class RemoveWorkflowServlet extends BaseHttpServlet 
implements IHopServer
       getWorkflowMap().removeWorkflow(entry);
 
       if (useXML) {
-        response.setContentType("text/xml");
-        response.setCharacterEncoding(Const.XML_ENCODING);
         out.print(XmlHandler.getXmlHeader(Const.XML_ENCODING));
         out.print(WebResult.OK.getXml());
+      } else if (useJson) {
+        out.println(WebResult.OK.getJson());
       } else {
-        response.setContentType("text/html;charset=UTF-8");
-
         out.println("<HTML>");
         out.println("<HEAD>");
         out.println(
@@ -134,12 +130,13 @@ public class RemoveWorkflowServlet extends 
BaseHttpServlet implements IHopServer
         out.println("</HTML>");
       }
     } else {
+      String notFoundMsg =
+          BaseMessages.getString(
+              PKG, "RemoveWorkflowServlet.Log.CoundNotFindSpecWorkflow", 
workflowName);
       if (useXML) {
-        out.println(
-            new WebResult(
-                WebResult.STRING_ERROR,
-                BaseMessages.getString(
-                    PKG, "RemoveWorkflowServlet.Log.CoundNotFindSpecWorkflow", 
workflowName)));
+        out.println(new WebResult(WebResult.STRING_ERROR, 
notFoundMsg).getXml());
+      } else if (useJson) {
+        out.println(new WebResult(WebResult.STRING_ERROR, 
notFoundMsg).getJson());
       } else {
         out.println(
             "<H1>"
diff --git a/engine/src/main/java/org/apache/hop/www/SniffTransformServlet.java 
b/engine/src/main/java/org/apache/hop/www/SniffTransformServlet.java
index f98e438fb0..73f2e3ac46 100644
--- a/engine/src/main/java/org/apache/hop/www/SniffTransformServlet.java
+++ b/engine/src/main/java/org/apache/hop/www/SniffTransformServlet.java
@@ -19,6 +19,8 @@ package org.apache.hop.www;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
 import jakarta.servlet.ServletException;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
@@ -30,6 +32,7 @@ import java.util.List;
 import org.apache.hop.core.Const;
 import org.apache.hop.core.annotations.HopServerServlet;
 import org.apache.hop.core.exception.HopTransformException;
+import org.apache.hop.core.json.HopJson;
 import org.apache.hop.core.row.IRowMeta;
 import org.apache.hop.core.row.IValueMeta;
 import org.apache.hop.core.row.RowBuffer;
@@ -77,18 +80,16 @@ public class SniffTransformServlet extends BaseHttpServlet 
implements IHopServer
     final int nrLines = Const.toInt(request.getParameter("lines"), 0);
     String type = Const.NVL(request.getParameter("type"), TYPE_OUTPUT);
     boolean useXML = "Y".equalsIgnoreCase(request.getParameter("xml"));
+    boolean useJson = isJsonRequest(request);
 
     response.setStatus(HttpServletResponse.SC_OK);
+    setResponseFormat(response, useXML, useJson);
 
-    if (useXML) {
-      response.setContentType("text/xml");
-      response.setCharacterEncoding(Const.XML_ENCODING);
-    } else {
-      response.setContentType("text/html;charset=UTF-8");
+    PrintWriter out = getSafeWriter(response);
+    if (out == null) {
+      return;
     }
 
-    PrintWriter out = response.getWriter();
-
     // ID is optional...
     //
     IPipelineEngine<PipelineMeta> pipeline;
@@ -201,6 +202,42 @@ public class SniffTransformServlet extends BaseHttpServlet 
implements IHopServer
           out.print(XmlHandler.getXmlHeader(Const.XML_ENCODING));
           out.println(rowBuffer.getXml());
 
+        } else if (useJson) {
+
+          // Send the result back as JSON
+          //
+          try {
+            ObjectNode root = HopJson.newMapper().createObjectNode();
+            if (rowBuffer.getRowMeta() != null) {
+              ArrayNode fields = root.putArray("fields");
+              for (IValueMeta vm : rowBuffer.getRowMeta().getValueMetaList()) {
+                ObjectNode field = fields.addObject();
+                field.put("name", vm.getName());
+                field.put("type", vm.getTypeDesc());
+              }
+              ArrayNode rows = root.putArray("rows");
+              for (Object[] rowData : rowBuffer.getBuffer()) {
+                ArrayNode row = rows.addArray();
+                for (int v = 0; v < rowBuffer.getRowMeta().size(); v++) {
+                  IValueMeta vm = rowBuffer.getRowMeta().getValueMeta(v);
+                  try {
+                    row.add(vm.getString(rowData[v]));
+                  } catch (Exception e) {
+                    row.addNull();
+                  }
+                }
+              }
+            }
+            out.println(
+                
HopJson.newMapper().writerWithDefaultPrettyPrinter().writeValueAsString(root));
+          } catch (Exception e) {
+            out.println(
+                new WebResult(
+                        WebResult.STRING_ERROR,
+                        "Failed to serialize rows to JSON: " + e.getMessage())
+                    .getJson());
+          }
+
         } else {
           response.setContentType("text/html;charset=UTF-8");
 
@@ -273,24 +310,15 @@ public class SniffTransformServlet extends 
BaseHttpServlet implements IHopServer
           out.println("</HTML>");
         }
       } else {
+        String notFoundMsg =
+            BaseMessages.getString(
+                PKG, "SniffTransformServlet.Log.CoundNotFindSpecTransform", 
transformName);
         if (useXML) {
-          out.println(
-              new WebResult(
-                      WebResult.STRING_ERROR,
-                      BaseMessages.getString(
-                          PKG,
-                          
"SniffTransformServlet.Log.CoundNotFindSpecTransform",
-                          transformName))
-                  .getXml());
+          out.println(new WebResult(WebResult.STRING_ERROR, 
notFoundMsg).getXml());
+        } else if (useJson) {
+          out.println(new WebResult(WebResult.STRING_ERROR, 
notFoundMsg).getJson());
         } else {
-          out.println(
-              CONST_HEADER_START
-                  + Encode.forHtml(
-                      BaseMessages.getString(
-                          PKG,
-                          
"SniffTransformServlet.Log.CoundNotFindSpecTransform",
-                          transformName))
-                  + CONST_HEADER_END);
+          out.println(CONST_HEADER_START + Encode.forHtml(notFoundMsg) + 
CONST_HEADER_END);
           out.println(
               "<a href=\""
                   + convertContextPath(GetStatusServlet.CONTEXT_PATH)
@@ -300,13 +328,13 @@ public class SniffTransformServlet extends 
BaseHttpServlet implements IHopServer
         }
       }
     } else {
+      String notFoundMsg =
+          BaseMessages.getString(
+              PKG, "SniffTransformServlet.Log.CoundNotFindSpecPipeline", 
pipelineName);
       if (useXML) {
-        out.println(
-            new WebResult(
-                    WebResult.STRING_ERROR,
-                    BaseMessages.getString(
-                        PKG, 
"SniffTransformServlet.Log.CoundNotFindSpecPipeline", pipelineName))
-                .getXml());
+        out.println(new WebResult(WebResult.STRING_ERROR, 
notFoundMsg).getXml());
+      } else if (useJson) {
+        out.println(new WebResult(WebResult.STRING_ERROR, 
notFoundMsg).getJson());
       } else {
         out.println(
             CONST_HEADER_START
diff --git 
a/engine/src/main/java/org/apache/hop/www/StartExecutionPipelineServlet.java 
b/engine/src/main/java/org/apache/hop/www/StartExecutionPipelineServlet.java
index 954c295055..dff3a56902 100644
--- a/engine/src/main/java/org/apache/hop/www/StartExecutionPipelineServlet.java
+++ b/engine/src/main/java/org/apache/hop/www/StartExecutionPipelineServlet.java
@@ -65,11 +65,19 @@ public class StartExecutionPipelineServlet extends 
BaseHttpServlet implements IH
     String pipelineName = 
StringEscapeUtils.escapeHtml(request.getParameter("name"));
     String id = request.getParameter("id");
     boolean useXML = "Y".equalsIgnoreCase(request.getParameter("xml"));
+    boolean useJson = isJsonRequest(request);
 
-    PrintWriter out = response.getWriter();
+    PrintWriter out = getSafeWriter(response);
+    if (out == null) {
+      return;
+    }
     if (useXML) {
       response.setContentType("text/xml");
+      response.setCharacterEncoding(Const.XML_ENCODING);
       out.print(XmlHandler.getXmlHeader(Const.XML_ENCODING));
+    } else if (useJson) {
+      response.setContentType("application/json");
+      response.setCharacterEncoding(Const.XML_ENCODING);
     } else {
       response.setContentType("text/html;charset=UTF-8");
       out.println("<HTML>");
@@ -119,6 +127,8 @@ public class StartExecutionPipelineServlet extends 
BaseHttpServlet implements IH
 
           if (useXML) {
             out.println(WebResult.OK.getXml());
+          } else if (useJson) {
+            out.println(WebResult.OK.getJson());
           } else {
             out.println(
                 "<H1>Pipeline "
@@ -139,7 +149,9 @@ public class StartExecutionPipelineServlet extends 
BaseHttpServlet implements IH
                   + pipelineName
                   + "] is not ready to be started. (Was not prepared for 
execution)";
           if (useXML) {
-            out.println(new WebResult(WebResult.STRING_ERROR, message));
+            out.println(new WebResult(WebResult.STRING_ERROR, 
message).getXml());
+          } else if (useJson) {
+            out.println(new WebResult(WebResult.STRING_ERROR, 
message).getJson());
           } else {
             out.println("<H1>" + Encode.forHtml(message) + "</H1>");
             out.println(
@@ -151,12 +163,13 @@ public class StartExecutionPipelineServlet extends 
BaseHttpServlet implements IH
           }
         }
       } else {
+        String notFoundMsg =
+            BaseMessages.getString(
+                PKG, "PipelineStatusServlet.Log.CoundNotFindSpecPipeline", 
pipelineName);
         if (useXML) {
-          out.println(
-              new WebResult(
-                  WebResult.STRING_ERROR,
-                  BaseMessages.getString(
-                      PKG, 
"PipelineStatusServlet.Log.CoundNotFindSpecPipeline", pipelineName)));
+          out.println(new WebResult(WebResult.STRING_ERROR, 
notFoundMsg).getXml());
+        } else if (useJson) {
+          out.println(new WebResult(WebResult.STRING_ERROR, 
notFoundMsg).getJson());
         } else {
           out.println(
               "<H1>"
@@ -173,13 +186,14 @@ public class StartExecutionPipelineServlet extends 
BaseHttpServlet implements IH
         }
       }
     } catch (Exception ex) {
+      String errorMsg =
+          "Unexpected error during pipeline execution preparation:"
+              + Const.CR
+              + Const.getStackTracker(ex);
       if (useXML) {
-        out.println(
-            new WebResult(
-                WebResult.STRING_ERROR,
-                "Unexpected error during pipeline execution preparation:"
-                    + Const.CR
-                    + Const.getStackTracker(ex)));
+        out.println(new WebResult(WebResult.STRING_ERROR, errorMsg).getXml());
+      } else if (useJson) {
+        out.println(new WebResult(WebResult.STRING_ERROR, errorMsg).getJson());
       } else {
         out.println("<p>");
         out.println("<pre>");
@@ -188,7 +202,7 @@ public class StartExecutionPipelineServlet extends 
BaseHttpServlet implements IH
       }
     }
 
-    if (!useXML) {
+    if (!useXML && !useJson) {
       out.println("<p>");
       out.println("</BODY>");
       out.println("</HTML>");
diff --git a/engine/src/main/java/org/apache/hop/www/StartPipelineServlet.java 
b/engine/src/main/java/org/apache/hop/www/StartPipelineServlet.java
index 331345e924..c79ca44679 100644
--- a/engine/src/main/java/org/apache/hop/www/StartPipelineServlet.java
+++ b/engine/src/main/java/org/apache/hop/www/StartPipelineServlet.java
@@ -73,14 +73,21 @@ public class StartPipelineServlet extends BaseHttpServlet 
implements IHopServerP
       pipelineName = "";
     }
     boolean useXML = "Y".equalsIgnoreCase(request.getParameter("xml"));
+    boolean useJson = isJsonRequest(request);
 
     response.setStatus(HttpServletResponse.SC_OK);
 
-    PrintWriter out = response.getWriter();
+    PrintWriter out = getSafeWriter(response);
+    if (out == null) {
+      return;
+    }
     if (useXML) {
       response.setContentType("text/xml");
       response.setCharacterEncoding(Const.XML_ENCODING);
       out.print(XmlHandler.getXmlHeader(Const.XML_ENCODING));
+    } else if (useJson) {
+      response.setContentType("application/json");
+      response.setCharacterEncoding(Const.XML_ENCODING);
     } else {
       response.setContentType("text/html;charset=UTF-8");
       out.println("<HTML>");
@@ -141,8 +148,9 @@ public class StartPipelineServlet extends BaseHttpServlet 
implements IHopServerP
             BaseMessages.getString(PKG, 
"StartPipelineServlet.Log.PipelineStarted", pipelineName);
         if (useXML) {
           out.println(new WebResult(WebResult.STRING_OK, message).getXml());
+        } else if (useJson) {
+          out.println(new WebResult(WebResult.STRING_OK, message).getJson());
         } else {
-
           out.println("<H1>" + Encode.forHtml(message) + "</H1>");
           out.println(
               "<a href=\""
@@ -160,7 +168,9 @@ public class StartPipelineServlet extends BaseHttpServlet 
implements IHopServerP
             BaseMessages.getString(
                 PKG, "PipelineStatusServlet.Log.CoundNotFindSpecPipeline", 
pipelineName);
         if (useXML) {
-          out.println(new WebResult(WebResult.STRING_ERROR, message, id));
+          out.println(new WebResult(WebResult.STRING_ERROR, message, 
id).getXml());
+        } else if (useJson) {
+          out.println(new WebResult(WebResult.STRING_ERROR, message, 
id).getJson());
         } else {
           out.println("<H1>" + Encode.forHtml(message) + "</H1>");
           out.println(
@@ -173,14 +183,15 @@ public class StartPipelineServlet extends BaseHttpServlet 
implements IHopServerP
         }
       }
     } catch (Exception ex) {
+      String errorMsg =
+          BaseMessages.getString(
+              PKG,
+              "StartPipelineServlet.Error.UnexpectedError",
+              Const.CR + Const.getStackTracker(ex));
       if (useXML) {
-        out.println(
-            new WebResult(
-                WebResult.STRING_ERROR,
-                BaseMessages.getString(
-                    PKG,
-                    "StartPipelineServlet.Error.UnexpectedError",
-                    Const.CR + Const.getStackTracker(ex))));
+        out.println(new WebResult(WebResult.STRING_ERROR, errorMsg).getXml());
+      } else if (useJson) {
+        out.println(new WebResult(WebResult.STRING_ERROR, errorMsg).getJson());
       } else {
         out.println("<p>");
         out.println("<pre>");
@@ -190,7 +201,7 @@ public class StartPipelineServlet extends BaseHttpServlet 
implements IHopServerP
       }
     }
 
-    if (!useXML) {
+    if (!useXML && !useJson) {
       out.println("<p>");
       out.println("</BODY>");
       out.println("</HTML>");
diff --git a/engine/src/main/java/org/apache/hop/www/StartWorkflowServlet.java 
b/engine/src/main/java/org/apache/hop/www/StartWorkflowServlet.java
index 202f15ca09..6ff69acdb0 100644
--- a/engine/src/main/java/org/apache/hop/www/StartWorkflowServlet.java
+++ b/engine/src/main/java/org/apache/hop/www/StartWorkflowServlet.java
@@ -72,14 +72,21 @@ public class StartWorkflowServlet extends BaseHttpServlet 
implements IHopServerP
     String workflowName = 
StringEscapeUtils.escapeHtml(request.getParameter("name"));
     String id = StringEscapeUtils.escapeHtml(request.getParameter("id"));
     boolean useXML = "Y".equalsIgnoreCase(request.getParameter("xml"));
+    boolean useJson = isJsonRequest(request);
 
     response.setStatus(HttpServletResponse.SC_OK);
 
-    PrintWriter out = response.getWriter();
+    PrintWriter out = getSafeWriter(response);
+    if (out == null) {
+      return;
+    }
     if (useXML) {
       response.setContentType("text/xml");
       response.setCharacterEncoding(Const.XML_ENCODING);
       out.print(XmlHandler.getXmlHeader(Const.XML_ENCODING));
+    } else if (useJson) {
+      response.setContentType("application/json");
+      response.setCharacterEncoding(Const.XML_ENCODING);
     } else {
       response.setContentType("text/html;charset=UTF-8");
       out.println("<HTML>");
@@ -164,8 +171,10 @@ public class StartWorkflowServlet extends BaseHttpServlet 
implements IHopServerP
         if (useXML) {
           out.println(
               new WebResult(WebResult.STRING_OK, message, 
workflow.getContainerId()).getXml());
+        } else if (useJson) {
+          out.println(
+              new WebResult(WebResult.STRING_OK, message, 
workflow.getContainerId()).getJson());
         } else {
-
           out.println("<H1>" + Encode.forHtml(message) + "</H1>");
           out.println(
               "<a href=\""
@@ -183,7 +192,9 @@ public class StartWorkflowServlet extends BaseHttpServlet 
implements IHopServerP
             BaseMessages.getString(
                 PKG, "StartWorkflowServlet.Log.SpecifiedWorkflowNotFound", 
workflowName);
         if (useXML) {
-          out.println(new WebResult(WebResult.STRING_ERROR, message));
+          out.println(new WebResult(WebResult.STRING_ERROR, message).getXml());
+        } else if (useJson) {
+          out.println(new WebResult(WebResult.STRING_ERROR, 
message).getJson());
         } else {
           out.println("<H1>" + Encode.forHtml(message) + "</H1>");
           out.println(
@@ -196,14 +207,15 @@ public class StartWorkflowServlet extends BaseHttpServlet 
implements IHopServerP
         }
       }
     } catch (Exception ex) {
+      String errorMsg =
+          BaseMessages.getString(
+              PKG,
+              "StartWorkflowServlet.Error.UnexpectedError",
+              Const.CR + Const.getStackTracker(ex));
       if (useXML) {
-        out.println(
-            new WebResult(
-                WebResult.STRING_ERROR,
-                BaseMessages.getString(
-                    PKG,
-                    "StartWorkflowServlet.Error.UnexpectedError",
-                    Const.CR + Const.getStackTracker(ex))));
+        out.println(new WebResult(WebResult.STRING_ERROR, errorMsg).getXml());
+      } else if (useJson) {
+        out.println(new WebResult(WebResult.STRING_ERROR, errorMsg).getJson());
       } else {
         out.println("<p>");
         out.println("<pre>");
@@ -213,7 +225,7 @@ public class StartWorkflowServlet extends BaseHttpServlet 
implements IHopServerP
       }
     }
 
-    if (!useXML) {
+    if (!useXML && !useJson) {
       out.println("<p>");
       out.println("</BODY>");
       out.println("</HTML>");
diff --git a/engine/src/main/java/org/apache/hop/www/StopPipelineServlet.java 
b/engine/src/main/java/org/apache/hop/www/StopPipelineServlet.java
index 6530af17be..5b7578acc6 100644
--- a/engine/src/main/java/org/apache/hop/www/StopPipelineServlet.java
+++ b/engine/src/main/java/org/apache/hop/www/StopPipelineServlet.java
@@ -62,13 +62,20 @@ public class StopPipelineServlet extends BaseHttpServlet 
implements IHopServerPl
     String id = request.getParameter("id");
     boolean inputOnly = 
"Y".equalsIgnoreCase(request.getParameter("inputOnly"));
     boolean useXML = "Y".equalsIgnoreCase(request.getParameter("xml"));
+    boolean useJson = isJsonRequest(request);
 
-    PrintWriter out = response.getWriter();
+    PrintWriter out = getSafeWriter(response);
+    if (out == null) {
+      return;
+    }
     try {
       if (useXML) {
         response.setContentType("text/xml");
         response.setCharacterEncoding(Const.XML_ENCODING);
         out.print(XmlHandler.getXmlHeader(Const.XML_ENCODING));
+      } else if (useJson) {
+        response.setContentType("application/json");
+        response.setCharacterEncoding(Const.XML_ENCODING);
       } else {
         response.setContentType("text/html;charset=UTF-8");
         out.println("<HTML>");
@@ -120,6 +127,8 @@ public class StopPipelineServlet extends BaseHttpServlet 
implements IHopServerPl
 
         if (useXML) {
           out.println(new WebResult(WebResult.STRING_OK, message, 
id).getXml());
+        } else if (useJson) {
+          out.println(new WebResult(WebResult.STRING_OK, message, 
id).getJson());
         } else {
           out.println("<H1>" + Encode.forHtml(message) + "</H1>");
           out.println(
@@ -139,6 +148,8 @@ public class StopPipelineServlet extends BaseHttpServlet 
implements IHopServerPl
 
         if (useXML) {
           out.println(new WebResult(WebResult.STRING_ERROR, message, 
id).getXml());
+        } else if (useJson) {
+          out.println(new WebResult(WebResult.STRING_ERROR, message, 
id).getJson());
         } else {
           out.println("<H1>" + Encode.forHtml(message) + "</H1>");
           out.println(
@@ -153,6 +164,8 @@ public class StopPipelineServlet extends BaseHttpServlet 
implements IHopServerPl
     } catch (Exception ex) {
       if (useXML) {
         out.println(new WebResult(WebResult.STRING_ERROR, 
Const.getStackTracker(ex)).getXml());
+      } else if (useJson) {
+        out.println(new WebResult(WebResult.STRING_ERROR, 
Const.getStackTracker(ex)).getJson());
       } else {
         out.println("<p>");
         out.println("<pre>");
@@ -162,7 +175,7 @@ public class StopPipelineServlet extends BaseHttpServlet 
implements IHopServerPl
       }
     }
 
-    if (!useXML) {
+    if (!useXML && !useJson) {
       out.println("<p>");
       out.println("</BODY>");
       out.println("</HTML>");
diff --git a/engine/src/main/java/org/apache/hop/www/StopWorkflowServlet.java 
b/engine/src/main/java/org/apache/hop/www/StopWorkflowServlet.java
index 4688cc227a..b199f43ce9 100644
--- a/engine/src/main/java/org/apache/hop/www/StopWorkflowServlet.java
+++ b/engine/src/main/java/org/apache/hop/www/StopWorkflowServlet.java
@@ -61,13 +61,20 @@ public class StopWorkflowServlet extends BaseHttpServlet 
implements IHopServerPl
     String workflowName = request.getParameter("name");
     String id = request.getParameter("id");
     boolean useXML = "Y".equalsIgnoreCase(request.getParameter("xml"));
+    boolean useJson = isJsonRequest(request);
 
-    PrintWriter out = response.getWriter();
+    PrintWriter out = getSafeWriter(response);
+    if (out == null) {
+      return;
+    }
     try {
       if (useXML) {
         response.setContentType("text/xml");
         response.setCharacterEncoding(Const.XML_ENCODING);
         out.print(XmlHandler.getXmlHeader(Const.XML_ENCODING));
+      } else if (useJson) {
+        response.setContentType("application/json");
+        response.setCharacterEncoding(Const.XML_ENCODING);
       } else {
         response.setContentType("text/html;charset=UTF-8");
         out.println("<HTML>");
@@ -113,6 +120,8 @@ public class StopWorkflowServlet extends BaseHttpServlet 
implements IHopServerPl
                 PKG, "WorkflowStatusServlet.Log.WorkflowStopRequested", 
workflowName);
         if (useXML) {
           out.println(new WebResult(WebResult.STRING_OK, message).getXml());
+        } else if (useJson) {
+          out.println(new WebResult(WebResult.STRING_OK, message).getJson());
         } else {
           out.println("<H1>" + Encode.forHtml(message) + "</H1>");
           out.println(
@@ -132,12 +141,14 @@ public class StopWorkflowServlet extends BaseHttpServlet 
implements IHopServerPl
                 PKG, "StopWorkflowServlet.Log.CoundNotFindWorkflow", 
workflowName);
         if (useXML) {
           out.println(new WebResult(WebResult.STRING_ERROR, message).getXml());
+        } else if (useJson) {
+          out.println(new WebResult(WebResult.STRING_ERROR, 
message).getJson());
         } else {
           out.println("<H1>" + Encode.forHtml(message) + "</H1>");
           out.println(
               "<a href=\""
                   + convertContextPath(GetStatusServlet.CONTEXT_PATH)
-                  + ">"
+                  + "\">"
                   + BaseMessages.getString(PKG, 
"PipelineStatusServlet.BackToStatusPage")
                   + "</a><p>");
           response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
@@ -146,6 +157,8 @@ public class StopWorkflowServlet extends BaseHttpServlet 
implements IHopServerPl
     } catch (Exception ex) {
       if (useXML) {
         out.println(new WebResult(WebResult.STRING_ERROR, 
Const.getStackTracker(ex)).getXml());
+      } else if (useJson) {
+        out.println(new WebResult(WebResult.STRING_ERROR, 
Const.getStackTracker(ex)).getJson());
       } else {
         out.println("<p>");
         out.println("<pre>");
@@ -155,7 +168,7 @@ public class StopWorkflowServlet extends BaseHttpServlet 
implements IHopServerPl
       }
     }
 
-    if (!useXML) {
+    if (!useXML && !useJson) {
       out.println("<p>");
       out.println("</BODY>");
       out.println("</HTML>");
diff --git a/engine/src/main/java/org/apache/hop/www/WebResult.java 
b/engine/src/main/java/org/apache/hop/www/WebResult.java
index dc1c5a21e0..362295041e 100644
--- a/engine/src/main/java/org/apache/hop/www/WebResult.java
+++ b/engine/src/main/java/org/apache/hop/www/WebResult.java
@@ -17,8 +17,10 @@
 
 package org.apache.hop.www;
 
+import com.fasterxml.jackson.core.JsonProcessingException;
 import org.apache.hop.core.Const;
 import org.apache.hop.core.exception.HopXmlException;
+import org.apache.hop.core.json.HopJson;
 import org.apache.hop.core.xml.XmlHandler;
 import org.apache.hop.i18n.BaseMessages;
 import org.w3c.dom.Document;
@@ -66,6 +68,14 @@ public class WebResult {
     return xml.toString();
   }
 
+  public String getJson() {
+    try {
+      return 
HopJson.newMapper().writerWithDefaultPrettyPrinter().writeValueAsString(this);
+    } catch (JsonProcessingException e) {
+      return "{\"result\":\"ERROR\",\"message\":\"Failed to serialize response 
to JSON\",\"id\":null}";
+    }
+  }
+
   @Override
   public String toString() {
     return getXml();
diff --git a/engine/src/main/java/org/apache/hop/www/WebServer.java 
b/engine/src/main/java/org/apache/hop/www/WebServer.java
index a3c90552b3..273f0907ea 100644
--- a/engine/src/main/java/org/apache/hop/www/WebServer.java
+++ b/engine/src/main/java/org/apache/hop/www/WebServer.java
@@ -49,12 +49,12 @@ import org.eclipse.jetty.ee11.servlet.ServletHolder;
 import org.eclipse.jetty.ee11.servlet.security.ConstraintMapping;
 import org.eclipse.jetty.ee11.servlet.security.ConstraintSecurityHandler;
 import org.eclipse.jetty.http.HttpVersion;
-import org.eclipse.jetty.jaas.JAASLoginService;
 import org.eclipse.jetty.security.Authenticator;
 import org.eclipse.jetty.security.Constraint;
 import org.eclipse.jetty.security.HashLoginService;
 import org.eclipse.jetty.security.PropertyUserStore;
 import org.eclipse.jetty.security.UserStore;
+import org.eclipse.jetty.security.jaas.JAASLoginService;
 import org.eclipse.jetty.server.Connector;
 import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.server.HttpConfiguration;
diff --git a/engine/src/main/java/org/apache/hop/www/WebServiceServlet.java 
b/engine/src/main/java/org/apache/hop/www/WebServiceServlet.java
index d35fa9ccf8..4807278f0f 100644
--- a/engine/src/main/java/org/apache/hop/www/WebServiceServlet.java
+++ b/engine/src/main/java/org/apache/hop/www/WebServiceServlet.java
@@ -72,7 +72,15 @@ public class WebServiceServlet extends BaseHttpServlet 
implements IHopServerPlug
   @Override
   protected void doPost(HttpServletRequest request, HttpServletResponse 
response)
       throws ServletException, IOException {
-    this.doGet(request, response);
+    try {
+      doGet(request, response);
+    } catch (Exception e) {
+      logError("Error handling web service POST request", e);
+      sendSafeError(
+          response,
+          HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+          "Unable to process web service request.");
+    }
   }
 
   @Override
diff --git a/lib/pom.xml b/lib/pom.xml
index 9770194ddb..935323e301 100644
--- a/lib/pom.xml
+++ b/lib/pom.xml
@@ -95,7 +95,6 @@
         <jackson.version>2.21.1</jackson.version>
         <jakarta.version>3.1.0</jakarta.version>
         <javassist.version>3.30.2-GA</javassist.version>
-        <jetty-jaas.version>11.0.26</jetty-jaas.version>
         <jetty.version>12.1.7</jetty.version>
         <jna.version>5.18.1</jna.version>
         <joda-time.version>2.14.0</joda-time.version>
@@ -105,6 +104,7 @@
         <json-smart.version>2.6.0</json-smart.version>
         <json4s.version>3.7.0-M11</json4s.version>
         <jsoup.version>1.22.1</jsoup.version>
+        <jspecify.version>1.0.0</jspecify.version>
         <juniversalchardet.version>2.5.0</juniversalchardet.version>
         <kafka-avro-serializer.version>7.6.1</kafka-avro-serializer.version>
         <kafka-clients.version>3.9.2</kafka-clients.version>
@@ -674,8 +674,33 @@
             </dependency>
             <dependency>
                 <groupId>org.eclipse.jetty</groupId>
-                <artifactId>jetty-jaas</artifactId>
-                <version>${jetty-jaas.version}</version>
+                <artifactId>jetty-alpn-client</artifactId>
+                <version>${jetty.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.eclipse.jetty</groupId>
+                <artifactId>jetty-client</artifactId>
+                <version>${jetty.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.eclipse.jetty.compression</groupId>
+                <artifactId>jetty-compression-common</artifactId>
+                <version>${jetty.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.eclipse.jetty.compression</groupId>
+                <artifactId>jetty-compression-gzip</artifactId>
+                <version>${jetty.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.eclipse.jetty</groupId>
+                <artifactId>jetty-http</artifactId>
+                <version>${jetty.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.eclipse.jetty</groupId>
+                <artifactId>jetty-io</artifactId>
+                <version>${jetty.version}</version>
             </dependency>
             <dependency>
                 <groupId>org.eclipse.jetty</groupId>
@@ -712,6 +737,11 @@
                 <artifactId>jetty-xml</artifactId>
                 <version>${jetty.version}</version>
             </dependency>
+            <dependency>
+                <groupId>org.jspecify</groupId>
+                <artifactId>jspecify</artifactId>
+                <version>${jspecify.version}</version>
+            </dependency>
             <dependency>
                 <groupId>org.glassfish.tyrus.bundles</groupId>
                 <artifactId>tyrus-standalone-client</artifactId>
diff --git a/plugins/engines/beam/src/assembly/assembly.xml 
b/plugins/engines/beam/src/assembly/assembly.xml
index 147409ab8d..b053fef2b5 100644
--- a/plugins/engines/beam/src/assembly/assembly.xml
+++ b/plugins/engines/beam/src/assembly/assembly.xml
@@ -85,6 +85,7 @@
                 <exclude>org.apache.kafka::jar</exclude>
                 <exclude>org.xerial.snappy:snappy-java:jar</exclude>
                 <exclude>org.eclipse.jetty::jar</exclude>
+                <exclude>org.eclipse.jetty.compression::jar</exclude>
                 <exclude>com.google.cloud:google-cloud-storage:jar</exclude>
                 <exclude>io.grpc::jar</exclude>
                 <exclude>com.google.cloud:google-cloud-core-grpc:jar</exclude>
diff --git 
a/plugins/misc/rest/src/main/java/org/apache/hop/metadata/rest/RestConnection.java
 
b/plugins/misc/rest/src/main/java/org/apache/hop/metadata/rest/RestConnection.java
index 6fa2f65890..08d76a7e2e 100644
--- 
a/plugins/misc/rest/src/main/java/org/apache/hop/metadata/rest/RestConnection.java
+++ 
b/plugins/misc/rest/src/main/java/org/apache/hop/metadata/rest/RestConnection.java
@@ -60,6 +60,8 @@ import 
org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
     documentationUrl = "/metadata-types/rest-connection.html",
     hopMetadataPropertyType = HopMetadataPropertyType.REST_CONNECTION,
     supportsGlobalReplace = true)
+// It is optional to disable SSL/TLS
+@SuppressWarnings({"java:S5527", "java:S4830", "java:S4423"})
 public class RestConnection extends HopMetadataBase implements IHopMetadata {
   public static final String BASIC = "Basic";
   public static final String API_KEY = "API Key";
@@ -241,7 +243,7 @@ public class RestConnection extends HopMetadataBase 
implements IHopMetadata {
     response.close();
   }
 
-  public void disconnect() throws HopException {
+  public void disconnect() {
     client.close();
   }
 
diff --git a/plugins/tech/google/pom.xml b/plugins/tech/google/pom.xml
index 7eabddec53..87ebc665c0 100755
--- a/plugins/tech/google/pom.xml
+++ b/plugins/tech/google/pom.xml
@@ -37,7 +37,6 @@
         
<google-api-drive.version>v3-rev20250216-2.0.0</google-api-drive.version>
         
<google-api-sheets.version>v4-rev20250211-2.0.0</google-api-sheets.version>
         
<google-api-sqladmin.version>v1-rev20230721-2.0.0</google-api-sqladmin.version>
-        <jetty-client.version>12.1.2</jetty-client.version>
     </properties>
 
     <dependencyManagement>
@@ -185,7 +184,6 @@
         <dependency>
             <groupId>org.eclipse.jetty</groupId>
             <artifactId>jetty-client</artifactId>
-            <version>${jetty-client.version}</version>
             <exclusions>
                 <exclusion>
                     <groupId>org.eclipse.jetty.ee11</groupId>
diff --git a/pom.xml b/pom.xml
index bff202c52b..962e2f5b1b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -110,6 +110,7 @@
         <jandex.version>3.5.3</jandex.version>
         <java-hamcrest.version>2.0.0.0</java-hamcrest.version>
         <javadoc.failOnWarnings>false</javadoc.failOnWarnings>
+        <jetty.version>12.1.7</jetty.version>
         <junit.version>6.0.3</junit.version>
         
<license.bundleLicenseFile>META-INF/LICENSE.txt</license.bundleLicenseFile>
         <license.failOnLicenseCheck>false</license.failOnLicenseCheck>
@@ -165,6 +166,39 @@
 
     <dependencyManagement>
         <dependencies>
+            <!-- Jetty: transitive deps (e.g. jersey-jetty-connector) can pull 
older patch lines (12.1.2).
+                 hop-libs already pins Jetty for modules that import it; root 
entries cover the rest of
+                 the reactor without importing hop-libs (would cycle: hop -> 
hop-libs -> parent hop). -->
+            <dependency>
+                <groupId>org.eclipse.jetty</groupId>
+                <artifactId>jetty-alpn-client</artifactId>
+                <version>${jetty.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.eclipse.jetty</groupId>
+                <artifactId>jetty-client</artifactId>
+                <version>${jetty.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.eclipse.jetty.compression</groupId>
+                <artifactId>jetty-compression-common</artifactId>
+                <version>${jetty.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.eclipse.jetty.compression</groupId>
+                <artifactId>jetty-compression-gzip</artifactId>
+                <version>${jetty.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.eclipse.jetty</groupId>
+                <artifactId>jetty-http</artifactId>
+                <version>${jetty.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.eclipse.jetty</groupId>
+                <artifactId>jetty-io</artifactId>
+                <version>${jetty.version}</version>
+            </dependency>
             <dependency>
                 <groupId>com.fasterxml.jackson.core</groupId>
                 <artifactId>jackson-core</artifactId>


Reply via email to