From 98060ad0618a680e222065ef8455e7698284e6b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?= Date: Fri, 23 Jan 2026 15:10:26 +0100 Subject: [PATCH 1/8] Ignored paths in `OntologyFilter` --- .../server/filter/request/OntologyFilter.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/main/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilter.java b/src/main/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilter.java index c996d5214..0390a989b 100644 --- a/src/main/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilter.java +++ b/src/main/java/com/atomgraph/linkeddatahub/server/filter/request/OntologyFilter.java @@ -54,12 +54,43 @@ public class OntologyFilter implements ContainerRequestFilter private static final Logger log = LoggerFactory.getLogger(OntologyFilter.class); + /** + * Paths that should not trigger ontology loading to avoid circular dependencies. + * + * When an ontology contains owl:imports pointing to URIs within these paths, + * loading the ontology would trigger HTTP requests to those URIs. If those requests + * are intercepted by this filter, it creates a circular dependency: + * + * 1. Request arrives for /uploads/xyz + * 2. OntologyFilter intercepts it and loads ontology + * 3. Ontology has owl:imports for /uploads/xyz + * 4. Jena FileManager makes HTTP request to /uploads/xyz + * 5. OntologyFilter intercepts it again → infinite loop/deadlock + * + * Additionally, uploaded files are binary/RDF content that don't require + * ontology context for their serving logic. + */ + private static final java.util.Set IGNORED_PATH_PREFIXES = java.util.Set.of( + "uploads/" + ); + @Inject com.atomgraph.linkeddatahub.Application system; @Override public void filter(ContainerRequestContext crc) throws IOException { + String path = crc.getUriInfo().getPath(); + + // Skip ontology loading for paths that may be referenced in owl:imports + // to prevent circular dependency deadlocks during ontology resolution + if (IGNORED_PATH_PREFIXES.stream().anyMatch(path::startsWith)) + { + if (log.isTraceEnabled()) log.trace("Skipping ontology loading for path: {}", path); + crc.setProperty(OWL.Ontology.getURI(), Optional.empty()); + return; + } + crc.setProperty(OWL.Ontology.getURI(), getOntology(crc)); } From 3edb376996e2e5655a42ab2e8692b05532d6a649 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?= Date: Sat, 24 Jan 2026 12:57:28 +0100 Subject: [PATCH 2/8] Debug test --- .../ontology-import-upload-no-deadlock.sh | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/http-tests/admin/model/ontology-import-upload-no-deadlock.sh b/http-tests/admin/model/ontology-import-upload-no-deadlock.sh index af69d213f..2b8e333a9 100755 --- a/http-tests/admin/model/ontology-import-upload-no-deadlock.sh +++ b/http-tests/admin/model/ontology-import-upload-no-deadlock.sh @@ -86,14 +86,19 @@ echo "Cleared ontology cache to force reload" # Use portable timeout implementation (works on both macOS and Linux) echo "Making request to trigger ontology loading (testing for deadlock)..." +echo "DEBUG: Target URL: $namespace_doc" +echo "DEBUG: Using cert: $OWNER_CERT_FILE" # Portable timeout function - works on both macOS and Linux +# Use temporary file to capture curl output and status +temp_output=$(mktemp) +temp_stderr=$(mktemp) request_pid="" ( - curl -k -f -s \ + curl -k -f -w "\nHTTP_STATUS:%{http_code}\n" \ -E "$OWNER_CERT_FILE":"$OWNER_CERT_PWD" \ -H "Accept: application/n-triples" \ - "$namespace_doc" > /dev/null + "$namespace_doc" > "$temp_output" 2> "$temp_stderr" ) & request_pid=$! @@ -104,6 +109,9 @@ while kill -0 "$request_pid" 2>/dev/null; do if [ $elapsed -ge $timeout_seconds ]; then kill -9 "$request_pid" 2>/dev/null || true echo "ERROR: Request timed out after ${timeout_seconds} seconds - deadlock detected!" + echo "DEBUG: Curl stderr output:" + cat "$temp_stderr" + rm -f "$temp_output" "$temp_stderr" exit 1 fi sleep 1 @@ -113,11 +121,26 @@ done # Check if curl succeeded wait "$request_pid" curl_exit_code=$? + +echo "DEBUG: Curl exit code: $curl_exit_code" +echo "DEBUG: Curl stderr:" +cat "$temp_stderr" +echo "DEBUG: Response (first 500 chars):" +head -c 500 "$temp_output" +echo "" + if [ $curl_exit_code -ne 0 ]; then echo "ERROR: Request failed with exit code $curl_exit_code" + rm -f "$temp_output" "$temp_stderr" exit 1 fi +# Extract HTTP status code +http_status=$(grep "HTTP_STATUS:" "$temp_output" | cut -d':' -f2) +echo "DEBUG: HTTP status code: $http_status" + +rm -f "$temp_output" "$temp_stderr" + echo "Request completed successfully in ${elapsed}s (no deadlock)" # Step 6: Verify the import is present in the loaded ontology From a598d7986f96c92a662c8b0e1c6efb71c8f3bf80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?= Date: Sun, 25 Jan 2026 12:01:03 +0100 Subject: [PATCH 3/8] Debug test --- .../model/ontology-import-upload-no-deadlock.sh | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/http-tests/admin/model/ontology-import-upload-no-deadlock.sh b/http-tests/admin/model/ontology-import-upload-no-deadlock.sh index 2b8e333a9..80276a938 100755 --- a/http-tests/admin/model/ontology-import-upload-no-deadlock.sh +++ b/http-tests/admin/model/ontology-import-upload-no-deadlock.sh @@ -93,32 +93,42 @@ echo "DEBUG: Using cert: $OWNER_CERT_FILE" # Use temporary file to capture curl output and status temp_output=$(mktemp) temp_stderr=$(mktemp) +echo "DEBUG: Created temp files: $temp_output, $temp_stderr" + request_pid="" ( + echo "DEBUG: [subprocess] Starting curl request..." >&2 curl -k -f -w "\nHTTP_STATUS:%{http_code}\n" \ -E "$OWNER_CERT_FILE":"$OWNER_CERT_PWD" \ -H "Accept: application/n-triples" \ "$namespace_doc" > "$temp_output" 2> "$temp_stderr" + curl_result=$? + echo "DEBUG: [subprocess] Curl finished with exit code: $curl_result" >&2 + exit $curl_result ) & request_pid=$! +echo "DEBUG: Background process PID: $request_pid" # Wait up to 30 seconds for the request to complete timeout_seconds=30 elapsed=0 +echo "DEBUG: Entering wait loop..." while kill -0 "$request_pid" 2>/dev/null; do if [ $elapsed -ge $timeout_seconds ]; then kill -9 "$request_pid" 2>/dev/null || true echo "ERROR: Request timed out after ${timeout_seconds} seconds - deadlock detected!" echo "DEBUG: Curl stderr output:" - cat "$temp_stderr" + cat "$temp_stderr" || echo "(could not read stderr file)" rm -f "$temp_output" "$temp_stderr" exit 1 fi sleep 1 ((elapsed++)) done +echo "DEBUG: Exited wait loop after ${elapsed}s" # Check if curl succeeded +echo "DEBUG: Calling wait on PID $request_pid..." wait "$request_pid" curl_exit_code=$? From 85e8c4122772f0c81645fb0d0f78f332f6ce2fa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?= Date: Sun, 25 Jan 2026 12:40:07 +0100 Subject: [PATCH 4/8] Debug test --- http-tests/admin/model/ontology-import-upload-no-deadlock.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/http-tests/admin/model/ontology-import-upload-no-deadlock.sh b/http-tests/admin/model/ontology-import-upload-no-deadlock.sh index 80276a938..351c934b5 100755 --- a/http-tests/admin/model/ontology-import-upload-no-deadlock.sh +++ b/http-tests/admin/model/ontology-import-upload-no-deadlock.sh @@ -123,14 +123,17 @@ while kill -0 "$request_pid" 2>/dev/null; do exit 1 fi sleep 1 - ((elapsed++)) + elapsed=$((elapsed + 1)) done echo "DEBUG: Exited wait loop after ${elapsed}s" # Check if curl succeeded echo "DEBUG: Calling wait on PID $request_pid..." +set +e # Temporarily disable exit-on-error for wait wait "$request_pid" curl_exit_code=$? +set -e # Re-enable exit-on-error +echo "DEBUG: wait returned: $curl_exit_code" echo "DEBUG: Curl exit code: $curl_exit_code" echo "DEBUG: Curl stderr:" From 3f6d94f6a872d920f1848bec3b70da2d4c33fad0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?= Date: Sun, 25 Jan 2026 13:44:07 +0100 Subject: [PATCH 5/8] Removed debug output --- .../ontology-import-upload-no-deadlock.sh | 37 +------------------ 1 file changed, 2 insertions(+), 35 deletions(-) diff --git a/http-tests/admin/model/ontology-import-upload-no-deadlock.sh b/http-tests/admin/model/ontology-import-upload-no-deadlock.sh index 351c934b5..2151f0c08 100755 --- a/http-tests/admin/model/ontology-import-upload-no-deadlock.sh +++ b/http-tests/admin/model/ontology-import-upload-no-deadlock.sh @@ -86,74 +86,41 @@ echo "Cleared ontology cache to force reload" # Use portable timeout implementation (works on both macOS and Linux) echo "Making request to trigger ontology loading (testing for deadlock)..." -echo "DEBUG: Target URL: $namespace_doc" -echo "DEBUG: Using cert: $OWNER_CERT_FILE" # Portable timeout function - works on both macOS and Linux -# Use temporary file to capture curl output and status -temp_output=$(mktemp) -temp_stderr=$(mktemp) -echo "DEBUG: Created temp files: $temp_output, $temp_stderr" - request_pid="" ( - echo "DEBUG: [subprocess] Starting curl request..." >&2 - curl -k -f -w "\nHTTP_STATUS:%{http_code}\n" \ + curl -k -f -s \ -E "$OWNER_CERT_FILE":"$OWNER_CERT_PWD" \ -H "Accept: application/n-triples" \ - "$namespace_doc" > "$temp_output" 2> "$temp_stderr" - curl_result=$? - echo "DEBUG: [subprocess] Curl finished with exit code: $curl_result" >&2 - exit $curl_result + "$namespace_doc" > /dev/null ) & request_pid=$! -echo "DEBUG: Background process PID: $request_pid" # Wait up to 30 seconds for the request to complete timeout_seconds=30 elapsed=0 -echo "DEBUG: Entering wait loop..." while kill -0 "$request_pid" 2>/dev/null; do if [ $elapsed -ge $timeout_seconds ]; then kill -9 "$request_pid" 2>/dev/null || true echo "ERROR: Request timed out after ${timeout_seconds} seconds - deadlock detected!" - echo "DEBUG: Curl stderr output:" - cat "$temp_stderr" || echo "(could not read stderr file)" - rm -f "$temp_output" "$temp_stderr" exit 1 fi sleep 1 elapsed=$((elapsed + 1)) done -echo "DEBUG: Exited wait loop after ${elapsed}s" # Check if curl succeeded -echo "DEBUG: Calling wait on PID $request_pid..." set +e # Temporarily disable exit-on-error for wait wait "$request_pid" curl_exit_code=$? set -e # Re-enable exit-on-error -echo "DEBUG: wait returned: $curl_exit_code" - -echo "DEBUG: Curl exit code: $curl_exit_code" -echo "DEBUG: Curl stderr:" -cat "$temp_stderr" -echo "DEBUG: Response (first 500 chars):" -head -c 500 "$temp_output" -echo "" if [ $curl_exit_code -ne 0 ]; then echo "ERROR: Request failed with exit code $curl_exit_code" - rm -f "$temp_output" "$temp_stderr" exit 1 fi -# Extract HTTP status code -http_status=$(grep "HTTP_STATUS:" "$temp_output" | cut -d':' -f2) -echo "DEBUG: HTTP status code: $http_status" - -rm -f "$temp_output" "$temp_stderr" - echo "Request completed successfully in ${elapsed}s (no deadlock)" # Step 6: Verify the import is present in the loaded ontology From 5566a3479f5eab91f128fe8e79a4172764fa72b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?= Date: Sun, 25 Jan 2026 14:09:45 +0100 Subject: [PATCH 6/8] Debug test --- .../ontology-import-upload-no-deadlock.sh | 42 +++---------------- 1 file changed, 6 insertions(+), 36 deletions(-) diff --git a/http-tests/admin/model/ontology-import-upload-no-deadlock.sh b/http-tests/admin/model/ontology-import-upload-no-deadlock.sh index 2151f0c08..1fe3d9ccb 100755 --- a/http-tests/admin/model/ontology-import-upload-no-deadlock.sh +++ b/http-tests/admin/model/ontology-import-upload-no-deadlock.sh @@ -83,45 +83,15 @@ echo "Cleared ontology cache to force reload" # Step 5: Make a request that triggers ontology loading # This would cause a deadlock without the OntologyFilter fix -# Use portable timeout implementation (works on both macOS and Linux) echo "Making request to trigger ontology loading (testing for deadlock)..." -# Portable timeout function - works on both macOS and Linux -request_pid="" -( - curl -k -f -s \ - -E "$OWNER_CERT_FILE":"$OWNER_CERT_PWD" \ - -H "Accept: application/n-triples" \ - "$namespace_doc" > /dev/null -) & -request_pid=$! - -# Wait up to 30 seconds for the request to complete -timeout_seconds=30 -elapsed=0 -while kill -0 "$request_pid" 2>/dev/null; do - if [ $elapsed -ge $timeout_seconds ]; then - kill -9 "$request_pid" 2>/dev/null || true - echo "ERROR: Request timed out after ${timeout_seconds} seconds - deadlock detected!" - exit 1 - fi - sleep 1 - elapsed=$((elapsed + 1)) -done - -# Check if curl succeeded -set +e # Temporarily disable exit-on-error for wait -wait "$request_pid" -curl_exit_code=$? -set -e # Re-enable exit-on-error - -if [ $curl_exit_code -ne 0 ]; then - echo "ERROR: Request failed with exit code $curl_exit_code" - exit 1 -fi - -echo "Request completed successfully in ${elapsed}s (no deadlock)" +curl -k -f -s \ + -E "$OWNER_CERT_FILE":"$OWNER_CERT_PWD" \ + -H "Accept: application/n-triples" \ + "$namespace_doc" > /dev/null + +echo "Request completed successfully (no deadlock)" # Step 6: Verify the import is present in the loaded ontology From 5930ccee81dc656418583891d7d1a3c2a871a8ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?= Date: Sun, 25 Jan 2026 14:31:02 +0100 Subject: [PATCH 7/8] Debug test --- .../ontology-import-upload-no-deadlock.sh | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/http-tests/admin/model/ontology-import-upload-no-deadlock.sh b/http-tests/admin/model/ontology-import-upload-no-deadlock.sh index 1fe3d9ccb..de1a2cede 100755 --- a/http-tests/admin/model/ontology-import-upload-no-deadlock.sh +++ b/http-tests/admin/model/ontology-import-upload-no-deadlock.sh @@ -81,19 +81,8 @@ clear-ontology.sh \ echo "Cleared ontology cache to force reload" -# Step 5: Make a request that triggers ontology loading -# This would cause a deadlock without the OntologyFilter fix - -echo "Making request to trigger ontology loading (testing for deadlock)..." - -curl -k -f -s \ - -E "$OWNER_CERT_FILE":"$OWNER_CERT_PWD" \ - -H "Accept: application/n-triples" \ - "$namespace_doc" > /dev/null - -echo "Request completed successfully (no deadlock)" - -# Step 6: Verify the import is present in the loaded ontology +# Step 5: Verify the import is present in the loaded ontology +# This request also triggers ontology loading and would detect deadlock curl -k -f -s \ -H "Accept: application/n-triples" \ @@ -102,7 +91,7 @@ curl -k -f -s \ echo "Verified owl:import is present in namespace ontology" -# Step 7: Verify the uploaded file is still accessible after ontology loading +# Step 6: Verify the uploaded file is still accessible after ontology loading curl -k -f -s \ -E "$AGENT_CERT_FILE":"$AGENT_CERT_PWD" \ @@ -111,7 +100,7 @@ curl -k -f -s \ echo "Uploaded file is still accessible after ontology import" -# Step 8: Verify that the imported ontology content is accessible via the namespace document +# Step 7: Verify that the imported ontology content is accessible via the namespace document # This confirms the import was actually loaded (not just skipped) curl -k -f -s \ From 6b4d572f211651b37cd0f19ad54b07a0113961f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?= Date: Sun, 25 Jan 2026 14:48:17 +0100 Subject: [PATCH 8/8] Removed debug output --- .../model/ontology-import-upload-no-deadlock.sh | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/http-tests/admin/model/ontology-import-upload-no-deadlock.sh b/http-tests/admin/model/ontology-import-upload-no-deadlock.sh index de1a2cede..939da9687 100755 --- a/http-tests/admin/model/ontology-import-upload-no-deadlock.sh +++ b/http-tests/admin/model/ontology-import-upload-no-deadlock.sh @@ -47,16 +47,12 @@ file_doc_ntriples=$(get.sh \ upload_uri=$(echo "$file_doc_ntriples" | sed -rn "s/<${file_doc//\//\\/}> <(.*)> \./\1/p") -echo "Uploaded file URI: $upload_uri" - # Verify the uploaded file is accessible before we add it as an import curl -k -f -s \ -E "$AGENT_CERT_FILE":"$AGENT_CERT_PWD" \ -H "Accept: ${file_content_type}" \ "$upload_uri" > /dev/null -echo "Upload file is accessible" - # Step 3: Add the uploaded file as an owl:import to the namespace ontology namespace_doc="${END_USER_BASE_URL}ns" @@ -69,8 +65,6 @@ add-ontology-import.sh \ --import "$upload_uri" \ "$ontology_doc" -echo "Added owl:import of uploaded file to namespace ontology" - # Step 4: Clear the namespace ontology from memory to force reload on next request clear-ontology.sh \ @@ -79,8 +73,6 @@ clear-ontology.sh \ -b "$ADMIN_BASE_URL" \ --ontology "$namespace" -echo "Cleared ontology cache to force reload" - # Step 5: Verify the import is present in the loaded ontology # This request also triggers ontology loading and would detect deadlock @@ -89,8 +81,6 @@ curl -k -f -s \ "$namespace_doc" \ | grep "<${namespace}> <${upload_uri}>" > /dev/null -echo "Verified owl:import is present in namespace ontology" - # Step 6: Verify the uploaded file is still accessible after ontology loading curl -k -f -s \ @@ -98,8 +88,6 @@ curl -k -f -s \ -H "Accept: ${file_content_type}" \ "$upload_uri" > /dev/null -echo "Uploaded file is still accessible after ontology import" - # Step 7: Verify that the imported ontology content is accessible via the namespace document # This confirms the import was actually loaded (not just skipped) @@ -110,7 +98,3 @@ curl -k -f -s \ --data-urlencode "query=SELECT * { ?p ?o }" \ "$namespace_doc" \ | grep 'Test Class' > /dev/null - -echo "Verified imported ontology content is accessible via SPARQL" - -echo "✓ All tests passed - no deadlock detected when importing uploaded files in ontology"