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() + ); + } +}
