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

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

commit 8035c616801abf58b8c4283d56422e3be765fdda
Author: Andy Seaborne <[email protected]>
AuthorDate: Fri Apr 3 11:20:29 2026 +0100

    GH-3831: Handle UpdateException as 'bad request'
---
 .../jena/sparql/lang/arq/ParserARQUpdate.java      | 10 +--
 .../jena/fuseki/servlets/SPARQLProtocol.java       |  6 +-
 .../apache/jena/fuseki/servlets/SPARQL_Update.java | 18 ++---
 .../org/apache/jena/fuseki/main/TS_FusekiMain.java |  3 +-
 .../org/apache/jena/fuseki/main/TestUpdate.java    | 79 ++++++++++++++++++++++
 5 files changed, 92 insertions(+), 24 deletions(-)

diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/lang/arq/ParserARQUpdate.java 
b/jena-arq/src/main/java/org/apache/jena/sparql/lang/arq/ParserARQUpdate.java
index 649f70fad4..c735e44a4c 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/sparql/lang/arq/ParserARQUpdate.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/sparql/lang/arq/ParserARQUpdate.java
@@ -26,11 +26,11 @@ import java.io.Reader;
 import org.apache.jena.atlas.logging.Log;
 import org.apache.jena.query.QueryException;
 import org.apache.jena.query.QueryParseException;
-import org.apache.jena.shared.JenaException;
 import org.apache.jena.sparql.core.Prologue;
 import org.apache.jena.sparql.lang.UpdateParser;
 import org.apache.jena.sparql.lang.arq.javacc.ARQParser;
 import org.apache.jena.sparql.modify.UpdateSink;
+import org.apache.jena.update.UpdateException;
 
 public class ParserARQUpdate extends UpdateParser {
     public ParserARQUpdate() {}
@@ -50,18 +50,14 @@ public class ParserARQUpdate extends UpdateParser {
             int col = parser.token.endColumn;
             int line = parser.token.endLine;
             throw new QueryParseException(tErr.getMessage(), line, col);
-        }
-
-        catch (QueryException ex) {
+        } catch (QueryException | UpdateException ex) {
             throw ex;
-        } catch (JenaException ex) {
-            throw new QueryException(ex.getMessage(), ex);
         } catch (Error err) {
             // The token stream can throw errors.
             throw new QueryParseException(err.getMessage(), err, -1, -1);
         } catch (Throwable th) {
             Log.error(this, "Unexpected throwable: ", th);
-            throw new QueryException(th.getMessage(), th);
+            throw new UpdateException(th.getMessage(), th);
         }
     }
 }
diff --git 
a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQLProtocol.java
 
b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQLProtocol.java
index d95c87c2d2..3c971523d8 100644
--- 
a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQLProtocol.java
+++ 
b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQLProtocol.java
@@ -30,11 +30,9 @@ import java.util.List;
 import java.util.function.Predicate;
 
 import jakarta.servlet.http.HttpServletRequest;
-
 import org.apache.jena.atlas.iterator.Iter;
 import org.apache.jena.atlas.lib.Lib;
 import org.apache.jena.query.Query;
-import org.apache.jena.query.QueryException;
 import org.apache.jena.query.QueryParseException;
 import org.apache.jena.sparql.core.DatasetDescription;
 
@@ -44,9 +42,9 @@ import org.apache.jena.sparql.core.DatasetDescription;
 public class SPARQLProtocol {
 
     /**
-     * Form a message from a {@link QueryException}.
+     * Form a message from an {@link Exception}.
      */
-    public static String messageForException(QueryException ex) {
+    public static String messageForException(Exception ex) {
         if ( ex.getMessage() != null )
             return ex.getMessage();
         if ( ex.getCause() != null )
diff --git 
a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Update.java
 
b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Update.java
index be1dc1b8b2..ddf594a36c 100644
--- 
a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Update.java
+++ 
b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Update.java
@@ -42,7 +42,6 @@ import java.util.Enumeration;
 import java.util.List;
 
 import jakarta.servlet.http.HttpServletRequest;
-
 import org.apache.jena.atlas.io.IO;
 import org.apache.jena.atlas.lib.Bytes;
 import org.apache.jena.atlas.lib.StrUtils;
@@ -52,13 +51,12 @@ import org.apache.jena.graph.Node;
 import org.apache.jena.graph.NodeFactory;
 import org.apache.jena.irix.IRIx;
 import org.apache.jena.irix.IRIxResolver;
-import org.apache.jena.query.QueryBuildException;
+import org.apache.jena.query.QueryException;
 import org.apache.jena.query.QueryParseException;
 import org.apache.jena.query.Syntax;
 import org.apache.jena.riot.WebContent;
 import org.apache.jena.riot.web.HttpNames;
 import org.apache.jena.shared.OperationDeniedException;
-import org.apache.jena.sparql.engine.http.QueryExceptionHTTP;
 import org.apache.jena.sparql.modify.UsingList;
 import org.apache.jena.update.UpdateAction;
 import org.apache.jena.update.UpdateException;
@@ -230,24 +228,20 @@ public class SPARQL_Update extends ActionService
             else
                 UpdateAction.execute(req, action.getActiveDSG());
             action.commit();
-        } catch (UpdateException ex) {
-            ActionLib.consumeBody(action);
-            abortSilent(action);
-            incCounter(action.getEndpoint().getCounters(), UpdateExecErrors);
-            ServletOps.errorOccurred(ex.getMessage());
         } catch (QueryParseException ex) {
             ActionLib.consumeBody(action);
             abortSilent(action);
+            incCounter(action.getEndpoint().getCounters(), UpdateExecErrors);
             String msg = messageForParseException(ex);
             action.log.warn(format("[%d] Parse error: %s", action.id, msg));
-            ServletOps.errorBadRequest(messageForException(ex));
-        } catch (QueryBuildException|QueryExceptionHTTP ex) {
+            ServletOps.errorBadRequest(msg);
+        } catch (UpdateException | QueryException ex) {
             ActionLib.consumeBody(action);
             abortSilent(action);
-            // Counter inc'ed further out.
+            incCounter(action.getEndpoint().getCounters(), UpdateExecErrors);
             String msg = messageForException(ex);
             action.log.warn(format("[%d] Bad request: %s", action.id, msg));
-            ServletOps.errorBadRequest(messageForException(ex));
+            ServletOps.errorBadRequest(msg);
         } catch (OperationDeniedException ex) {
             ActionLib.consumeBody(action);
             abortSilent(action);
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TS_FusekiMain.java
 
b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TS_FusekiMain.java
index 443b8b9f81..09da890cda 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TS_FusekiMain.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TS_FusekiMain.java
@@ -56,6 +56,7 @@ import org.apache.jena.fuseki.main.sys.TestFusekiModules;
   , TestHttpOptions.class
   , TestQuery.class
   , TestSPARQLProtocol.class
+  , TestUpdate.class
 
   , TestPatchFuseki.class
   , TestFusekiCustomScriptFunc.class
@@ -63,7 +64,7 @@ import org.apache.jena.fuseki.main.sys.TestFusekiModules;
   , TS_PrefixesService.class
   , TestMetrics.class
   , TestFusekiShaclValidation.class
-  
+
   // Temporary independent test due for tarcking failures.
   , TestFusekiShaclValidation2.class
 
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestUpdate.java
 
b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestUpdate.java
new file mode 100644
index 0000000000..88f67f6219
--- /dev/null
+++ 
b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestUpdate.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *   SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.apache.jena.fuseki.main;
+
+import org.junit.jupiter.api.Test;
+
+import org.apache.jena.rdflink.RDFLink;
+import org.apache.jena.sparql.core.DatasetGraph;
+import org.apache.jena.sparql.core.DatasetGraphFactory;
+import org.apache.jena.sparql.exec.UpdateExec;
+
+public class TestUpdate {
+/*
+curl -v -XPOST 
'http://localhost:3030/'"${DS}"'/?using-named-graph-uri=http%3A%2F%2Fexample%2Fpeople'
 \
+     -H 'Content-type: application/sparql-update' \
+     --data-binary 'WITH <http://example/addresses> DELETE { ?person ?p  
"Bill" } WHERE {}'
+ */
+
+    private static String PREFIXES = """
+            PREFIX : <http://example/>
+            """;
+    private static String DS = "/updateTest";
+
+    private FusekiServer server() {
+        DatasetGraph dsgTesting = DatasetGraphFactory.createTxnMem();
+        FusekiServer server = FusekiServer.create()
+                .port(0)
+                //.verbose(true)
+                .add(DS, dsgTesting)
+                .enablePing(true)
+                .enableMetrics(true)
+                .start();
+        return server;
+
+    }
+
+    @Test public void update2() {
+        FusekiServer server = server();
+        String serviceURL = server.datasetURL(DS);
+        try ( RDFLink link = RDFLink.connect(serviceURL) ) {
+            link.update(PREFIXES+"INSERT DATA { :s :p :o }");
+        }
+    }
+
+
+    @Test public void update1() {
+        FusekiServer server = server();
+        String serviceURL = server.datasetURL(DS);
+        UpdateExec.service(serviceURL).update(PREFIXES+"INSERT DATA { :s :p :o 
}").execute();
+    }
+
+    @Test public void updateError1() {
+        FusekiServer server = server();
+        String serviceURL = server.datasetURL(DS);
+        String URL = serviceURL+"/?using-named-graph-uri=http://example/ng1";;
+        FusekiTestLib.expect400(()->
+            UpdateExec.service(URL).update(PREFIXES+" WITH  
<http://example/ng2> INSERT { :s :p :o } WHERE {}").execute()
+        );
+    }
+}

Reply via email to