diff --git a/.github/workflows/develop-status.yml b/.github/workflows/develop-status.yml index c007077a98..cb782abfc2 100644 --- a/.github/workflows/develop-status.yml +++ b/.github/workflows/develop-status.yml @@ -29,7 +29,7 @@ jobs: - name: Build run: mvn -B -U clean install -Pquick,\!formatting - name: Verify - run: mvn -B verify -P\!skipSlowTests,\!formatting -Dmaven.javadoc.skip=true -Djapicmp.skip -Denforcer.skip=true -Danimal.sniffer.skip=true + run: mvn -B install -P-skipSlowTests -Dmaven.javadoc.skip=true - name: Publish Test Report if: failure() uses: scacap/action-surefire-report@v1 diff --git a/.github/workflows/main-status.yml b/.github/workflows/main-status.yml index 53d14ab0a9..ba8fd33f39 100644 --- a/.github/workflows/main-status.yml +++ b/.github/workflows/main-status.yml @@ -27,9 +27,9 @@ jobs: restore-keys: | ${{ runner.os }}-jdk${{ matrix.jdk }}-maven- - name: Build - run: mvn -B -U -T 2 clean install -Pquick,-formatting + run: mvn -B -U -T 2C clean install -DskipTests - name: Run all tests - run: mvn -B verify -P-skipSlowTests,-formatting -Dmaven.javadoc.skip=true -Djapicmp.skip -Denforcer.skip=true -Danimal.sniffer.skip=true + run: mvn -B install -P-skipSlowTests -Dmaven.javadoc.skip=true - name: Publish Test Report if: failure() uses: scacap/action-surefire-report@v1 diff --git a/.github/workflows/pr-verify.yml b/.github/workflows/pr-verify.yml index 00ee011a57..29edc6b64e 100644 --- a/.github/workflows/pr-verify.yml +++ b/.github/workflows/pr-verify.yml @@ -50,7 +50,7 @@ jobs: strategy: fail-fast: true matrix: - jdk: [ 11, 24 ] + jdk: [ 11, 25 ] steps: - uses: actions/checkout@v4 - name: Set up JDK diff --git a/PLANS.md b/PLANS.md new file mode 100644 index 0000000000..7d8044a9f7 --- /dev/null +++ b/PLANS.md @@ -0,0 +1,152 @@ +# Codex Execution Plans (ExecPlans): + +This document describes the requirements for an execution plan ("ExecPlan"), a design document that a coding agent can follow to deliver a working feature or system change. Treat the reader as a complete beginner to this repository: they have only the current working tree and the single ExecPlan file you provide. There is no memory of prior plans and no external context. + +## How to use ExecPlans and PLANS.md + +When authoring an executable specification (ExecPlan), follow PLANS.md _to the letter_. If it is not in your context, refresh your memory by reading the entire PLANS.md file. Be thorough in reading (and re-reading) source material to produce an accurate specification. When creating a spec, start from the skeleton and flesh it out as you do your research. + +When implementing an executable specification (ExecPlan), do not prompt the user for "next steps"; simply proceed to the next milestone. Keep all sections up to date, add or split entries in the list at every stopping point to affirmatively state the progress made and next steps. Resolve ambiguities autonomously, and commit frequently. + +When discussing an executable specification (ExecPlan), record decisions in a log in the spec for posterity; it should be unambiguously clear why any change to the specification was made. ExecPlans are living documents, and it should always be possible to restart from _only_ the ExecPlan and no other work. + +When researching a design with challenging requirements or significant unknowns, use milestones to implement proof of concepts, "toy implementations", etc., that allow validating whether the user's proposal is feasible. Read the source code of libraries by finding or acquiring them, research deeply, and include prototypes to guide a fuller implementation. + +## Requirements + +NON-NEGOTIABLE REQUIREMENTS: + +* Every ExecPlan must be fully self-contained. Self-contained means that in its current form it contains all knowledge and instructions needed for a novice to succeed. +* Every ExecPlan is a living document. Contributors are required to revise it as progress is made, as discoveries occur, and as design decisions are finalized. Each revision must remain fully self-contained. +* Every ExecPlan must enable a complete novice to implement the feature end-to-end without prior knowledge of this repo. +* Every ExecPlan must produce a demonstrably working behavior, not merely code changes to "meet a definition". +* Every ExecPlan must define every term of art in plain language or do not use it. + +Purpose and intent come first. Begin by explaining, in a few sentences, why the work matters from a user's perspective: what someone can do after this change that they could not do before, and how to see it working. Then guide the reader through the exact steps to achieve that outcome, including what to edit, what to run, and what they should observe. + +The agent executing your plan can list files, read files, search, run the project, and run tests. It does not know any prior context and cannot infer what you meant from earlier milestones. Repeat any assumption you rely on. Do not point to external blogs or docs; if knowledge is required, embed it in the plan itself in your own words. If an ExecPlan builds upon a prior ExecPlan and that file is checked in, incorporate it by reference. If it is not, you must include all relevant context from that plan. + +## Formatting + +Format and envelope are simple and strict. Each ExecPlan must be one single fenced code block labeled as `md` that begins and ends with triple backticks. Do not nest additional triple-backtick code fences inside; when you need to show commands, transcripts, diffs, or code, present them as indented blocks within that single fence. Use indentation for clarity rather than code fences inside an ExecPlan to avoid prematurely closing the ExecPlan's code fence. Use two newlines after every heading, use # and ## and so on, and correct syntax for ordered and unordered lists. + +When writing an ExecPlan to a Markdown (.md) file where the content of the file *is only* the single ExecPlan, you should omit the triple backticks. + +Write in plain prose. Prefer sentences over lists. Avoid checklists, tables, and long enumerations unless brevity would obscure meaning. Checklists are permitted only in the `Progress` section, where they are mandatory. Narrative sections must remain prose-first. + +## Guidelines + +Self-containment and plain language are paramount. If you introduce a phrase that is not ordinary English ("daemon", "middleware", "RPC gateway", "filter graph"), define it immediately and remind the reader how it manifests in this repository (for example, by naming the files or commands where it appears). Do not say "as defined previously" or "according to the architecture doc." Include the needed explanation here, even if you repeat yourself. + +Avoid common failure modes. Do not rely on undefined jargon. Do not describe "the letter of a feature" so narrowly that the resulting code compiles but does nothing meaningful. Do not outsource key decisions to the reader. When ambiguity exists, resolve it in the plan itself and explain why you chose that path. Err on the side of over-explaining user-visible effects and under-specifying incidental implementation details. + +Anchor the plan with observable outcomes. State what the user can do after implementation, the commands to run, and the outputs they should see. Acceptance should be phrased as behavior a human can verify ("after starting the server, navigating to [http://localhost:8080/health](http://localhost:8080/health) returns HTTP 200 with body OK") rather than internal attributes ("added a HealthCheck struct"). If a change is internal, explain how its impact can still be demonstrated (for example, by running tests that fail before and pass after, and by showing a scenario that uses the new behavior). + +Specify repository context explicitly. Name files with full repository-relative paths, name functions and modules precisely, and describe where new files should be created. If touching multiple areas, include a short orientation paragraph that explains how those parts fit together so a novice can navigate confidently. When running commands, show the working directory and exact command line. When outcomes depend on environment, state the assumptions and provide alternatives when reasonable. + +Be idempotent and safe. Write the steps so they can be run multiple times without causing damage or drift. If a step can fail halfway, include how to retry or adapt. If a migration or destructive operation is necessary, spell out backups or safe fallbacks. Prefer additive, testable changes that can be validated as you go. + +Validation is not optional. Include instructions to run tests, to start the system if applicable, and to observe it doing something useful. Describe comprehensive testing for any new features or capabilities. Include expected outputs and error messages so a novice can tell success from failure. Where possible, show how to prove that the change is effective beyond compilation (for example, through a small end-to-end scenario, a CLI invocation, or an HTTP request/response transcript). State the exact test commands appropriate to the project’s toolchain and how to interpret their results. + +Capture evidence. When your steps produce terminal output, short diffs, or logs, include them inside the single fenced block as indented examples. Keep them concise and focused on what proves success. If you need to include a patch, prefer file-scoped diffs or small excerpts that a reader can recreate by following your instructions rather than pasting large blobs. + +## Milestones + +Milestones are narrative, not bureaucracy. If you break the work into milestones, introduce each with a brief paragraph that describes the scope, what will exist at the end of the milestone that did not exist before, the commands to run, and the acceptance you expect to observe. Keep it readable as a story: goal, work, result, proof. Progress and milestones are distinct: milestones tell the story, progress tracks granular work. Both must exist. Never abbreviate a milestone merely for the sake of brevity, do not leave out details that could be crucial to a future implementation. + +Each milestone must be independently verifiable and incrementally implement the overall goal of the execution plan. + +## Living plans and design decisions + +* ExecPlans are living documents. As you make key design decisions, update the plan to record both the decision and the thinking behind it. Record all decisions in the `Decision Log` section. +* ExecPlans must contain and maintain a `Progress` section, a `Surprises & Discoveries` section, a `Decision Log`, and an `Outcomes & Retrospective` section. These are not optional. +* When you discover optimizer behavior, performance tradeoffs, unexpected bugs, or inverse/unapply semantics that shaped your approach, capture those observations in the `Surprises & Discoveries` section with short evidence snippets (test output is ideal). +* If you change course mid-implementation, document why in the `Decision Log` and reflect the implications in `Progress`. Plans are guides for the next contributor as much as checklists for you. +* At completion of a major task or the full plan, write an `Outcomes & Retrospective` entry summarizing what was achieved, what remains, and lessons learned. + +# Prototyping milestones and parallel implementations + +It is acceptable—-and often encouraged—-to include explicit prototyping milestones when they de-risk a larger change. Examples: adding a low-level operator to a dependency to validate feasibility, or exploring two composition orders while measuring optimizer effects. Keep prototypes additive and testable. Clearly label the scope as “prototyping”; describe how to run and observe results; and state the criteria for promoting or discarding the prototype. + +Prefer additive code changes followed by subtractions that keep tests passing. Parallel implementations (e.g., keeping an adapter alongside an older path during migration) are fine when they reduce risk or enable tests to continue passing during a large migration. Describe how to validate both paths and how to retire one safely with tests. When working with multiple new libraries or feature areas, consider creating spikes that evaluate the feasibility of these features _independently_ of one another, proving that the external library performs as expected and implements the features we need in isolation. + +## Skeleton of a Good ExecPlan + +```md +# + +This ExecPlan is a living document. The sections `Progress`, `Surprises & Discoveries`, `Decision Log`, and `Outcomes & Retrospective` must be kept up to date as work proceeds. + +If PLANS.md file is checked into the repo, reference the path to that file here from the repository root and note that this document must be maintained in accordance with PLANS.md. + +## Purpose / Big Picture + +Explain in a few sentences what someone gains after this change and how they can see it working. State the user-visible behavior you will enable. + +## Progress + +Use a list with checkboxes to summarize granular steps. Every stopping point must be documented here, even if it requires splitting a partially completed task into two (“done” vs. “remaining”). This section must always reflect the actual current state of the work. + +- [x] (2025-10-01 13:00Z) Example completed step. +- [ ] Example incomplete step. +- [ ] Example partially completed step (completed: X; remaining: Y). + +Use timestamps to measure rates of progress. + +## Surprises & Discoveries + +Document unexpected behaviors, bugs, optimizations, or insights discovered during implementation. Provide concise evidence. + +- Observation: … + Evidence: … + +## Decision Log + +Record every decision made while working on the plan in the format: + +- Decision: … + Rationale: … + Date/Author: … + +## Outcomes & Retrospective + +Summarize outcomes, gaps, and lessons learned at major milestones or at completion. Compare the result against the original purpose. + +## Context and Orientation + +Describe the current state relevant to this task as if the reader knows nothing. Name the key files and modules by full path. Define any non-obvious term you will use. Do not refer to prior plans. + +## Plan of Work + +Describe, in prose, the sequence of edits and additions. For each edit, name the file and location (function, module) and what to insert or change. Keep it concrete and minimal. + +## Concrete Steps + +State the exact commands to run and where to run them (working directory). When a command generates output, show a short expected transcript so the reader can compare. This section must be updated as work proceeds. + +## Validation and Acceptance + +Describe how to start or exercise the system and what to observe. Phrase acceptance as behavior, with specific inputs and outputs. If tests are involved, say "run and expect passed; the new test fails before the change and passes after>". + +## Idempotence and Recovery + +If steps can be repeated safely, say so. If a step is risky, provide a safe retry or rollback path. Keep the environment clean after completion. + +## Artifacts and Notes + +Include the most important transcripts, diffs, or snippets as indented examples. Keep them concise and focused on what proves success. + +## Interfaces and Dependencies + +Be prescriptive. Name the libraries, modules, and services to use and why. Specify the types, traits/interfaces, and function signatures that must exist at the end of the milestone. Prefer stable names and paths such as `crate::module::function` or `package.submodule.Interface`. E.g.: + +In crates/foo/planner.rs, define: + + pub trait Planner { + fn plan(&self, observed: &Observed) -> Vec; + } +``` + +If you follow the guidance above, a single, stateless agent -- or a human novice -- can read your ExecPlan from top to bottom and produce a working, observable result. That is the bar: SELF-CONTAINED, SELF-SUFFICIENT, NOVICE-GUIDING, OUTCOME-FOCUSED. + +When you revise a plan, you must ensure your changes are comprehensively reflected across all sections, including the living document sections, and you must write a note at the bottom of the plan describing the change and the reason why. ExecPlans must describe not just the what but the why for almost everything. diff --git a/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/AbstractIRI.java b/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/AbstractIRI.java index ad3db23bc9..b068fcff57 100644 --- a/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/AbstractIRI.java +++ b/core/model-api/src/main/java/org/eclipse/rdf4j/model/base/AbstractIRI.java @@ -58,8 +58,6 @@ public int hashCode() { cachedHashCode = cached; } } - cached = stringValue().hashCode(); - cachedHashCode = cached; } return cached; } @@ -126,6 +124,22 @@ private int split() { } } + @Override + public int hashCode() { + return iri.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof IRI)) { + return false; + } + return iri.equals(o.toString()); + } + } } diff --git a/core/model/src/main/java/org/eclipse/rdf4j/model/impl/SimpleIRI.java b/core/model/src/main/java/org/eclipse/rdf4j/model/impl/SimpleIRI.java index e40b15e714..08729a6c5a 100644 --- a/core/model/src/main/java/org/eclipse/rdf4j/model/impl/SimpleIRI.java +++ b/core/model/src/main/java/org/eclipse/rdf4j/model/impl/SimpleIRI.java @@ -123,4 +123,20 @@ public String getLocalName() { return iriString.substring(localNameIdx); } + @Override + public int hashCode() { + return iriString.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof IRI)) { + return false; + } + + return iriString.equals(o.toString()); + } } diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/ValueStore.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/ValueStore.java index c1469254f4..2d8ac34f7e 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/ValueStore.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/ValueStore.java @@ -1623,7 +1623,8 @@ private LmdbIRI data2uri(long id, byte[] data, LmdbIRI value) throws IOException if (value == null) { return new LmdbIRI(revision, namespace, localName, id); } else { - value.setIRIString(namespace + localName); + value.setNamespaceAndIri(namespace, localName); +// value.setIRIString(namespace + localName); return value; } } diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/model/LmdbIRI.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/model/LmdbIRI.java index 99cf00cbe3..f6c77a0c93 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/model/LmdbIRI.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/model/LmdbIRI.java @@ -11,13 +11,15 @@ package org.eclipse.rdf4j.sail.lmdb.model; import java.io.ObjectStreamException; +import java.util.Objects; -import org.eclipse.rdf4j.model.impl.SimpleIRI; +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.util.URIUtil; import org.eclipse.rdf4j.sail.lmdb.ValueStoreRevision; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class LmdbIRI extends SimpleIRI implements LmdbResource { +public class LmdbIRI implements LmdbResource, IRI { private static final long serialVersionUID = -5888138591826143179L; private static final Logger log = LoggerFactory.getLogger(LmdbIRI.class); @@ -31,13 +33,21 @@ public class LmdbIRI extends SimpleIRI implements LmdbResource { private volatile long internalID; private volatile boolean initialized = false; + /** + * The IRI string. + */ + private String iriString; + + /** + * An index indicating the first character of the local name in the IRI string, -1 if not yet set. + */ + private int localNameIdx; /*--------------* * Constructors * *--------------*/ public LmdbIRI(ValueStoreRevision revision, long internalID) { - super(); setInternalID(internalID, revision); } @@ -46,17 +56,19 @@ public LmdbIRI(ValueStoreRevision revision, String uri) { } public LmdbIRI(ValueStoreRevision revision, String uri, long internalID) { - super(uri); + setIRIString(uri); setInternalID(internalID, revision); this.initialized = true; } - public LmdbIRI(ValueStoreRevision revision, String namespace, String localname) { - this(revision, namespace + localname); + public LmdbIRI(ValueStoreRevision revision, String namespace, String localName) { + this(revision, namespace, localName, UNKNOWN_ID); } - public LmdbIRI(ValueStoreRevision revision, String namespace, String localname, long internalID) { - this(revision, namespace + localname, internalID); + public LmdbIRI(ValueStoreRevision revision, String namespace, String localName, long internalID) { + this.revision = revision; + setNamespaceAndIri(namespace, localName); + setInternalID(internalID, revision); } /*---------* @@ -79,31 +91,50 @@ public long getInternalID() { return internalID; } - @Override - public void setIRIString(String iriString) { - super.setIRIString(iriString); + private void setIRIString(String iriString) { + Objects.requireNonNull(iriString, "iriString must not be null"); + + if (iriString.indexOf(':') < 0) { + throw new IllegalArgumentException("Not a valid (absolute) IRI: " + iriString); + } + + this.iriString = iriString; + this.localNameIdx = -1; } @Override public String getNamespace() { + init(); - return super.getNamespace(); + if (localNameIdx < 0) { + localNameIdx = URIUtil.getLocalNameIndex(iriString); + } + + return iriString.substring(0, localNameIdx); } @Override public String getLocalName() { + init(); - return super.getLocalName(); + if (localNameIdx < 0) { + localNameIdx = URIUtil.getLocalNameIndex(iriString); + } + + return iriString.substring(localNameIdx); } @Override public String stringValue() { + if (iriString != null) { + return iriString; + } init(); - return super.stringValue(); + return iriString; } public void init() { - if (!initialized) { + if (iriString == null && !initialized) { synchronized (this) { if (!initialized) { boolean resolved = revision.resolveValue(internalID, this); @@ -122,20 +153,75 @@ public boolean equals(Object o) { return true; } - if (o instanceof LmdbIRI && internalID != UNKNOWN_ID) { + if (o == null) { + return false; + } + + if (o.getClass() == LmdbIRI.class) { + if (internalID == UNKNOWN_ID) { + boolean equals = stringValue().equals(((IRI) o).stringValue()); + if (equals && revision.equals(((LmdbIRI) o).revision)) { + internalID = ((LmdbIRI) o).internalID; + } + return equals; + } + LmdbIRI otherLmdbURI = (LmdbIRI) o; - if (otherLmdbURI.internalID != UNKNOWN_ID && revision.equals(otherLmdbURI.revision)) { + if (revision.equals(otherLmdbURI.revision)) { + if (otherLmdbURI.internalID == UNKNOWN_ID) { + boolean equals = stringValue().equals(((IRI) o).stringValue()); + if (equals) { + otherLmdbURI.internalID = this.internalID; + } + return equals; + } + // LmdbURI's from the same revision of the same lmdb store, with // both ID's set - return internalID == otherLmdbURI.internalID; + boolean equal = internalID == otherLmdbURI.internalID; + if (equal) { + if (iriString == null) { + iriString = otherLmdbURI.iriString; + localNameIdx = otherLmdbURI.localNameIdx; + } else if (otherLmdbURI.iriString == null) { + otherLmdbURI.iriString = iriString; + otherLmdbURI.localNameIdx = localNameIdx; + } + } + return equal; } } - return super.equals(o); + + if (!(o instanceof IRI)) { + return false; + } + + return stringValue().equals(((IRI) o).stringValue()); + } + + @Override + public int hashCode() { + if (this.iriString != null) { + return this.iriString.hashCode(); + } + + init(); + return iriString.hashCode(); } protected Object writeReplace() throws ObjectStreamException { init(); return this; } + + @Override + public String toString() { + return stringValue(); + } + + public void setNamespaceAndIri(String namespace, String localName) { + localNameIdx = namespace.length(); + this.iriString = namespace + localName; + } } diff --git a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/model/LmdbLiteral.java b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/model/LmdbLiteral.java index d6efea7435..84e16136bc 100644 --- a/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/model/LmdbLiteral.java +++ b/core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/model/LmdbLiteral.java @@ -67,6 +67,7 @@ public LmdbLiteral(ValueStoreRevision revision, long internalID) { } public LmdbLiteral(ValueStoreRevision revision, String label, long internalID) { + assert label != null; this.label = label; coreDatatype = CoreDatatype.XSD.STRING; datatype = CoreDatatype.XSD.STRING.getIri(); @@ -79,6 +80,7 @@ public LmdbLiteral(ValueStoreRevision revision, String label, String lang) { } public LmdbLiteral(ValueStoreRevision revision, String label, String lang, long internalID) { + assert label != null; this.label = label; this.language = lang; coreDatatype = CoreDatatype.RDF.LANGSTRING; @@ -100,6 +102,7 @@ public LmdbLiteral(ValueStoreRevision revision, String label, CoreDatatype datat } public LmdbLiteral(ValueStoreRevision revision, String label, IRI datatype, long internalID) { + assert label != null; this.label = label; this.datatype = datatype; this.coreDatatype = null; @@ -109,6 +112,7 @@ public LmdbLiteral(ValueStoreRevision revision, String label, IRI datatype, long public LmdbLiteral(ValueStoreRevision revision, String label, IRI datatype, CoreDatatype coreDatatype, long internalID) { + assert label != null; this.label = label; assert datatype != null; assert coreDatatype != null; @@ -120,6 +124,7 @@ public LmdbLiteral(ValueStoreRevision revision, String label, IRI datatype, Core } public LmdbLiteral(ValueStoreRevision revision, String label, CoreDatatype coreDatatype, long internalID) { + assert label != null; this.label = label; this.coreDatatype = coreDatatype; this.datatype = coreDatatype.getIri(); @@ -196,9 +201,10 @@ public void init() { if (!initialized) { synchronized (this) { if (!initialized) { - revision.resolveValue(internalID, this); + boolean resolved = revision.resolveValue(internalID, this); + initialized = resolved; + assert resolved; } - initialized = true; } } } diff --git a/core/sail/shacl/pom.xml b/core/sail/shacl/pom.xml index 0a59d3cfec..8c5ae41955 100644 --- a/core/sail/shacl/pom.xml +++ b/core/sail/shacl/pom.xml @@ -97,6 +97,24 @@ maven-assembly-plugin + + org.apache.maven.plugins + maven-compiler-plugin + + + default-testCompile + + + + org.openjdk.jmh + jmh-generator-annprocess + ${jmhVersion} + + + + + + org.apache.maven.plugins maven-surefire-plugin diff --git a/core/sail/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/AbstractShaclTest.java b/core/sail/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/AbstractShaclTest.java index 33b43c19ea..5300ecc0ff 100644 --- a/core/sail/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/AbstractShaclTest.java +++ b/core/sail/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/AbstractShaclTest.java @@ -1011,6 +1011,7 @@ void runTestCaseSingleTransaction(TestCase testCase) { validationReportActual = ((ShaclSailValidationException) sailException.getCause()) .validationReportAsModel(); printResults(sailException); + shaclSailConnection.rollback(); } } @@ -1064,6 +1065,7 @@ void runTestCaseRevalidate(TestCase testCase, IsolationLevel isolationLevel) { if (e.getCause() instanceof ShaclSailValidationException) { report = ((ShaclSailValidationException) e.getCause()).getValidationReport(); } + shaclSailConnection.rollback(); } } diff --git a/pom.xml b/pom.xml index 44e2322d41..1d2f315909 100644 --- a/pom.xml +++ b/pom.xml @@ -656,7 +656,7 @@ no.hasmac hasmac-json-ld - 0.10.1 + 0.10.2 diff --git a/scripts/build-javadoc-archive.sh b/scripts/build-javadoc-archive.sh new file mode 100755 index 0000000000..9114e06100 --- /dev/null +++ b/scripts/build-javadoc-archive.sh @@ -0,0 +1,168 @@ +#!/usr/bin/env bash + +set -euo pipefail + +show_usage() { + cat <<'EOF' +Usage: build-javadoc-archive.sh [--output-dir ] [--help] + +Builds the aggregated RDF4J Javadocs and creates a compressed archive for +the current project version. By default, the archive is written to +site/static/javadoc/.tar.xz and a latest.tar.xz companion copy is +maintained. + +Environment overrides: + JAVADOC_BUILD_CMD Custom shell command that prepares target/reports/apidocs. + When set, the Maven build steps are skipped. + JAVADOC_SKIP_INSTALL If set to "true", skips the initial mvn -Pquick install. + JAVADOC_SOURCE_DIR Use an existing apidocs directory instead of searching defaults. + JAVADOC_ARCHIVE_VERSION + Override the version used for the archive file name. +EOF +} + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" + +OUTPUT_DIR="${REPO_ROOT}/site/static/javadoc" +COMPRESSOR="${JAVADOC_COMPRESSOR:-xz}" + +while [[ $# -gt 0 ]]; do + case "$1" in + --output-dir) + if [[ $# -lt 2 ]]; then + echo "Missing value for --output-dir" >&2 + exit 1 + fi + OUTPUT_DIR="$2" + shift 2 + ;; + -h|--help) + show_usage + exit 0 + ;; + *) + echo "Unknown option: $1" >&2 + show_usage >&2 + exit 1 + ;; + esac +done + +mkdir -p "${OUTPUT_DIR}" +OUTPUT_DIR="$(cd "${OUTPUT_DIR}" && pwd)" + +cd "${REPO_ROOT}" + +run_maven_with_fallback() { + local -a args=("$@") + local -a offline_cmd=("mvn" "-o" "${args[@]}") + if "${offline_cmd[@]}"; then + return 0 + fi + + echo "Offline Maven command failed, retrying online: mvn ${args[*]}" >&2 + local -a online_cmd=("mvn" "${args[@]}") + "${online_cmd[@]}" +} + +locate_javadoc_dir() { + if [[ -n "${JAVADOC_SOURCE_DIR:-}" ]]; then + if [[ -d "${JAVADOC_SOURCE_DIR}" ]]; then + printf '%s\n' "${JAVADOC_SOURCE_DIR}" + return 0 + fi + echo "JAVADOC_SOURCE_DIR '${JAVADOC_SOURCE_DIR}' does not exist" >&2 + return 1 + fi + + local candidate + for candidate in "${REPO_ROOT}/target/reports/apidocs" "${REPO_ROOT}/target/site/apidocs"; do + if [[ -d "${candidate}" ]]; then + printf '%s\n' "${candidate}" + return 0 + fi + done + + return 1 +} + +determine_version() { + local version + version="$(awk -F'[<>]' '//{print $3; exit}' pom.xml)" + if [[ -z "${version:-}" ]]; then + echo "Unable to determine project version from pom.xml" >&2 + exit 1 + fi + printf '%s\n' "${version}" +} + +prepare_javadocs() { + if [[ -n "${JAVADOC_SOURCE_DIR:-}" && -d "${JAVADOC_SOURCE_DIR}" ]]; then + echo "Using pre-built Javadocs from ${JAVADOC_SOURCE_DIR}; skipping Maven build." >&2 + return + fi + + if [[ -n "${JAVADOC_BUILD_CMD:-}" ]]; then + echo "Using custom javadoc build command from JAVADOC_BUILD_CMD" >&2 + bash -c "${JAVADOC_BUILD_CMD}" + return + fi + + if [[ "${JAVADOC_SKIP_INSTALL:-}" != "true" ]]; then + run_maven_with_fallback "-Pquick" "install" + else + echo "Skipping Maven install step because JAVADOC_SKIP_INSTALL=true" >&2 + fi + + run_maven_with_fallback "-Passembly" "-DskipTests" "-Djapicmp.skip" "javadoc:aggregate-no-fork" +} + +PROJECT_VERSION="${JAVADOC_ARCHIVE_VERSION:-$(determine_version)}" +echo "Building Javadocs for RDF4J ${PROJECT_VERSION}" + +prepare_javadocs + +JAVADOC_DIR="$(locate_javadoc_dir 2>/dev/null || true)" +if [[ -z "${JAVADOC_DIR}" || ! -d "${JAVADOC_DIR}" ]]; then + echo "Unable to locate generated Javadocs. Checked target/reports/apidocs and target/site/apidocs." >&2 + exit 1 +fi + +if [[ -z "$(find "${JAVADOC_DIR}" -mindepth 1 -print -quit)" ]]; then + echo "Javadoc directory '${JAVADOC_DIR}' is empty" >&2 + exit 1 +fi + +TMP_ARCHIVE="$(mktemp "${OUTPUT_DIR}/.${PROJECT_VERSION}.XXXXXX.tar.${COMPRESSOR}")" +trap 'rm -f "${TMP_ARCHIVE}"' EXIT + +echo "Compressing javadocs" + +case "${COMPRESSOR}" in + xz) + tar --no-xattrs --exclude '*/.*' -c -C "${JAVADOC_DIR}" . | xz -9 -T0 > "${TMP_ARCHIVE}" + EXTENSION="tar.xz" + ;; + zst|zstd) + tar --no-xattrs --exclude '*/.*' -c -C "${JAVADOC_DIR}" . | zstd -q --ultra -22 -T0 -o "${TMP_ARCHIVE}" + EXTENSION="tar.zst" + ;; + gz|gzip) + tar --no-xattrs --exclude '*/.*' -czf "${TMP_ARCHIVE}" -C "${JAVADOC_DIR}" . + EXTENSION="tgz" + ;; + *) + echo "Unsupported compressor '${COMPRESSOR}'. Supported values: xz, zst, gz." >&2 + exit 1 + ;; +esac + +FINAL_ARCHIVE="${OUTPUT_DIR}/${PROJECT_VERSION}.${EXTENSION}" +mv -f "${TMP_ARCHIVE}" "${FINAL_ARCHIVE}" +trap - EXIT + +cp -f "${FINAL_ARCHIVE}" "${OUTPUT_DIR}/latest.${EXTENSION}" + +echo "Created archive: ${FINAL_ARCHIVE}" +echo "Updated latest.${EXTENSION}" diff --git a/scripts/milestone-release.sh b/scripts/milestone-release.sh index e7a9531eef..82a952d16b 100755 --- a/scripts/milestone-release.sh +++ b/scripts/milestone-release.sh @@ -214,11 +214,12 @@ set -e mvn package -Passembly -DskipTests -Djapicmp.skip +JAVADOC_SKIP_INSTALL=true scripts/build-javadoc-archive.sh + git checkout main RELEASE_NOTES_BRANCH="${MVN_VERSION_RELEASE}-release-notes" git checkout -b "${RELEASE_NOTES_BRANCH}" -tar --no-xattrs --exclude ".*" -cvzf "site/static/javadoc/${MVN_VERSION_RELEASE}.tgz" -C target/site/apidocs . git add --all git commit -s -a -m "javadocs for ${MVN_VERSION_RELEASE}" git push --set-upstream origin "${RELEASE_NOTES_BRANCH}" diff --git a/scripts/release.sh b/scripts/release.sh index 456d4d7cc0..d5fc858b0c 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -254,11 +254,11 @@ set -e mvn package -Passembly -DskipTests -Djapicmp.skip +JAVADOC_SKIP_INSTALL=true scripts/build-javadoc-archive.sh + git checkout main git checkout -b "${RELEASE_NOTES_BRANCH}" -tar --no-xattrs --exclude ".*" -cvzf "site/static/javadoc/${MVN_VERSION_RELEASE}.tgz" -C target/reports/apidocs . -cp -f "site/static/javadoc/${MVN_VERSION_RELEASE}.tgz" "site/static/javadoc/latest.tgz" git add --all git commit -s -a -m "javadocs for ${MVN_VERSION_RELEASE}" git push --set-upstream origin "${RELEASE_NOTES_BRANCH}" diff --git a/scripts/update-javadoc-indexes.sh b/scripts/update-javadoc-indexes.sh new file mode 100755 index 0000000000..ca74a35f19 --- /dev/null +++ b/scripts/update-javadoc-indexes.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" +JAVADOC_DIR="${REPO_ROOT}/site/static/javadoc" +OLD_DIR="${JAVADOC_DIR}/old" + +if [[ ! -d "${JAVADOC_DIR}" ]]; then + echo "Javadoc directory '${JAVADOC_DIR}' does not exist." >&2 + exit 1 +fi + +if [[ ! -d "${OLD_DIR}" ]]; then + echo "Legacy javadoc directory '${OLD_DIR}' does not exist." >&2 + exit 1 +fi + +export JAVADOC_DIR OLD_DIR + +python3 <<'PY' +import datetime +import json +import os +import re +from pathlib import Path + +javadoc_dir = Path(os.environ["JAVADOC_DIR"]) +old_dir = Path(os.environ["OLD_DIR"]) + +def isoformat(timestamp: float) -> str: + return datetime.datetime.fromtimestamp(timestamp, tz=datetime.timezone.utc).isoformat().replace("+00:00", "Z") + +def natural_key(value: str): + return [int(part) if part.isdigit() else part.lower() for part in re.split(r"(\d+)", value)] + +directories = [ + { + "name": path.name, + "href": f"{path.name}/", + "lastModified": isoformat(path.stat().st_mtime), + } + for path in javadoc_dir.iterdir() + if path.is_dir() and not path.name.startswith(".") +] + +directories.sort(key=lambda item: natural_key(item["name"]), reverse=True) + +files = [ + { + "name": path.name, + "href": path.name, + "size": path.stat().st_size, + "lastModified": isoformat(path.stat().st_mtime), + } + for path in old_dir.iterdir() + if path.is_file() and not path.name.startswith(".") +] + +files.sort(key=lambda item: natural_key(item["name"]), reverse=True) + +(javadoc_dir / "manifest.json").write_text( + json.dumps(directories, indent=2) + "\n", encoding="utf-8" +) + +(old_dir / "files.json").write_text( + json.dumps(files, indent=2) + "\n", encoding="utf-8" +) +PY + +echo "Updated ${JAVADOC_DIR}/manifest.json" +echo "Updated ${OLD_DIR}/files.json" diff --git a/site/content/download.md b/site/content/download.md index bb12fd5780..8316ddbe98 100644 --- a/site/content/download.md +++ b/site/content/download.md @@ -5,15 +5,15 @@ toc: true You can either retrieve RDF4J via Apache Maven, or download the SDK or onejar directly. -## RDF4J 5.1.6 (latest) +## RDF4J 5.2.0 (latest) -RDF4J 5.1.6 is our latest stable release. It requires Java 11 minimally. -For details on what’s new and how to upgrade, see the [release and upgrade notes](/release-notes/5.1.6). +RDF4J 5.2.0 is our latest stable release. It requires Java 11 minimally. +For details on what’s new and how to upgrade, see the [release and upgrade notes](/release-notes/5.2.0). -- [RDF4J 5.1.6 SDK (zip)](http://www.eclipse.org/downloads/download.php?file=/rdf4j/eclipse-rdf4j-5.1.6-sdk.zip)
+- [RDF4J 5.2.0 SDK (zip)](http://www.eclipse.org/downloads/download.php?file=/rdf4j/eclipse-rdf4j-5.2.0-sdk.zip)
Full Eclipse RDF4J SDK, containing all libraries, RDF4J Server, Workbench, and Console applications, and Javadoc API. -- [RDF4J 5.1.6 onejar](http://www.eclipse.org/downloads/download.php?file=/rdf4j/eclipse-rdf4j-5.1.6-onejar.jar)
+- [RDF4J 5.2.0 onejar](http://www.eclipse.org/downloads/download.php?file=/rdf4j/eclipse-rdf4j-5.2.0-onejar.jar)
Single jar file for easy inclusion of the full RDF4J toolkit in your Java project. - [RDF4J artifacts](https://search.maven.org/search?q=org.eclipse.rdf4j) on the [Maven Central Repository](http://search.maven.org/) @@ -28,7 +28,7 @@ You can include RDF4J as a Maven dependency in your Java project by including th org.eclipse.rdf4j rdf4j-bom - 5.1.6 + 5.2.0 pom import @@ -52,6 +52,11 @@ which artifacts RDF4J provides. ## Older releases +### RDF4J 5.1 + +- [RDF4J 5.1.6 SDK (zip)](http://www.eclipse.org/downloads/download.php?file=/rdf4j/eclipse-rdf4j-5.1.6-sdk.zip) +- [RDF4J 5.1.6 onejar](http://www.eclipse.org/downloads/download.php?file=/rdf4j/eclipse-rdf4j-5.1.6-onejar.jar) + ### RDF4J 5.0 - [RDF4J 5.0.3 SDK (zip)](http://www.eclipse.org/downloads/download.php?file=/rdf4j/eclipse-rdf4j-5.0.3-sdk.zip) diff --git a/site/content/news/rdf4j-520.md b/site/content/news/rdf4j-520.md new file mode 100644 index 0000000000..53faaf27f4 --- /dev/null +++ b/site/content/news/rdf4j-520.md @@ -0,0 +1,14 @@ +--- +title: "RDF4J 5.2.0 released" +date: 2025-10-05T16:48:42+0200 +layout: "single" +categories: ["news"] +--- +RDF4J 5.2.0 is now available. This is a minor release with several small and large improvements. + +For more details, have a look at the [release notes](/release-notes/5.2.0). + +### Links + +- [Download RDF4J](/download/) +- [release notes](/release-notes/5.2.0). diff --git a/site/content/release-notes/5.2.0.md b/site/content/release-notes/5.2.0.md new file mode 100644 index 0000000000..b55025072e --- /dev/null +++ b/site/content/release-notes/5.2.0.md @@ -0,0 +1,26 @@ +--- +title: "5.2.0" +toc: true +--- +RDF4J 5.2.0 is a minor release that fixes 9 issues. + +### Highlights +* Explain plans over HTTP. You can now request EXPLAIN via the REST API, making it easier to diagnose and tune slow SPARQL queries. +* SPARQL queries using MINUS and OPTIONAL clauses are significantly faster. +* Performance improvements to the LMDB Store, reducing query time by an average of 50% and reducing overhead for concurrent queries. +* Configurable LMDB GC. A new valueEvictionInterval lets you tune (or disable) LMDB value‑cache garbage collection to balance latency vs. memory. + +### Other improvements +* Higher bulk‑ingest throughput for Sails that build on AbstractSailConnection thanks to an experimental bulk‑add hook. +* SPARQL UPDATE operations now show up in the query/update logs. + +### Bug fixes +* Geospatial accuracy (Lucene). Filters like ehContains now correctly exclude geometries outside the requested area when a resource has multiple shapes. +* Namespace operations on unknown repositories now return 404 instead of 500. +* The SPARQL parser now rejects invalid CONSTRUCT templates that contain property paths, with a clear error. + +For a complete overview, see [all issues fixed in 5.2.0](https://github.com/eclipse/rdf4j/milestone/115?closed=1). + +### Acknowledgements + +This release was made possible by contributions from [Antoine Willerval](https://github.com/ate47), [Benji Herber](https://github.com/benherber), [Håvard M. Ottestad](https://github.com/hmottestad), [kenwenzel](https://github.com/kenwenzel), [Linn Aung](https://github.com/linnaung) and [Chengxu Bian](https://github.com/odysa). diff --git a/site/static/javadoc/5.1.5.tgz b/site/static/javadoc/5.1.5.tgz deleted file mode 100644 index 272f9265a7..0000000000 Binary files a/site/static/javadoc/5.1.5.tgz and /dev/null differ diff --git a/site/static/javadoc/5.1.6.tgz b/site/static/javadoc/5.1.6.tgz deleted file mode 100644 index 74da914411..0000000000 Binary files a/site/static/javadoc/5.1.6.tgz and /dev/null differ diff --git a/site/static/javadoc/5.2.0.tar.xz b/site/static/javadoc/5.2.0.tar.xz new file mode 100644 index 0000000000..faf29dac8c Binary files /dev/null and b/site/static/javadoc/5.2.0.tar.xz differ diff --git a/site/static/javadoc/index.html b/site/static/javadoc/index.html new file mode 100644 index 0000000000..f41d670015 --- /dev/null +++ b/site/static/javadoc/index.html @@ -0,0 +1,126 @@ + + + + + RDF4J Javadoc Archives + + + + +
+

RDF4J Javadoc Archives

+

+ Available Javadoc directories are detected automatically. Choose a version below to open the extracted documentation. +

+
    +
  • Loading available directories…
  • +
+

+ Looking for older archive files? See the legacy downloads. +

+
+ + + diff --git a/site/static/javadoc/latest.tar.xz b/site/static/javadoc/latest.tar.xz new file mode 100644 index 0000000000..faf29dac8c Binary files /dev/null and b/site/static/javadoc/latest.tar.xz differ diff --git a/site/static/javadoc/latest.tgz b/site/static/javadoc/latest.tgz deleted file mode 100644 index 74da914411..0000000000 Binary files a/site/static/javadoc/latest.tgz and /dev/null differ diff --git a/site/static/javadoc/manifest.json b/site/static/javadoc/manifest.json new file mode 100644 index 0000000000..a44ea82ff7 --- /dev/null +++ b/site/static/javadoc/manifest.json @@ -0,0 +1,7 @@ +[ + { + "name": "old", + "href": "old/", + "lastModified": "2025-11-02T11:23:57.731915Z" + } +] diff --git a/site/static/javadoc/3.0.4.tgz b/site/static/javadoc/old/3.0.4.tgz similarity index 100% rename from site/static/javadoc/3.0.4.tgz rename to site/static/javadoc/old/3.0.4.tgz diff --git a/site/static/javadoc/3.1.0.tgz b/site/static/javadoc/old/3.1.0.tgz similarity index 100% rename from site/static/javadoc/3.1.0.tgz rename to site/static/javadoc/old/3.1.0.tgz diff --git a/site/static/javadoc/3.2.0.tgz b/site/static/javadoc/old/3.2.0.tgz similarity index 100% rename from site/static/javadoc/3.2.0.tgz rename to site/static/javadoc/old/3.2.0.tgz diff --git a/site/static/javadoc/3.3.0.tgz b/site/static/javadoc/old/3.3.0.tgz similarity index 100% rename from site/static/javadoc/3.3.0.tgz rename to site/static/javadoc/old/3.3.0.tgz diff --git a/site/static/javadoc/3.4.3.tgz b/site/static/javadoc/old/3.4.3.tgz similarity index 100% rename from site/static/javadoc/3.4.3.tgz rename to site/static/javadoc/old/3.4.3.tgz diff --git a/site/static/javadoc/3.5.0.tgz b/site/static/javadoc/old/3.5.0.tgz similarity index 100% rename from site/static/javadoc/3.5.0.tgz rename to site/static/javadoc/old/3.5.0.tgz diff --git a/site/static/javadoc/3.6.0.tgz b/site/static/javadoc/old/3.6.0.tgz similarity index 100% rename from site/static/javadoc/3.6.0.tgz rename to site/static/javadoc/old/3.6.0.tgz diff --git a/site/static/javadoc/3.6.3.tgz b/site/static/javadoc/old/3.6.3.tgz similarity index 100% rename from site/static/javadoc/3.6.3.tgz rename to site/static/javadoc/old/3.6.3.tgz diff --git a/site/static/javadoc/4.0.0-M2.tgz b/site/static/javadoc/old/4.0.0-M2.tgz similarity index 100% rename from site/static/javadoc/4.0.0-M2.tgz rename to site/static/javadoc/old/4.0.0-M2.tgz diff --git a/site/static/javadoc/4.0.0-M3.tgz b/site/static/javadoc/old/4.0.0-M3.tgz similarity index 100% rename from site/static/javadoc/4.0.0-M3.tgz rename to site/static/javadoc/old/4.0.0-M3.tgz diff --git a/site/static/javadoc/4.0.0.tgz b/site/static/javadoc/old/4.0.0.tgz similarity index 100% rename from site/static/javadoc/4.0.0.tgz rename to site/static/javadoc/old/4.0.0.tgz diff --git a/site/static/javadoc/4.0.1.tgz b/site/static/javadoc/old/4.0.1.tgz similarity index 100% rename from site/static/javadoc/4.0.1.tgz rename to site/static/javadoc/old/4.0.1.tgz diff --git a/site/static/javadoc/4.0.2.tgz b/site/static/javadoc/old/4.0.2.tgz similarity index 100% rename from site/static/javadoc/4.0.2.tgz rename to site/static/javadoc/old/4.0.2.tgz diff --git a/site/static/javadoc/4.0.3.tgz b/site/static/javadoc/old/4.0.3.tgz similarity index 100% rename from site/static/javadoc/4.0.3.tgz rename to site/static/javadoc/old/4.0.3.tgz diff --git a/site/static/javadoc/4.0.4.tgz b/site/static/javadoc/old/4.0.4.tgz similarity index 100% rename from site/static/javadoc/4.0.4.tgz rename to site/static/javadoc/old/4.0.4.tgz diff --git a/site/static/javadoc/4.1.0-M1.tgz b/site/static/javadoc/old/4.1.0-M1.tgz similarity index 100% rename from site/static/javadoc/4.1.0-M1.tgz rename to site/static/javadoc/old/4.1.0-M1.tgz diff --git a/site/static/javadoc/4.1.0.tgz b/site/static/javadoc/old/4.1.0.tgz similarity index 100% rename from site/static/javadoc/4.1.0.tgz rename to site/static/javadoc/old/4.1.0.tgz diff --git a/site/static/javadoc/4.1.1.tgz b/site/static/javadoc/old/4.1.1.tgz similarity index 100% rename from site/static/javadoc/4.1.1.tgz rename to site/static/javadoc/old/4.1.1.tgz diff --git a/site/static/javadoc/4.1.2.tgz b/site/static/javadoc/old/4.1.2.tgz similarity index 100% rename from site/static/javadoc/4.1.2.tgz rename to site/static/javadoc/old/4.1.2.tgz diff --git a/site/static/javadoc/4.1.3.tgz b/site/static/javadoc/old/4.1.3.tgz similarity index 100% rename from site/static/javadoc/4.1.3.tgz rename to site/static/javadoc/old/4.1.3.tgz diff --git a/site/static/javadoc/4.2.0.tgz b/site/static/javadoc/old/4.2.0.tgz similarity index 100% rename from site/static/javadoc/4.2.0.tgz rename to site/static/javadoc/old/4.2.0.tgz diff --git a/site/static/javadoc/4.2.1.tgz b/site/static/javadoc/old/4.2.1.tgz similarity index 100% rename from site/static/javadoc/4.2.1.tgz rename to site/static/javadoc/old/4.2.1.tgz diff --git a/site/static/javadoc/4.2.2.tgz b/site/static/javadoc/old/4.2.2.tgz similarity index 100% rename from site/static/javadoc/4.2.2.tgz rename to site/static/javadoc/old/4.2.2.tgz diff --git a/site/static/javadoc/4.2.3.tgz b/site/static/javadoc/old/4.2.3.tgz similarity index 100% rename from site/static/javadoc/4.2.3.tgz rename to site/static/javadoc/old/4.2.3.tgz diff --git a/site/static/javadoc/4.2.4.tgz b/site/static/javadoc/old/4.2.4.tgz similarity index 100% rename from site/static/javadoc/4.2.4.tgz rename to site/static/javadoc/old/4.2.4.tgz diff --git a/site/static/javadoc/4.3.0.tgz b/site/static/javadoc/old/4.3.0.tgz similarity index 100% rename from site/static/javadoc/4.3.0.tgz rename to site/static/javadoc/old/4.3.0.tgz diff --git a/site/static/javadoc/4.3.1.tgz b/site/static/javadoc/old/4.3.1.tgz similarity index 100% rename from site/static/javadoc/4.3.1.tgz rename to site/static/javadoc/old/4.3.1.tgz diff --git a/site/static/javadoc/4.3.10.tgz b/site/static/javadoc/old/4.3.10.tgz similarity index 100% rename from site/static/javadoc/4.3.10.tgz rename to site/static/javadoc/old/4.3.10.tgz diff --git a/site/static/javadoc/4.3.11.tgz b/site/static/javadoc/old/4.3.11.tgz similarity index 100% rename from site/static/javadoc/4.3.11.tgz rename to site/static/javadoc/old/4.3.11.tgz diff --git a/site/static/javadoc/4.3.12.tgz b/site/static/javadoc/old/4.3.12.tgz similarity index 100% rename from site/static/javadoc/4.3.12.tgz rename to site/static/javadoc/old/4.3.12.tgz diff --git a/site/static/javadoc/4.3.13.tgz b/site/static/javadoc/old/4.3.13.tgz similarity index 100% rename from site/static/javadoc/4.3.13.tgz rename to site/static/javadoc/old/4.3.13.tgz diff --git a/site/static/javadoc/4.3.14.tgz b/site/static/javadoc/old/4.3.14.tgz similarity index 100% rename from site/static/javadoc/4.3.14.tgz rename to site/static/javadoc/old/4.3.14.tgz diff --git a/site/static/javadoc/4.3.15.tgz b/site/static/javadoc/old/4.3.15.tgz similarity index 100% rename from site/static/javadoc/4.3.15.tgz rename to site/static/javadoc/old/4.3.15.tgz diff --git a/site/static/javadoc/4.3.2.tgz b/site/static/javadoc/old/4.3.2.tgz similarity index 100% rename from site/static/javadoc/4.3.2.tgz rename to site/static/javadoc/old/4.3.2.tgz diff --git a/site/static/javadoc/4.3.3.tgz b/site/static/javadoc/old/4.3.3.tgz similarity index 100% rename from site/static/javadoc/4.3.3.tgz rename to site/static/javadoc/old/4.3.3.tgz diff --git a/site/static/javadoc/4.3.4.tgz b/site/static/javadoc/old/4.3.4.tgz similarity index 100% rename from site/static/javadoc/4.3.4.tgz rename to site/static/javadoc/old/4.3.4.tgz diff --git a/site/static/javadoc/4.3.5.tgz b/site/static/javadoc/old/4.3.5.tgz similarity index 100% rename from site/static/javadoc/4.3.5.tgz rename to site/static/javadoc/old/4.3.5.tgz diff --git a/site/static/javadoc/4.3.6.tgz b/site/static/javadoc/old/4.3.6.tgz similarity index 100% rename from site/static/javadoc/4.3.6.tgz rename to site/static/javadoc/old/4.3.6.tgz diff --git a/site/static/javadoc/4.3.7.tgz b/site/static/javadoc/old/4.3.7.tgz similarity index 100% rename from site/static/javadoc/4.3.7.tgz rename to site/static/javadoc/old/4.3.7.tgz diff --git a/site/static/javadoc/4.3.8.tgz b/site/static/javadoc/old/4.3.8.tgz similarity index 100% rename from site/static/javadoc/4.3.8.tgz rename to site/static/javadoc/old/4.3.8.tgz diff --git a/site/static/javadoc/4.3.9.tgz b/site/static/javadoc/old/4.3.9.tgz similarity index 100% rename from site/static/javadoc/4.3.9.tgz rename to site/static/javadoc/old/4.3.9.tgz diff --git a/site/static/javadoc/5.0.0-M1.tgz b/site/static/javadoc/old/5.0.0-M1.tgz similarity index 100% rename from site/static/javadoc/5.0.0-M1.tgz rename to site/static/javadoc/old/5.0.0-M1.tgz diff --git a/site/static/javadoc/5.0.0-M2.tgz b/site/static/javadoc/old/5.0.0-M2.tgz similarity index 100% rename from site/static/javadoc/5.0.0-M2.tgz rename to site/static/javadoc/old/5.0.0-M2.tgz diff --git a/site/static/javadoc/5.0.0-M3.tgz b/site/static/javadoc/old/5.0.0-M3.tgz similarity index 100% rename from site/static/javadoc/5.0.0-M3.tgz rename to site/static/javadoc/old/5.0.0-M3.tgz diff --git a/site/static/javadoc/5.0.0.tgz b/site/static/javadoc/old/5.0.0.tgz similarity index 100% rename from site/static/javadoc/5.0.0.tgz rename to site/static/javadoc/old/5.0.0.tgz diff --git a/site/static/javadoc/5.0.1.tgz b/site/static/javadoc/old/5.0.1.tgz similarity index 100% rename from site/static/javadoc/5.0.1.tgz rename to site/static/javadoc/old/5.0.1.tgz diff --git a/site/static/javadoc/5.0.2.tgz b/site/static/javadoc/old/5.0.2.tgz similarity index 100% rename from site/static/javadoc/5.0.2.tgz rename to site/static/javadoc/old/5.0.2.tgz diff --git a/site/static/javadoc/5.1.0-M1.tgz b/site/static/javadoc/old/5.1.0-M1.tgz similarity index 100% rename from site/static/javadoc/5.1.0-M1.tgz rename to site/static/javadoc/old/5.1.0-M1.tgz diff --git a/site/static/javadoc/5.1.0.tgz b/site/static/javadoc/old/5.1.0.tgz similarity index 100% rename from site/static/javadoc/5.1.0.tgz rename to site/static/javadoc/old/5.1.0.tgz diff --git a/site/static/javadoc/5.1.1.tgz b/site/static/javadoc/old/5.1.1.tgz similarity index 100% rename from site/static/javadoc/5.1.1.tgz rename to site/static/javadoc/old/5.1.1.tgz diff --git a/site/static/javadoc/5.1.2.tgz b/site/static/javadoc/old/5.1.2.tgz similarity index 100% rename from site/static/javadoc/5.1.2.tgz rename to site/static/javadoc/old/5.1.2.tgz diff --git a/site/static/javadoc/old/files.json b/site/static/javadoc/old/files.json new file mode 100644 index 0000000000..b85c81789e --- /dev/null +++ b/site/static/javadoc/old/files.json @@ -0,0 +1,308 @@ +[ + { + "name": "5.1.2.tgz", + "href": "5.1.2.tgz", + "size": 12880093, + "lastModified": "2025-11-02T11:23:57.733154Z" + }, + { + "name": "5.1.1.tgz", + "href": "5.1.1.tgz", + "size": 12879603, + "lastModified": "2025-11-02T11:23:57.689839Z" + }, + { + "name": "5.1.0.tgz", + "href": "5.1.0.tgz", + "size": 8373660, + "lastModified": "2025-11-02T11:23:57.651304Z" + }, + { + "name": "5.1.0-M1.tgz", + "href": "5.1.0-M1.tgz", + "size": 8347155, + "lastModified": "2025-11-02T11:23:57.621344Z" + }, + { + "name": "5.0.2.tgz", + "href": "5.0.2.tgz", + "size": 7373982, + "lastModified": "2025-11-02T11:23:57.584732Z" + }, + { + "name": "5.0.1.tgz", + "href": "5.0.1.tgz", + "size": 7375279, + "lastModified": "2025-11-02T11:23:57.559888Z" + }, + { + "name": "5.0.0.tgz", + "href": "5.0.0.tgz", + "size": 7372556, + "lastModified": "2025-11-02T11:23:57.535421Z" + }, + { + "name": "5.0.0-M3.tgz", + "href": "5.0.0-M3.tgz", + "size": 7370351, + "lastModified": "2025-11-02T11:23:57.510946Z" + }, + { + "name": "5.0.0-M2.tgz", + "href": "5.0.0-M2.tgz", + "size": 7186122, + "lastModified": "2025-11-02T11:23:57.477138Z" + }, + { + "name": "5.0.0-M1.tgz", + "href": "5.0.0-M1.tgz", + "size": 7283857, + "lastModified": "2025-11-02T11:23:57.451740Z" + }, + { + "name": "4.3.15.tgz", + "href": "4.3.15.tgz", + "size": 8534757, + "lastModified": "2025-11-02T11:23:57.198581Z" + }, + { + "name": "4.3.14.tgz", + "href": "4.3.14.tgz", + "size": 8535562, + "lastModified": "2025-11-02T11:23:57.169279Z" + }, + { + "name": "4.3.13.tgz", + "href": "4.3.13.tgz", + "size": 7645239, + "lastModified": "2025-11-02T11:23:57.131255Z" + }, + { + "name": "4.3.12.tgz", + "href": "4.3.12.tgz", + "size": 7643334, + "lastModified": "2025-11-02T11:23:57.104730Z" + }, + { + "name": "4.3.11.tgz", + "href": "4.3.11.tgz", + "size": 7610088, + "lastModified": "2025-11-02T11:23:57.078027Z" + }, + { + "name": "4.3.10.tgz", + "href": "4.3.10.tgz", + "size": 7609077, + "lastModified": "2025-11-02T11:23:57.051777Z" + }, + { + "name": "4.3.9.tgz", + "href": "4.3.9.tgz", + "size": 7610480, + "lastModified": "2025-11-02T11:23:57.426590Z" + }, + { + "name": "4.3.8.tgz", + "href": "4.3.8.tgz", + "size": 7613057, + "lastModified": "2025-11-02T11:23:57.400210Z" + }, + { + "name": "4.3.7.tgz", + "href": "4.3.7.tgz", + "size": 7611841, + "lastModified": "2025-11-02T11:23:57.366652Z" + }, + { + "name": "4.3.6.tgz", + "href": "4.3.6.tgz", + "size": 7610450, + "lastModified": "2025-11-02T11:23:57.339767Z" + }, + { + "name": "4.3.5.tgz", + "href": "4.3.5.tgz", + "size": 7605316, + "lastModified": "2025-11-02T11:23:57.313006Z" + }, + { + "name": "4.3.4.tgz", + "href": "4.3.4.tgz", + "size": 7603347, + "lastModified": "2025-11-02T11:23:57.286265Z" + }, + { + "name": "4.3.3.tgz", + "href": "4.3.3.tgz", + "size": 7604064, + "lastModified": "2025-11-02T11:23:57.252664Z" + }, + { + "name": "4.3.2.tgz", + "href": "4.3.2.tgz", + "size": 8396965, + "lastModified": "2025-11-02T11:23:57.226887Z" + }, + { + "name": "4.3.1.tgz", + "href": "4.3.1.tgz", + "size": 7602676, + "lastModified": "2025-11-02T11:23:57.017352Z" + }, + { + "name": "4.3.0.tgz", + "href": "4.3.0.tgz", + "size": 7608870, + "lastModified": "2025-11-02T11:23:56.991778Z" + }, + { + "name": "4.2.4.tgz", + "href": "4.2.4.tgz", + "size": 8395634, + "lastModified": "2025-11-02T11:23:56.966494Z" + }, + { + "name": "4.2.3.tgz", + "href": "4.2.3.tgz", + "size": 7591427, + "lastModified": "2025-11-02T11:23:56.930356Z" + }, + { + "name": "4.2.2.tgz", + "href": "4.2.2.tgz", + "size": 7586531, + "lastModified": "2025-11-02T11:23:56.904933Z" + }, + { + "name": "4.2.1.tgz", + "href": "4.2.1.tgz", + "size": 7651298, + "lastModified": "2025-11-02T11:23:56.878734Z" + }, + { + "name": "4.2.0.tgz", + "href": "4.2.0.tgz", + "size": 7645693, + "lastModified": "2025-11-02T11:23:56.853637Z" + }, + { + "name": "4.1.3.tgz", + "href": "4.1.3.tgz", + "size": 7595213, + "lastModified": "2025-11-02T11:23:56.819525Z" + }, + { + "name": "4.1.2.tgz", + "href": "4.1.2.tgz", + "size": 7595246, + "lastModified": "2025-11-02T11:23:56.793472Z" + }, + { + "name": "4.1.1.tgz", + "href": "4.1.1.tgz", + "size": 7584726, + "lastModified": "2025-11-02T11:23:56.766398Z" + }, + { + "name": "4.1.0.tgz", + "href": "4.1.0.tgz", + "size": 7577048, + "lastModified": "2025-11-02T11:23:56.740131Z" + }, + { + "name": "4.1.0-M1.tgz", + "href": "4.1.0-M1.tgz", + "size": 7551349, + "lastModified": "2025-11-02T11:23:56.704563Z" + }, + { + "name": "4.0.4.tgz", + "href": "4.0.4.tgz", + "size": 7433683, + "lastModified": "2025-11-02T11:23:56.678166Z" + }, + { + "name": "4.0.3.tgz", + "href": "4.0.3.tgz", + "size": 7432236, + "lastModified": "2025-11-02T11:23:56.652027Z" + }, + { + "name": "4.0.2.tgz", + "href": "4.0.2.tgz", + "size": 7249038, + "lastModified": "2025-11-02T11:23:56.625635Z" + }, + { + "name": "4.0.1.tgz", + "href": "4.0.1.tgz", + "size": 7222832, + "lastModified": "2025-11-02T11:23:56.593105Z" + }, + { + "name": "4.0.0.tgz", + "href": "4.0.0.tgz", + "size": 7213862, + "lastModified": "2025-11-02T11:23:56.568175Z" + }, + { + "name": "4.0.0-M3.tgz", + "href": "4.0.0-M3.tgz", + "size": 7213039, + "lastModified": "2025-11-02T11:23:56.543167Z" + }, + { + "name": "4.0.0-M2.tgz", + "href": "4.0.0-M2.tgz", + "size": 6986103, + "lastModified": "2025-11-02T11:23:56.517639Z" + }, + { + "name": "3.6.3.tgz", + "href": "3.6.3.tgz", + "size": 6483996, + "lastModified": "2025-11-02T11:23:56.486378Z" + }, + { + "name": "3.6.0.tgz", + "href": "3.6.0.tgz", + "size": 6453661, + "lastModified": "2025-11-02T11:23:56.463273Z" + }, + { + "name": "3.5.0.tgz", + "href": "3.5.0.tgz", + "size": 6520344, + "lastModified": "2025-11-02T11:23:56.440281Z" + }, + { + "name": "3.4.3.tgz", + "href": "3.4.3.tgz", + "size": 6378669, + "lastModified": "2025-11-02T11:23:56.416077Z" + }, + { + "name": "3.3.0.tgz", + "href": "3.3.0.tgz", + "size": 6351007, + "lastModified": "2025-11-02T11:23:56.381962Z" + }, + { + "name": "3.2.0.tgz", + "href": "3.2.0.tgz", + "size": 6243121, + "lastModified": "2025-11-02T11:23:56.358226Z" + }, + { + "name": "3.1.0.tgz", + "href": "3.1.0.tgz", + "size": 5906762, + "lastModified": "2025-11-02T11:23:56.334012Z" + }, + { + "name": "3.0.4.tgz", + "href": "3.0.4.tgz", + "size": 5063374, + "lastModified": "2025-11-02T11:23:56.311946Z" + } +] diff --git a/site/static/javadoc/old/index.html b/site/static/javadoc/old/index.html new file mode 100644 index 0000000000..79dd836fcd --- /dev/null +++ b/site/static/javadoc/old/index.html @@ -0,0 +1,144 @@ + + + + + Legacy RDF4J Javadoc Archives + + + + +
+

Legacy RDF4J Javadoc Archives

+

+ Downloadable Javadoc archives for older RDF4J releases are listed below. +

+
    +
  • Loading archived files…
  • +
+

+ Return to the current Javadoc directories. +

+
+ + + diff --git a/site/static/javadoc/uncompress.sh b/site/static/javadoc/uncompress.sh index b9425ec80c..b17943aacc 100755 --- a/site/static/javadoc/uncompress.sh +++ b/site/static/javadoc/uncompress.sh @@ -1,16 +1,84 @@ #!/bin/bash -for path in ./*.tgz; do - archive=$(basename -- "$path") - dirname="${archive%.*}" - if test -d "$dirname"; then - if [ "$dirname" -ot "$archive" ]; then - tar xzf $archive -C $dirname - fi - else - mkdir $dirname - tar xzf $archive -C $dirname - fi - touch $dirname +set -o pipefail + +determine_target_dir() { + local archive_name="$1" + + case "${archive_name}" in + *.tar.xz) printf '%s\n' "${archive_name%.tar.xz}" ;; + *.tar.zst) printf '%s\n' "${archive_name%.tar.zst}" ;; + *.tar.gz) printf '%s\n' "${archive_name%.tar.gz}" ;; + *.tgz) printf '%s\n' "${archive_name%.tgz}" ;; + *) printf '%s\n' "${archive_name%.*}" ;; + esac +} + +extract_archive() { + local archive_path="$1" + local target_dir="$2" + local archive_name + + archive_name="$(basename -- "${archive_path}")" + + case "${archive_name}" in + *.tar.xz) + tar -xJf "${archive_path}" -C "${target_dir}" + ;; + *.tar.zst) + if command -v unzstd >/dev/null 2>&1; then + tar --use-compress-program=unzstd -xf "${archive_path}" -C "${target_dir}" + else + if ! command -v zstd >/dev/null 2>&1; then + echo "Required 'zstd' binary not found." >&2 + return 1 + fi + if ! zstd -d --stdout "${archive_path}" | tar -xf - -C "${target_dir}"; then + echo "Failed to extract '${archive_name}' with zstd." >&2 + return 1 + fi + fi + ;; + *.tar.gz|*.tgz) + tar -xzf "${archive_path}" -C "${target_dir}" + ;; + *) + echo "Unsupported archive '${archive_name}'." >&2 + return 1 + ;; + esac +} + +for path in ./*.tar.xz ./*.tar.zst ./*.tar.gz ./*.tgz; do + [ -e "${path}" ] || continue + + archive="$(basename -- "${path}")" + dirname="$(determine_target_dir "${archive}")" + + should_extract=false + if [ ! -d "${dirname}" ]; then + mkdir -p "${dirname}" + should_extract=true + elif [ "${dirname}" -ot "${archive}" ]; then + should_extract=true + fi + + if "${should_extract}"; then + if ! extract_archive "${path}" "${dirname}"; then + echo "Failed to extract '${archive}'." >&2 + exit 1 + fi + fi + + touch "${dirname}" done +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +repo_root="$(cd "${script_dir}/../../.." && pwd)" +update_script="${repo_root}/scripts/update-javadoc-indexes.sh" + +if [ -x "${update_script}" ]; then + "${update_script}" +else + echo "Skipping manifest update; helper script not found at ${update_script}." >&2 +fi