rvesse commented on code in PR #2538:
URL: https://github.com/apache/jena/pull/2538#discussion_r1642376636


##########
jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionPrefixesR.java:
##########
@@ -0,0 +1,156 @@
+/*
+ * 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
+ *
+ *     http://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.
+ */
+
+package org.apache.jena.fuseki.servlets;
+
+import com.google.gson.JsonArray;
+import org.apache.jena.atlas.logging.FmtLog;
+import org.apache.jena.riot.WebContent;
+import org.apache.jena.riot.web.HttpNames;
+
+import org.apache.jena.fuseki.servlets.prefixes.ActionPrefixesBase;
+import org.apache.jena.fuseki.servlets.prefixes.PrefixUtils;
+import org.apache.jena.fuseki.servlets.prefixes.JsonObject;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+public class ActionPrefixesR extends ActionPrefixesBase {
+
+    private static final String NO_PREFIX_NS = "";
+
+    public ActionPrefixesR() {}
+
+    @Override
+    protected void doOptions(HttpAction action) {
+        ActionLib.setCommonHeadersForOptions(action);
+        action.setResponseHeader(HttpNames.hAllow, "GET,OPTIONS");
+        ServletOps.success(action);
+    }
+
+    public void validateGet(HttpAction action) {
+        validate(action);
+        // check if the combination of parameters is legal
+        String prefix = action.getRequestParameter("prefix");
+        String uri = action.getRequestParameter("uri");
+        if(prefix != null && uri != null) {
+            ServletOps.errorBadRequest("Provide only one of the prefix or uri 
parameters!");
+            return;
+        }
+    }
+
+    @Override
+    protected void doGet(HttpAction action) {
+        ActionLib.setCommonHeaders(action);
+        validateGet(action);
+
+        action.beginRead();
+        try {
+            // Not null (valid request)
+            String prefix = action.getRequestParameter("prefix");
+            String uri = action.getRequestParameter("uri");
+
+            if (prefix == null && uri == null) {
+                //getAll
+                Map<String, String> allPairs = prefixes(action).getAll();
+                JsonArray allJsonPairs = new JsonArray();
+                allPairs.entrySet().stream()
+                        .forEach(entry -> {
+                            com.google.gson.JsonObject jsonObject = new 
com.google.gson.JsonObject();
+                            jsonObject.addProperty("prefix", entry.getKey());
+                            jsonObject.addProperty("uri", entry.getValue());
+                            allJsonPairs.add(jsonObject);
+                            FmtLog.info(action.log, "[%d] - %s", action.id, 
new JsonObject(entry.getKey(), entry.getValue()));
+                        });
+                action.setResponseContentType(WebContent.contentTypeJSON);
+                
action.getResponseOutputStream().print(String.valueOf(allJsonPairs));
+
+                ServletOps.success(action);
+                action.endRead();
+                return;
+            }
+            if (prefix != null && uri == null) {

Review Comment:
   Stylistic (and entirely optional): Rather than these if checks which aren't 
particularly clear why not have a `chooseResponseType()` (or similarly named 
method) that does those in a short method returning an `enum` value indicating 
the response type selected and then using a `switch` statement here for clarity?



##########
jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionPrefixesRW.java:
##########
@@ -0,0 +1,141 @@
+/*
+ * 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
+ *
+ *     http://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.
+ */
+
+package org.apache.jena.fuseki.servlets;
+
+import org.apache.jena.atlas.logging.FmtLog;
+import org.apache.jena.riot.WebContent;
+import org.apache.jena.riot.web.HttpNames;
+
+import org.apache.jena.fuseki.servlets.prefixes.PrefixUtils;
+
+import java.io.IOException;
+
+public class ActionPrefixesRW extends ActionPrefixesR {
+
+    public ActionPrefixesRW() {}
+
+    @Override
+    protected void doOptions(HttpAction action) {
+        ActionLib.setCommonHeadersForOptions(action);
+        action.setResponseHeader(HttpNames.hAllow, "GET,OPTIONS,POST");
+        ServletOps.success(action);
+    }
+
+    public void validateDelete(HttpAction action) {
+        validate(action);
+        String prefixToRemove = action.getRequestParameter("removeprefix");
+        if (prefixToRemove == null || prefixToRemove.isEmpty() || 
!PrefixUtils.prefixIsValid(prefixToRemove)) {
+            ServletOps.errorBadRequest("Remove operation unsuccessful!");
+            return;
+        }
+    }
+
+    @Override
+    protected void doDelete(HttpAction action) {
+        validateDelete(action);
+        action.beginWrite();
+
+        try {
+            String prefixToRemove = action.getRequestParameter("removeprefix");

Review Comment:
   Since this is a `DELETE` request would it make more sense to just use the 
same `prefix` parameter as the read endpoints?
   
   The use of HTTP verb already differentiates this request from a read request 
so using a different parameter name seems unnecessary



##########
jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionPrefixesR.java:
##########
@@ -0,0 +1,156 @@
+/*
+ * 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
+ *
+ *     http://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.
+ */
+
+package org.apache.jena.fuseki.servlets;
+
+import com.google.gson.JsonArray;
+import org.apache.jena.atlas.logging.FmtLog;
+import org.apache.jena.riot.WebContent;
+import org.apache.jena.riot.web.HttpNames;
+
+import org.apache.jena.fuseki.servlets.prefixes.ActionPrefixesBase;
+import org.apache.jena.fuseki.servlets.prefixes.PrefixUtils;
+import org.apache.jena.fuseki.servlets.prefixes.JsonObject;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+public class ActionPrefixesR extends ActionPrefixesBase {
+
+    private static final String NO_PREFIX_NS = "";
+
+    public ActionPrefixesR() {}
+
+    @Override
+    protected void doOptions(HttpAction action) {
+        ActionLib.setCommonHeadersForOptions(action);
+        action.setResponseHeader(HttpNames.hAllow, "GET,OPTIONS");
+        ServletOps.success(action);
+    }
+
+    public void validateGet(HttpAction action) {
+        validate(action);
+        // check if the combination of parameters is legal
+        String prefix = action.getRequestParameter("prefix");
+        String uri = action.getRequestParameter("uri");
+        if(prefix != null && uri != null) {
+            ServletOps.errorBadRequest("Provide only one of the prefix or uri 
parameters!");
+            return;
+        }
+    }
+
+    @Override
+    protected void doGet(HttpAction action) {
+        ActionLib.setCommonHeaders(action);
+        validateGet(action);
+
+        action.beginRead();
+        try {
+            // Not null (valid request)
+            String prefix = action.getRequestParameter("prefix");
+            String uri = action.getRequestParameter("uri");
+
+            if (prefix == null && uri == null) {
+                //getAll
+                Map<String, String> allPairs = prefixes(action).getAll();
+                JsonArray allJsonPairs = new JsonArray();
+                allPairs.entrySet().stream()
+                        .forEach(entry -> {
+                            com.google.gson.JsonObject jsonObject = new 
com.google.gson.JsonObject();
+                            jsonObject.addProperty("prefix", entry.getKey());
+                            jsonObject.addProperty("uri", entry.getValue());
+                            allJsonPairs.add(jsonObject);
+                            FmtLog.info(action.log, "[%d] - %s", action.id, 
new JsonObject(entry.getKey(), entry.getValue()));
+                        });
+                action.setResponseContentType(WebContent.contentTypeJSON);
+                
action.getResponseOutputStream().print(String.valueOf(allJsonPairs));
+
+                ServletOps.success(action);
+                action.endRead();
+                return;
+            }
+            if (prefix != null && uri == null) {
+
+                if (prefix.isEmpty()) {

Review Comment:
   @afs Does it make sense to move some of these checks into `validate()` 
method or does it not make much difference from a Fuseki request lifecycle 
perspective?



##########
jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/PrefixesService.java:
##########
@@ -0,0 +1,57 @@
+/*
+ * 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
+ *
+ *     http://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.
+ */
+
+package org.apache.jena.fuseki.servlets;
+
+import org.apache.jena.fuseki.server.Operation;
+import org.apache.jena.fuseki.server.OperationRegistry;
+import org.apache.jena.fuseki.servlets.ActionService;
+
+/** The PrefixesService class registers the PrefixesR and PrefixesRW operations
+ * and actions for the Prefixes Service. The service allows for prefix 
management
+ * on a given dataset provided in the config.ttl file. When making requests
+ * to the API the url can have 3 parameters: prefix, uri, and prefixToRemove,
+ * the combination of which defines the operation performed. The read 
operations
+ * are fetching the prefix by URI, fetching the URI by prefix, and fetching 
all.
+ * They are evoked via HTTP GET. The read-write operations are removing and 
updating
+ * prefix-URI pairs. Those are POST requests. */
+
+public class PrefixesService {
+    public static final Operation operationPrefixesRW;
+    public static final Operation operationPrefixesR;
+
+    private static final ActionService procPrefixR;
+    private static final ActionService procPrefixRW;
+
+    static {
+        operationPrefixesR = 
Operation.alloc("http://telicent.io/fuseki/operation/prefixes-r";,

Review Comment:
   Needs to change to be a Jena controlled namespace



##########
jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/prefixes/ActionProcPrefixes.java:
##########
@@ -0,0 +1,228 @@
+/*
+ * 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
+ *
+ *     http://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.
+ */
+
+package org.apache.jena.fuseki.servlets.prefixes;
+
+import com.google.gson.JsonArray;
+import org.apache.jena.atlas.logging.FmtLog;
+import org.apache.jena.fuseki.servlets.*;
+import org.apache.jena.query.TxnType;
+import org.apache.jena.riot.WebContent;
+import org.apache.jena.sparql.core.Transactional;
+
+import java.io.IOException;
+import java.util.*;
+
+
+//validate and unpack the HTTP Get request,
+//call the storage's fetch URI by the prefix function from the HTTP request
+
+public class ActionProcPrefixes extends BaseActionREST {
+
+    private final PrefixesAccess storage;
+
+    // The response to a prefix lookup when there is no such prefix.
+    // This value is not a proper namespace because it is not an absolute URI.
+    private static final String NO_PREFIX_NS = "";
+
+    public ActionProcPrefixes(PrefixesAccess storage) {
+        this.storage = storage;
+    }
+
+    public void validateGet (HttpAction action) {
+
+        // sanitize parameters
+        Iterator<String> paramNames = 
action.getRequestParameterNames().asIterator();
+        while(paramNames.hasNext()) {
+            String check = paramNames.next();
+            if(!check.equals("prefix") && !check.equals("uri") && 
!check.equals("removeprefix")) {
+                ServletOps.errorBadRequest("Unrecognized parameter");
+                return;
+            }
+        }
+        // check if the combination of parameters is legal
+        String prefix = action.getRequestParameter("prefix");
+        String uri = action.getRequestParameter("uri");
+        if(prefix != null && uri != null) {
+            ServletOps.errorBadRequest("Provide only one of the prefix or uri 
parameters!");
+            return;
+        }
+    }
+
+    public void validatePost (HttpAction action) {
+        String prefix = action.getRequestParameter("prefix");
+        String uri = action.getRequestParameter("uri");
+        String prefixToRemove = action.getRequestParameter("removeprefix");
+
+        if (prefix.isEmpty() && uri.isEmpty() && prefixToRemove.isEmpty()) {
+            ServletOps.errorBadRequest("Empty operation - unsuccessful!");
+            return;
+        }
+        if (prefixToRemove.isEmpty()) {
+            if (prefix.isEmpty() || uri.isEmpty() || 
!PrefixUtils.prefixIsValid(prefix) || !PrefixUtils.uriIsValid(uri)) {
+                ServletOps.errorBadRequest("Update operation unsuccessful!");
+                return;
+            }
+        }
+        if (prefix.isEmpty() && uri.isEmpty()) {
+            if (!PrefixUtils.prefixIsValid(prefixToRemove)) {
+                ServletOps.errorBadRequest("Remove operation unsuccessful!");
+                return;
+            }
+        }
+    }
+
+    @Override
+    public void doGet (HttpAction action) {
+
+        validateGet(action);
+
+        Transactional transactional = storage.transactional();
+        transactional.begin(TxnType.READ);
+
+        try {
+            // Not null (valid request)
+            String prefix = action.getRequestParameter("prefix");
+            String uri = action.getRequestParameter("uri");
+
+            if (prefix == null && uri == null) {
+                //getAll
+                Map<String, String> allPairs = storage.getAll();
+                JsonArray allJsonPairs = new JsonArray();
+                allPairs.entrySet().stream()
+                        .forEach(entry -> {
+                            com.google.gson.JsonObject jsonObject = new 
com.google.gson.JsonObject();
+                            jsonObject.addProperty("prefix", entry.getKey());
+                            jsonObject.addProperty("uri", entry.getValue());
+                            allJsonPairs.add(jsonObject);
+                            FmtLog.info(action.log, "[%d] - %s", action.id, 
new JsonObject(entry.getKey(), entry.getValue()));
+                        });
+                action.setResponseContentType(WebContent.contentTypeJSON);
+                
action.getResponseOutputStream().print(String.valueOf(allJsonPairs));
+
+                transactional.commit();
+                ServletOps.success(action);
+                transactional.end();
+                return;
+            }
+            if (prefix != null && uri == null) {
+
+                if (prefix.isEmpty()) {
+                    ServletOps.errorBadRequest("Empty prefix!");
+                    return;
+                }
+                else if (!PrefixUtils.prefixIsValid(prefix)) {
+                    ServletOps.errorBadRequest("Prefix contains illegal 
characters!");
+                    return;
+                }
+                else {
+                    //fetchURI
+                    Optional<String> x = storage.fetchURI(prefix);
+                    String namespace = x.orElse(NO_PREFIX_NS);
+
+                    JsonObject jsonObject = new JsonObject(prefix, namespace);
+
+                    // Build the response.
+                    action.setResponseContentType(WebContent.contentTypeJSON);
+                    action.getResponseOutputStream().print(namespace);
+                    // Indicate success
+                    FmtLog.info(action.log, "[%d] %s -> %s", action.id, 
prefix, jsonObject.toString());
+                    transactional.commit();
+                    ServletOps.success(action);
+                    return;
+                }
+            }
+            if (prefix == null && uri != null) {
+                if (uri.isEmpty()) {
+                    ServletOps.errorBadRequest("Empty URI!");
+                    return;
+                }
+                else if (!PrefixUtils.uriIsValid(uri)) {
+                    ServletOps.errorBadRequest("URI contains illegal 
characters!");
+                    return;
+                }
+                else {
+                    //fetchPrefix
+                    List<String> prefixList =storage.fetchPrefix(uri);
+                    JsonArray prefixJsonArray = new JsonArray();
+                    for (String p : prefixList) {
+                        com.google.gson.JsonObject jsonObject2 = new 
com.google.gson.JsonObject();
+                        jsonObject2.addProperty("prefix", p);
+                        jsonObject2.addProperty("uri", uri);
+                        prefixJsonArray.add(jsonObject2);
+                        FmtLog.info(action.log, "[%d] - %s", action.id, new 
JsonObject(p, uri));
+                    }
+                    // Build the response.
+                    action.setResponseContentType(WebContent.contentTypeJSON);
+                    
action.getResponseOutputStream().print(String.valueOf(prefixJsonArray));
+                    // Indicate success
+                    transactional.commit();
+                    ServletOps.success(action);
+                    transactional.end();
+                    return;
+                }
+            }
+
+        } catch (RuntimeException | IOException ex) {
+            try { transactional.abort(); }
+            catch (Throwable th ) {
+                FmtLog.warn(action.log, th, "[%d] GET prefix = %s", action.id);
+            }
+            ServletOps.errorOccurred(ex);
+        } finally {
+            transactional.end();
+        }
+    }
+
+    @Override
+    public void doPost (HttpAction action) {
+        validatePost(action);
+        Transactional transactional = storage.transactional();
+
+        transactional.begin(TxnType.WRITE);
+        try {
+            String prefix = action.getRequestParameter("prefix");
+            String uri = action.getRequestParameter("uri");
+
+            if(prefix.isEmpty()) {
+                String prefixToRemove = 
action.getRequestParameter("removeprefix");

Review Comment:
   Again same comment about overloading `POST` to be both update and delete 
while also having a separate `DELETE` operation, would be cleaner to keep 
`POST` as update only



##########
jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/prefixes/PrefixesRDF.java:
##########
@@ -0,0 +1,220 @@
+/*
+ * 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
+ *
+ *     http://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.
+ */
+
+package org.apache.jena.fuseki.servlets.prefixes;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.jena.query.*;
+import org.apache.jena.sparql.core.DatasetGraph;
+import org.apache.jena.sparql.core.Transactional;
+import org.apache.jena.sparql.exec.QueryExec;
+import org.apache.jena.sparql.exec.RowSet;
+import org.apache.jena.sparql.exec.UpdateExec;
+import org.apache.jena.tdb2.DatabaseMgr;
+
+/**
+ * The prefix-URI mappings are represented in blank nodes of type Prefix, with 
prefixName and prefixURI attributes.
+ * The prefixName and prefixURI of the same node are a distinct prefix-URI 
mapping.
+ *
+ * PREFIX prefixes: <http://jena.apache.org/prefixes#>
+ * PREFIX rdf:      <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
+ * PREFIX xsd:      <http://www.w3.org/2001/XMLSchema#>
+ *
+ * [] rdf:type prefixes:Prefix ;
+ *    prefixes:prefixName  "test" ;
+ *    prefixes:prefixURI   "http://example/testPrefix#"^^xsd:anyURI
+ *    .
+ *
+ * [ rdf:type prefixes:Prefix ;
+ *   prefixes:prefixName   "ns" ;
+ *   prefixes:prefixURI    "http://example/namespace#"^^xsd:anyURI
+ * ]
+ */
+
+public class PrefixesRDF implements PrefixesAccess {
+
+    DatasetGraph dataset = DatabaseMgr.createDatasetGraph();
+    public void setDataset() {
+
+        dataset.begin(ReadWrite.WRITE) ;
+        try {
+
+            String update1 = """
+                   PREFIX prefixes: <http://jena.apache.org/prefixes#>
+                   PREFIX rdf:      
<http://www.w3.org/1999/02/22-rdf-syntax-ns#>
+                   PREFIX xsd:      <http://www.w3.org/2001/XMLSchema#>
+                   
+                   INSERT DATA {
+                         [] rdf:type prefixes:Prefix ;
+                            prefixes:prefixName  "prefix1" ;
+                            prefixes:prefixURI   
"http://www.localhost.org/uri1#"^^xsd:anyURI
+                   }""";
+            UpdateExec.dataset(dataset).update(update1).execute();
+
+            String update2 = """
+                    PREFIX prefixes: <http://jena.apache.org/prefixes#>
+                    PREFIX rdf:      
<http://www.w3.org/1999/02/22-rdf-syntax-ns#>
+                    PREFIX xsd:      <http://www.w3.org/2001/XMLSchema#>
+                    
+                    INSERT DATA {
+                          [] rdf:type prefixes:Prefix ;
+                             prefixes:prefixName  "prefix2" ;
+                             prefixes:prefixURI   
"http://www.localhost.org/uri2#"^^xsd:anyURI
+                    }""";
+            UpdateExec.dataset(dataset).update(update2).execute();
+
+            dataset.commit();
+        } catch (Throwable th) {
+            dataset.abort();
+            throw(th);
+        } finally {
+            dataset.end() ;
+        }
+
+    }
+
+    public Transactional transactional() { return dataset; }
+
+    @Override
+    public Optional<String> fetchURI(String prefix) {
+
+        String query = "PREFIX prefixes: <http://jena.apache.org/prefixes#> " +
+                "SELECT ?prefixURI WHERE { " +
+                "?X prefixes:prefixName \"" + prefix + "\" . " +
+                "?X prefixes:prefixURI ?prefixURI " +
+                "}";
+
+        try (QueryExec qExec = 
QueryExec.dataset(dataset).query(query).build()) {
+
+            RowSet rowSet = qExec.select();
+            List<String> x = new ArrayList<String>();
+
+            rowSet.forEachRemaining(row -> {
+                String prefixNameLiteral = 
row.get("prefixURI").getLiteralLexicalForm();
+                if (prefixNameLiteral != null) {
+                    x.add(prefixNameLiteral);
+                }
+            });
+            if (x.isEmpty())
+                return Optional.empty();
+            //originally x.getFirst()
+            return Optional.ofNullable(x.get(0));
+        }
+    }
+
+    @Override
+    public void updatePrefix(String prefix, String uri) {
+
+        String query =
+                "PREFIX prefixes: <http://jena.apache.org/prefixes#>\n" +
+                "PREFIX xsd:      <http://www.w3.org/2001/XMLSchema#>\n" +
+                "ASK WHERE {  ?X prefixes:prefixName  \"" + prefix + "\" ; \n" 
+
+                "                prefixes:prefixURI   ?prefixURI . }";
+        try (QueryExec qExec = 
QueryExec.dataset(dataset).query(query).build()) {
+
+            AtomicBoolean result = new AtomicBoolean(false);
+            dataset.executeRead(()->{
+                result.set(qExec.ask());
+            });
+
+            String update;
+            if (result.get())
+                update =
+                        "PREFIX prefixes: 
<http://jena.apache.org/prefixes#>\n" +

Review Comment:
   Stylistic: Maybe use multi-line strings to make the queries and updates more 
readable in the code?



##########
jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/prefixes/TestPrefixesService.java:
##########
@@ -0,0 +1,584 @@
+/*
+ * 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
+ *
+ *     http://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.
+ */
+
+package org.apache.jena.fuseki.main.prefixes;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParser;
+import com.google.gson.JsonSyntaxException;
+import org.apache.jena.atlas.web.HttpException;
+import org.apache.jena.fuseki.main.FusekiServer;
+import org.apache.jena.fuseki.servlets.PrefixesService;
+import org.apache.jena.sparql.exec.http.Params;
+import org.apache.jena.web.HttpSC;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import static org.apache.jena.http.HttpOp.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class TestPrefixesService {

Review Comment:
   This appears to be a copy paste from `TestAbstractPrefixParam` with the only 
difference being the Fuseki setup is configuration rather than programmatically 
driven.
   
   Could it not extend that class and simply `@Override` the `before()` and 
`afterSuite()` methods?



##########
jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/prefixes/PrefixesAccess.java:
##########
@@ -0,0 +1,42 @@
+/*
+ * 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
+ *
+ *     http://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.
+ */
+
+package org.apache.jena.fuseki.servlets.prefixes;
+
+
+import org.apache.jena.sparql.core.Transactional;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+//interface for interacting with the storages
+public interface PrefixesAccess {
+
+    Optional<String> fetchURI(String prefix);
+
+    Transactional transactional();
+
+    void updatePrefix(String prefix, String uri);
+    void removePrefix(String prefixToRemove);
+
+    Map<String, String> getAll();
+
+    /** Fetches the prefixes assigned o the provided URI. There can be 
multiple in the JsonArray.**/

Review Comment:
   ```suggestion
       /** Fetches the prefixes assigned to the provided URI. There can be 
multiple in the list**/
   ```



##########
jena-fuseki2/examples/config-prefixes.ttl:
##########
@@ -0,0 +1,24 @@
+## Licensed under the terms of http://www.apache.org/licenses/LICENSE-2.0
+
+PREFIX example:  <http://example.org/fuseki/>
+PREFIX fuseki:  <http://jena.apache.org/fuseki#>
+PREFIX rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
+PREFIX :        <http://telicent.io/config#>
+PREFIX ja:      <http://jena.hpl.hp.com/2005/11/Assembler#>
+PREFIX tdb2:    <http://jena.apache.org/2016/tdb#>
+
+PREFIX ex: <http://example.org/fuseki/operation/>

Review Comment:
   This namespace does not appear to match that actually used later in the code 
so unclear this example is actually usable?



##########
jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionPrefixesR.java:
##########
@@ -0,0 +1,156 @@
+/*
+ * 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
+ *
+ *     http://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.
+ */
+
+package org.apache.jena.fuseki.servlets;
+
+import com.google.gson.JsonArray;
+import org.apache.jena.atlas.logging.FmtLog;
+import org.apache.jena.riot.WebContent;
+import org.apache.jena.riot.web.HttpNames;
+
+import org.apache.jena.fuseki.servlets.prefixes.ActionPrefixesBase;
+import org.apache.jena.fuseki.servlets.prefixes.PrefixUtils;
+import org.apache.jena.fuseki.servlets.prefixes.JsonObject;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+public class ActionPrefixesR extends ActionPrefixesBase {
+
+    private static final String NO_PREFIX_NS = "";
+
+    public ActionPrefixesR() {}
+
+    @Override
+    protected void doOptions(HttpAction action) {
+        ActionLib.setCommonHeadersForOptions(action);
+        action.setResponseHeader(HttpNames.hAllow, "GET,OPTIONS");
+        ServletOps.success(action);
+    }
+
+    public void validateGet(HttpAction action) {
+        validate(action);
+        // check if the combination of parameters is legal
+        String prefix = action.getRequestParameter("prefix");

Review Comment:
   Can we have all the parameter and JSON field names defined as constants 
somewhere please?



##########
jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/prefixes/PrefixesRDF.java:
##########
@@ -0,0 +1,220 @@
+/*
+ * 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
+ *
+ *     http://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.
+ */
+
+package org.apache.jena.fuseki.servlets.prefixes;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.jena.query.*;
+import org.apache.jena.sparql.core.DatasetGraph;
+import org.apache.jena.sparql.core.Transactional;
+import org.apache.jena.sparql.exec.QueryExec;
+import org.apache.jena.sparql.exec.RowSet;
+import org.apache.jena.sparql.exec.UpdateExec;
+import org.apache.jena.tdb2.DatabaseMgr;
+
+/**
+ * The prefix-URI mappings are represented in blank nodes of type Prefix, with 
prefixName and prefixURI attributes.
+ * The prefixName and prefixURI of the same node are a distinct prefix-URI 
mapping.
+ *
+ * PREFIX prefixes: <http://jena.apache.org/prefixes#>
+ * PREFIX rdf:      <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
+ * PREFIX xsd:      <http://www.w3.org/2001/XMLSchema#>
+ *
+ * [] rdf:type prefixes:Prefix ;
+ *    prefixes:prefixName  "test" ;
+ *    prefixes:prefixURI   "http://example/testPrefix#"^^xsd:anyURI
+ *    .
+ *
+ * [ rdf:type prefixes:Prefix ;
+ *   prefixes:prefixName   "ns" ;
+ *   prefixes:prefixURI    "http://example/namespace#"^^xsd:anyURI
+ * ]
+ */
+
+public class PrefixesRDF implements PrefixesAccess {
+
+    DatasetGraph dataset = DatabaseMgr.createDatasetGraph();
+    public void setDataset() {
+
+        dataset.begin(ReadWrite.WRITE) ;
+        try {
+
+            String update1 = """
+                   PREFIX prefixes: <http://jena.apache.org/prefixes#>
+                   PREFIX rdf:      
<http://www.w3.org/1999/02/22-rdf-syntax-ns#>
+                   PREFIX xsd:      <http://www.w3.org/2001/XMLSchema#>
+                   
+                   INSERT DATA {
+                         [] rdf:type prefixes:Prefix ;
+                            prefixes:prefixName  "prefix1" ;
+                            prefixes:prefixURI   
"http://www.localhost.org/uri1#"^^xsd:anyURI
+                   }""";
+            UpdateExec.dataset(dataset).update(update1).execute();
+
+            String update2 = """
+                    PREFIX prefixes: <http://jena.apache.org/prefixes#>
+                    PREFIX rdf:      
<http://www.w3.org/1999/02/22-rdf-syntax-ns#>
+                    PREFIX xsd:      <http://www.w3.org/2001/XMLSchema#>
+                    
+                    INSERT DATA {
+                          [] rdf:type prefixes:Prefix ;
+                             prefixes:prefixName  "prefix2" ;
+                             prefixes:prefixURI   
"http://www.localhost.org/uri2#"^^xsd:anyURI
+                    }""";
+            UpdateExec.dataset(dataset).update(update2).execute();
+
+            dataset.commit();
+        } catch (Throwable th) {
+            dataset.abort();
+            throw(th);
+        } finally {
+            dataset.end() ;
+        }
+
+    }
+
+    public Transactional transactional() { return dataset; }
+
+    @Override
+    public Optional<String> fetchURI(String prefix) {
+
+        String query = "PREFIX prefixes: <http://jena.apache.org/prefixes#> " +

Review Comment:
   Could be better to use a parameterised string here i.e. 
https://jena.apache.org/documentation/query/parameterized-sparql-strings.html



##########
jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/prefixes/PrefixesRDF.java:
##########
@@ -0,0 +1,220 @@
+/*
+ * 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
+ *
+ *     http://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.
+ */
+
+package org.apache.jena.fuseki.servlets.prefixes;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.jena.query.*;
+import org.apache.jena.sparql.core.DatasetGraph;
+import org.apache.jena.sparql.core.Transactional;
+import org.apache.jena.sparql.exec.QueryExec;
+import org.apache.jena.sparql.exec.RowSet;
+import org.apache.jena.sparql.exec.UpdateExec;
+import org.apache.jena.tdb2.DatabaseMgr;
+
+/**
+ * The prefix-URI mappings are represented in blank nodes of type Prefix, with 
prefixName and prefixURI attributes.
+ * The prefixName and prefixURI of the same node are a distinct prefix-URI 
mapping.
+ *
+ * PREFIX prefixes: <http://jena.apache.org/prefixes#>
+ * PREFIX rdf:      <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
+ * PREFIX xsd:      <http://www.w3.org/2001/XMLSchema#>
+ *
+ * [] rdf:type prefixes:Prefix ;
+ *    prefixes:prefixName  "test" ;
+ *    prefixes:prefixURI   "http://example/testPrefix#"^^xsd:anyURI
+ *    .
+ *
+ * [ rdf:type prefixes:Prefix ;
+ *   prefixes:prefixName   "ns" ;
+ *   prefixes:prefixURI    "http://example/namespace#"^^xsd:anyURI
+ * ]
+ */
+
+public class PrefixesRDF implements PrefixesAccess {
+
+    DatasetGraph dataset = DatabaseMgr.createDatasetGraph();
+    public void setDataset() {
+
+        dataset.begin(ReadWrite.WRITE) ;
+        try {

Review Comment:
   Is this method leftover debugging code?



##########
jena-ontapi/src/main/java/org/apache/jena/ontapi/OntModelFactory.java:
##########


Review Comment:
   Unrelated changes from a merge conflict resolution?



##########
jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionPrefixesRW.java:
##########
@@ -0,0 +1,141 @@
+/*
+ * 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
+ *
+ *     http://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.
+ */
+
+package org.apache.jena.fuseki.servlets;
+
+import org.apache.jena.atlas.logging.FmtLog;
+import org.apache.jena.riot.WebContent;
+import org.apache.jena.riot.web.HttpNames;
+
+import org.apache.jena.fuseki.servlets.prefixes.PrefixUtils;
+
+import java.io.IOException;
+
+public class ActionPrefixesRW extends ActionPrefixesR {
+
+    public ActionPrefixesRW() {}
+
+    @Override
+    protected void doOptions(HttpAction action) {
+        ActionLib.setCommonHeadersForOptions(action);
+        action.setResponseHeader(HttpNames.hAllow, "GET,OPTIONS,POST");
+        ServletOps.success(action);
+    }
+
+    public void validateDelete(HttpAction action) {
+        validate(action);
+        String prefixToRemove = action.getRequestParameter("removeprefix");
+        if (prefixToRemove == null || prefixToRemove.isEmpty() || 
!PrefixUtils.prefixIsValid(prefixToRemove)) {
+            ServletOps.errorBadRequest("Remove operation unsuccessful!");
+            return;
+        }
+    }
+
+    @Override
+    protected void doDelete(HttpAction action) {
+        validateDelete(action);
+        action.beginWrite();
+
+        try {
+            String prefixToRemove = action.getRequestParameter("removeprefix");
+            prefixes(action).removePrefix(prefixToRemove);
+            FmtLog.info(action.log, "[%d] Remove %s:", action.id, 
prefixToRemove);
+
+            action.commit();
+
+            // Indicate success
+            ServletOps.success(action);
+
+            // Build the response.
+            action.setResponseContentType(WebContent.contentTypeJSON);
+            action.getResponseOutputStream().print("");
+
+        } catch (RuntimeException | IOException ex) {
+            try { action.abort(); }
+            catch (Throwable th ) {
+                FmtLog.warn(action.log, th, "[%d] POST prefix = %s", 
action.id);
+            }
+            ServletOps.errorOccurred(ex);
+        } finally {
+            action.end();
+        }
+    }
+
+
+    public void validatePost(HttpAction action) {
+        validate(action);
+        String prefix = action.getRequestParameter("prefix");
+        String uri = action.getRequestParameter("uri");
+        String prefixToRemove = action.getRequestParameter("removeprefix");

Review Comment:
   So I guess this is why `removeprefix` is a separate parameter because we 
have `POST` allowing for both update and delete, might be cleaner to just limit 
`POST` to updating a prefix and keep `DELETE` for deleting a prefix



##########
jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/prefixes/ActionProcPrefixes.java:
##########
@@ -0,0 +1,228 @@
+/*
+ * 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
+ *
+ *     http://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.
+ */
+
+package org.apache.jena.fuseki.servlets.prefixes;
+
+import com.google.gson.JsonArray;
+import org.apache.jena.atlas.logging.FmtLog;
+import org.apache.jena.fuseki.servlets.*;
+import org.apache.jena.query.TxnType;
+import org.apache.jena.riot.WebContent;
+import org.apache.jena.sparql.core.Transactional;
+
+import java.io.IOException;
+import java.util.*;
+
+
+//validate and unpack the HTTP Get request,
+//call the storage's fetch URI by the prefix function from the HTTP request
+
+public class ActionProcPrefixes extends BaseActionREST {

Review Comment:
   There seems to be two different implementations of the same thing - this and 
the `ActionPrefixesR`/`ActionPrefixedRW` combination - can some/all of the 
logic be shared between these to reduce the duplication?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to