Compare commits
17
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e9ffba484e | ||
|
|
e292a14819
|
||
|
|
66f10bc238
|
||
|
|
dc16888dc4 | ||
|
|
889296d8f5
|
||
|
|
e085859a25
|
||
|
|
4f850c2cf3
|
||
|
|
f9adf1d3d3 | ||
|
|
2dcee6fbea
|
||
|
|
34a3666bbf | ||
|
|
6a3153456e | ||
|
|
9073e9bf1c | ||
|
|
98eef612d0
|
||
|
|
cb420158ba
|
||
|
|
10b3a716b5
|
||
|
|
207e3ce034 | ||
|
|
063b1e8136
|
No files matched your search
@@ -3,5 +3,5 @@ on:
|
||||
push:
|
||||
jobs:
|
||||
maven:
|
||||
uses: self:KofTwentyTwo/ci-workflows/.gitea/workflows/maven.yaml@main
|
||||
uses: self:KofTwentyTwo/ci-workflows/.gitea/workflows/maven.yaml@v1
|
||||
secrets: inherit
|
||||
@@ -1,6 +1,6 @@
|
||||
# QBit Document Generation
|
||||
|
||||
> **Paused, unreleased work in progress — 2026-11-04.** Implementation has stopped apart from the scoped definition-rename addition of 2026-11-06 (see [status](docs/RELEASE-STATUS.md)). This is a source checkpoint, not a production-ready release or a complete installable package set.
|
||||
> **Version 1.0.** Published to the KOF22 Gitea Maven registry (`com.kingsrook.qbits`). Requires QQQ Core `4.1.0-RC.22`; the bridges need Record Files `0.1.0-RC.1` and Scripting `1.0.0`. See [status](docs/RELEASE-STATUS.md).
|
||||
|
||||
Start with [current status and build prerequisites](docs/RELEASE-STATUS.md) and [known issues](docs/KNOWN-ISSUES.md). These documents supersede older release-readiness statements below.
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
<parent><groupId>com.kingsrook.qbits</groupId><artifactId>qbit-document-generation-reactor</artifactId><version>${revision}</version><relativePath>../../pom.xml</relativePath></parent>
|
||||
<groupId>com.kingsrook.qbits</groupId><artifactId>qbit-document-generation-printing</artifactId><version>${revision}</version>
|
||||
<name>Document Generation Printing Bridge</name>
|
||||
<properties><qqq.version>4.1.0-RC.21</qqq.version><!-- Owner-supplied sibling QBit coordinates (not yet published): set to the released version --><qbit-record-files.version>0.1.0-SNAPSHOT</qbit-record-files.version><maven.compiler.release>21</maven.compiler.release><maven.compiler.proc>none</maven.compiler.proc><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<properties><qqq.version>4.1.0-RC.26</qqq.version><!-- Released sibling QBit coordinates from the Gitea Maven registry --><qbit-record-files.version>0.4.0-RC.1</qbit-record-files.version><maven.compiler.release>21</maven.compiler.release><maven.compiler.proc>none</maven.compiler.proc><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<!-- Released Printing 1.0.0: RetainedPrintFacade.Acceptance carries title/optionsJson and PrintingService enforces options.mediaProfile id+revision. -->
|
||||
<qbit-printing.version>1.0.0</qbit-printing.version><enforcer.skip>true</enforcer.skip></properties>
|
||||
<qbit-printing.version>1.2.0-RC.1</qbit-printing.version><enforcer.skip>true</enforcer.skip></properties>
|
||||
<dependencyManagement><dependencies><dependency><groupId>com.kingsrook.qqq</groupId><artifactId>qqq-bom-pom</artifactId><version>${qqq.version}</version><type>pom</type><scope>import</scope></dependency>
|
||||
<dependency><groupId>com.kingsrook.qqq</groupId><artifactId>qqq-backend-core</artifactId><version>${qqq.version}</version></dependency>
|
||||
<dependency><groupId>com.kingsrook.qqq</groupId><artifactId>qqq-backend-module-rdbms</artifactId><version>${qqq.version}</version></dependency>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent><groupId>com.kingsrook.qbits</groupId><artifactId>qbit-document-generation-reactor</artifactId><version>${revision}</version><relativePath>../../pom.xml</relativePath></parent>
|
||||
<groupId>com.kingsrook.qbits</groupId><artifactId>qbit-document-generation-record-files</artifactId><version>${revision}</version>
|
||||
<name>Document Generation Record Files Bridge</name>
|
||||
<properties><qqq.version>4.1.0-RC.21</qqq.version><!-- Owner-supplied sibling QBit coordinates (not yet published): set to the released version --><qbit-record-files.version>0.1.0-SNAPSHOT</qbit-record-files.version><maven.compiler.release>21</maven.compiler.release><maven.compiler.proc>none</maven.compiler.proc><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><enforcer.skip>true</enforcer.skip></properties>
|
||||
<properties><qqq.version>4.1.0-RC.26</qqq.version><!-- Released sibling QBit coordinates from the Gitea Maven registry --><qbit-record-files.version>0.4.0-RC.1</qbit-record-files.version><maven.compiler.release>21</maven.compiler.release><maven.compiler.proc>none</maven.compiler.proc><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><enforcer.skip>true</enforcer.skip></properties>
|
||||
<dependencyManagement><dependencies><dependency><groupId>com.kingsrook.qqq</groupId><artifactId>qqq-bom-pom</artifactId><version>${qqq.version}</version><type>pom</type><scope>import</scope></dependency>
|
||||
<dependency><groupId>com.kingsrook.qqq</groupId><artifactId>qqq-backend-core</artifactId><version>${qqq.version}</version></dependency>
|
||||
<dependency><groupId>com.kingsrook.qqq</groupId><artifactId>qqq-backend-module-rdbms</artifactId><version>${qqq.version}</version></dependency>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<groupId>com.kingsrook.qbits</groupId><artifactId>qbit-document-generation-scripting</artifactId><version>${revision}</version>
|
||||
<name>Document Generation Scripting Bridge</name>
|
||||
<licenses><license><name>GNU Affero General Public License v3.0</name><url>https://www.gnu.org/licenses/agpl-3.0-standalone.html</url><distribution>repo</distribution></license></licenses>
|
||||
<properties><qqq.version>4.1.0-RC.21</qqq.version><!-- Owner-supplied sibling QBit coordinates (not yet published): set to the released version --><qbit-scripting.version>0.1.0-SNAPSHOT</qbit-scripting.version><maven.compiler.release>21</maven.compiler.release><maven.compiler.proc>none</maven.compiler.proc><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><enforcer.skip>true</enforcer.skip></properties>
|
||||
<properties><qqq.version>4.1.0-RC.26</qqq.version><!-- Released sibling QBit coordinates from the Gitea Maven registry --><qbit-scripting.version>1.1.0-RC.3</qbit-scripting.version><maven.compiler.release>21</maven.compiler.release><maven.compiler.proc>none</maven.compiler.proc><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><enforcer.skip>true</enforcer.skip></properties>
|
||||
<dependencyManagement><dependencies>
|
||||
<dependency><groupId>com.kingsrook.qqq</groupId><artifactId>qqq-backend-core</artifactId><version>${qqq.version}</version></dependency>
|
||||
<dependency><groupId>com.kingsrook.qqq</groupId><artifactId>qqq-backend-module-rdbms</artifactId><version>${qqq.version}</version></dependency>
|
||||
|
||||
@@ -26,7 +26,7 @@ Submitted records provide only IDs. The source body is always fetched afresh thr
|
||||
|
||||
## Database/deployment constraints
|
||||
|
||||
The durable queue supports file-backed H2 with configured and actual WRITE_DELAY=0, or native MySQL 8.4 under the live checks in [MYSQL.md](MYSQL.md), or native PostgreSQL 17 under the equivalent live checks in `GenerationPostgres`. Both require fresh independent writable connections and the owned tables on one backend. Ambient QQQ transactions are refused. Native mapping drift and unqualified physical targets are denied. Owned statements have five-second JDBC deadlines; MySQL row/metadata lock waits must each be 1–5 seconds. H2 tests pin 2.5.252 and close all connection providers before a cold reopen. Actual pinned MySQL 8.4.13 tests cover concurrent claims/edits, physical pooled isolation restoration, weakened durability/settings, index/engine/mapping drift, bounded lock contention and server restart. PostgreSQL/Aurora are not implicitly supported. Schema changes are operator migrations; metadata registration does not execute DDL. Host power-loss and volume/backup restoration remain deployment acceptance work.
|
||||
The durable queue supports file-backed H2 with configured and actual WRITE_DELAY=0, or native MySQL 8.4 under the live checks in [MYSQL.md](MYSQL.md), or native PostgreSQL 17 under the equivalent live checks in `GenerationPostgres`. Both require fresh independent writable connections and the owned tables on one backend. Ambient QQQ transactions are refused. Native mapping drift and unqualified physical targets are denied. Owned statements have five-second JDBC deadlines; MySQL row/metadata lock waits must each be 1–5 seconds. H2 tests pin 2.5.252 and close all connection providers before a cold reopen. Actual pinned MySQL 8.4.13 tests cover concurrent claims/edits, physical pooled isolation restoration, weakened durability/settings, index/engine/mapping drift, bounded lock contention and server restart. Amazon Aurora PostgreSQL 17 passes through the same PostgreSQL checks with one difference: Aurora always runs with `full_page_writes` off and has no parameter to change it, because its storage layer does not write torn pages, so `GenerationPostgres` accepts that value when the server resolves `aurora_version()` (`to_regprocedure('aurora_version()') IS NOT NULL`, evaluated in the settings query). Stock PostgreSQL must still report `full_page_writes` on, and `fsync`, `synchronous_commit` and every other check are unchanged on Aurora. The test for this rule runs on a real `postgres:17` engine with a simulated `aurora_version()` function; no test in this repository runs on Aurora itself. Aurora MySQL is not qualified (see [MYSQL.md](MYSQL.md)). Schema changes are operator migrations; metadata registration does not execute DDL. Host power-loss and volume/backup restoration remain deployment acceptance work.
|
||||
|
||||
No queue/render/file/script code is executed through a trusted host JavaScript VM. The renderer and scripting integrations continue to require qualified Linux/runsc; this workstation has no runsc runtime, so hostile execution remains unavailable. The queue does not weaken those gates.
|
||||
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
# Known issues and unfinished work
|
||||
# Known issues
|
||||
|
||||
Updated: 2026-11-04. These are unresolved items, not a plan currently being executed.
|
||||
|
||||
- Renderer verification has **3 failing tests out of 174**: `BarcodeDeclaredCapabilityTest.barHeightIsAdvertisedAcceptedAndEffectiveTogether`, `gs1DatabarBarHeightDependsOnMode`, and `unsupportedOptionIsAClosedWorkerCode`. The imported main implementation and capability tests disagree; do not hide this by skipping tests or advertising unsupported options.
|
||||
- The Record Files and Scripting bridge dependencies, including their `tests` classifier artifacts, are not available as a completed public release cohort. The complete root reactor is not release-qualified.
|
||||
- Earlier foundation runs hit local socket exhaustion/PostgreSQL connection resets. The final 225-test run passed; the transport failures remain a reproducibility concern.
|
||||
- Renderer image deployment, hostile-template containment, production capacity and physical barcode/printing acceptance are not established by the Maven tests. Source-first templates are implemented; a visual designer is outside this version.
|
||||
- Renderer image deployment, hostile-template containment, production capacity and physical barcode and printing acceptance are not established by the Maven tests. Source-first templates are implemented; a visual designer is outside this version.
|
||||
- Barcode `barHeight` is accepted only where it changes the output. The sixteen fixed-geometry encoders (matrix symbols and postal four-state codes) neither advertise nor accept it and refuse it with `UNSUPPORTED_BARCODE_OPTION`, as does GS1 DataBar in stacked mode.
|
||||
- Native Zint and patched OpenHTML retain their own licenses and corresponding source. The checked-in archives and hash files must ship with the relevant distribution.
|
||||
- Some historical design/verification notes describe earlier private builds. This status and this issue list supersede their release claims.
|
||||
- Some historical design and verification notes describe earlier private builds; this status and this issue list supersede their release claims.
|
||||
@@ -1,6 +1,6 @@
|
||||
# Document Generation — current status
|
||||
|
||||
Updated: 2026-11-06. **Unreleased work in progress; not a production 1.0 release.** The 2026-11-04 stop was lifted only for the definition-rename change below; no other implementation is in progress.
|
||||
**Version 1.0**, built and published by the shared `ci-workflows` Maven pipeline to the KOF22 Gitea Maven registry (`com.kingsrook.qbits`). `mvn deploy` runs every module's tests once. Depends on QQQ Core `4.1.0-RC.22`, Record Files `0.1.0-RC.1` and Scripting `1.0.0`.
|
||||
|
||||
## Purpose
|
||||
|
||||
@@ -20,6 +20,4 @@ Foundation: 225 tests passed with zero failures/errors/skips, Checkstyle clean a
|
||||
|
||||
## Source and release state
|
||||
|
||||
The starting source was extracted from Website-Backend commit b6fb23825a87412397dec703b9617148ae7eb601, then adapted for standalone packaging. This repository preserves the resulting source and existing licenses. It does not declare a published artifact cohort, completed deployment or complete acceptance.
|
||||
|
||||
See [known issues](KNOWN-ISSUES.md) and the README for the existing API and integration documentation. No further implementation, tests, releases or deployment should be started without explicit user instruction; the definition rename was added under such an instruction so a published release can replace GG Forge's local `0.1.0-forge.ee782b7d6ea8-rename1` pin. The outstanding dependency order is Core, Collaboration, Scripting/Record Files, then the integrated Generation bridges and matching UI.
|
||||
The starting source was extracted from Website-Backend commit b6fb23825a87412397dec703b9617148ae7eb601, then adapted for standalone packaging; existing licenses are preserved. See [known issues](KNOWN-ISSUES.md) and the README for the API and integration documentation.
|
||||
+1
-1
@@ -7,7 +7,7 @@
|
||||
<version>${revision}</version>
|
||||
<name>QBit Document Generation</name>
|
||||
<description>User-defined document templates and generation coordination for QQQ</description>
|
||||
<properties><qqq.version>4.1.0-RC.21</qqq.version><maven.compiler.source>21</maven.compiler.source><maven.compiler.target>21</maven.compiler.target><maven.compiler.proc>none</maven.compiler.proc><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><enforcer.skip>false</enforcer.skip></properties>
|
||||
<properties><qqq.version>4.1.0-RC.26</qqq.version><maven.compiler.source>21</maven.compiler.source><maven.compiler.target>21</maven.compiler.target><maven.compiler.proc>none</maven.compiler.proc><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><enforcer.skip>false</enforcer.skip></properties>
|
||||
<licenses><license><name>Apache License, Version 2.0</name><url>https://www.apache.org/licenses/LICENSE-2.0</url><distribution>repo</distribution></license></licenses>
|
||||
<dependencyManagement><dependencies><dependency><groupId>com.kingsrook.qqq</groupId><artifactId>qqq-bom-pom</artifactId><version>${qqq.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>
|
||||
<dependencies>
|
||||
|
||||
+29
-6
@@ -38,7 +38,8 @@ import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSBackendMetaDat
|
||||
/*******************************************************************************
|
||||
** Qualification of the native QQQ PostgreSQL17 backend (vendor and backend type
|
||||
** "postgres") and this QBit's owned tables. Observes the live connection only;
|
||||
** it never changes session or server settings, schema or data.
|
||||
** it never changes session or server settings, schema or data. Amazon Aurora
|
||||
** PostgreSQL is the one accepted variant: see durable().
|
||||
*******************************************************************************/
|
||||
final class GenerationPostgres
|
||||
{
|
||||
@@ -46,7 +47,11 @@ final class GenerationPostgres
|
||||
|
||||
private static final String SETTINGS = "SELECT current_setting('server_version_num')::int, current_setting('transaction_read_only'), pg_is_in_recovery(),"
|
||||
+ " current_setting('synchronous_commit'), current_setting('fsync'), current_setting('full_page_writes'), current_setting('TimeZone'),"
|
||||
+ " (SELECT setting::bigint FROM pg_settings WHERE name='lock_timeout'), current_setting('transaction_isolation'), current_schema(), current_database()";
|
||||
+ " (SELECT setting::bigint FROM pg_settings WHERE name='lock_timeout'), current_setting('transaction_isolation'), current_schema(), current_database(),"
|
||||
+ " to_regprocedure('aurora_version()') IS NOT NULL";
|
||||
|
||||
private static final String REQUIREMENTS = "Generation requires synchronous PostgreSQL commits, full-page writes (except on Amazon Aurora PostgreSQL),"
|
||||
+ " a primary writable server, READ COMMITTED and UTC sessions";
|
||||
|
||||
private static final String TABLE = "SELECT c.relpersistence FROM pg_class c JOIN pg_namespace n ON n.oid=c.relnamespace"
|
||||
+ " WHERE n.nspname=current_schema() AND c.relname=? AND c.relkind='r'";
|
||||
@@ -91,7 +96,25 @@ final class GenerationPostgres
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
** Exact server, durability, session and owned-table checks for PostgreSQL 17.
|
||||
** Commit durability of the observed server. Every server needs
|
||||
** synchronous_commit on or remote_apply and fsync on. Stock PostgreSQL also
|
||||
** needs full_page_writes on, which protects crash recovery from torn pages.
|
||||
** Amazon Aurora PostgreSQL runs with full_page_writes off and offers no
|
||||
** parameter to change it, because its storage layer does not write torn
|
||||
** pages; so off is accepted there, and only there. Aurora is detected in the
|
||||
** settings query by to_regprocedure('aurora_version()') IS NOT NULL, which is
|
||||
** true on Aurora, false on stock PostgreSQL and never raises an error (calling
|
||||
** a missing function would abort the transaction).
|
||||
***************************************************************************/
|
||||
static boolean durable(String synchronousCommit, String fsync, String fullPageWrites, boolean aurora)
|
||||
{
|
||||
return (("on".equals(synchronousCommit) || "remote_apply".equals(synchronousCommit)) && "on".equals(fsync)
|
||||
&& ("on".equals(fullPageWrites) || aurora && "off".equals(fullPageWrites)));
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
** Exact server, durability, session and owned-table checks for PostgreSQL 17,
|
||||
** with the single Amazon Aurora PostgreSQL allowance stated on durable().
|
||||
***************************************************************************/
|
||||
static void qualify(Connection connection, RDBMSBackendMetaData backend) throws SQLException, QException
|
||||
{
|
||||
@@ -105,7 +128,7 @@ final class GenerationPostgres
|
||||
{
|
||||
if(!row.next())
|
||||
{
|
||||
throw new QException("Generation requires synchronous PostgreSQL commits, full-page writes, a primary writable server, READ COMMITTED and UTC sessions.");
|
||||
throw new QException(REQUIREMENTS + ".");
|
||||
}
|
||||
List<String> weak = new java.util.ArrayList<>();
|
||||
if(row.getInt(1) / 10000 != 17 || row.getString(10) == null || !Objects.equals(backend.getDatabaseName(), row.getString(11)))
|
||||
@@ -116,7 +139,7 @@ final class GenerationPostgres
|
||||
{
|
||||
weak.add("writable primary");
|
||||
}
|
||||
if(!Set.of("on", "remote_apply").contains(row.getString(4)) || !"on".equals(row.getString(5)) || !"on".equals(row.getString(6)))
|
||||
if(!durable(row.getString(4), row.getString(5), row.getString(6), row.getBoolean(12)))
|
||||
{
|
||||
weak.add("synchronous commit");
|
||||
}
|
||||
@@ -130,7 +153,7 @@ final class GenerationPostgres
|
||||
}
|
||||
if(!weak.isEmpty())
|
||||
{
|
||||
throw new QException("Generation requires synchronous PostgreSQL commits, full-page writes, a primary writable server, READ COMMITTED and UTC sessions; not satisfied: " + String.join(", ", weak));
|
||||
throw new QException(REQUIREMENTS + "; not satisfied: " + String.join(", ", weak));
|
||||
}
|
||||
long lockTimeout = row.getLong(8);
|
||||
if(lockTimeout < 1 || lockTimeout > 5000)
|
||||
|
||||
+44
-1
@@ -52,13 +52,24 @@ final class PostgresGenerationDatabase implements AutoCloseable
|
||||
** Starts only this suite's loopback container with durable settings.
|
||||
***************************************************************************/
|
||||
PostgresGenerationDatabase()
|
||||
{
|
||||
this(true);
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
** Pinned durability settings come from the server command line, which no
|
||||
** ALTER SYSTEM can override. An unpinned engine starts with the same values
|
||||
** (the PostgreSQL defaults) and lets a test weaken them through system().
|
||||
***************************************************************************/
|
||||
PostgresGenerationDatabase(boolean pinned)
|
||||
{
|
||||
// pgjdbc announces the JVM zone as the session TimeZone; qualification requires UTC.
|
||||
previousZone = java.util.TimeZone.getDefault();
|
||||
java.util.TimeZone.setDefault(java.util.TimeZone.getTimeZone("UTC"));
|
||||
container = new GenericContainer<>(IMAGE)
|
||||
.withEnv("POSTGRES_PASSWORD", rootPassword).withEnv("POSTGRES_DB", "bootstrap")
|
||||
.withCommand("postgres", "-c", "fsync=on", "-c", "synchronous_commit=on", "-c", "full_page_writes=on", "-c", "max_connections=60")
|
||||
.withCommand(pinned ? new String[] { "postgres", "-c", "fsync=on", "-c", "synchronous_commit=on", "-c", "full_page_writes=on", "-c", "max_connections=60" }
|
||||
: new String[] { "postgres", "-c", "max_connections=60" })
|
||||
.withExposedPorts(5432)
|
||||
.withCreateContainerCmdModifier(command -> command.getHostConfig().withMemory(768L * 1024 * 1024).withNanoCPUs(2_000_000_000L)
|
||||
.withPortBindings(FixtureEndpoint.publishedBinding(5432)))
|
||||
@@ -143,6 +154,38 @@ final class PostgresGenerationDatabase implements AutoCloseable
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
** Change one reloadable server setting of an unpinned engine (ALTER SYSTEM and
|
||||
** a configuration reload), then wait until a new session actually reports it.
|
||||
** Fixed fixture names and values only, never application input.
|
||||
***************************************************************************/
|
||||
void system(String name, String value) throws Exception
|
||||
{
|
||||
ConnectionManager.closeConnectionProviders();
|
||||
try(var connection = DriverManager.getConnection(server + "bootstrap" + OPTIONS, "postgres", rootPassword); var statement = connection.createStatement())
|
||||
{
|
||||
statement.execute("ALTER SYSTEM SET " + name + " = '" + value + "'");
|
||||
statement.execute("SELECT pg_reload_conf()");
|
||||
}
|
||||
long deadline = System.nanoTime() + java.time.Duration.ofSeconds(30).toNanos();
|
||||
while(true)
|
||||
{
|
||||
try(var connection = DriverManager.getConnection(server + "bootstrap" + OPTIONS, "postgres", rootPassword); var statement = connection.createStatement();
|
||||
var row = statement.executeQuery("SELECT current_setting('" + name + "')"))
|
||||
{
|
||||
if(row.next() && value.equals(row.getString(1)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(System.nanoTime() >= deadline)
|
||||
{
|
||||
throw new IllegalStateException("PostgreSQL fixture did not apply " + name);
|
||||
}
|
||||
Thread.sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
** Cold server restart retains only this container's data layer (committed
|
||||
** data must survive; Docker may publish a different loopback port).
|
||||
|
||||
+94
-3
@@ -41,6 +41,9 @@ class PostgresGenerationQueueTest extends GenerationQueueJdbcTest
|
||||
{
|
||||
private static PostgresGenerationDatabase postgres;
|
||||
|
||||
// The engine the fixture calls of the running test address: the shared pinned one unless that test started its own.
|
||||
private PostgresGenerationDatabase current;
|
||||
|
||||
/***********************************************************************
|
||||
** One exact disposable engine per suite; all test cases use new databases.
|
||||
***********************************************************************/
|
||||
@@ -68,7 +71,7 @@ class PostgresGenerationQueueTest extends GenerationQueueJdbcTest
|
||||
@Override
|
||||
protected RDBMSBackendMetaData database(String name, String defaultUrl) throws Exception
|
||||
{
|
||||
return (postgres.database(name));
|
||||
return (engine().database(name));
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
@@ -77,7 +80,7 @@ class PostgresGenerationQueueTest extends GenerationQueueJdbcTest
|
||||
@Override
|
||||
protected Connection connection() throws Exception
|
||||
{
|
||||
return (postgres.connection());
|
||||
return (engine().connection());
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
@@ -86,7 +89,15 @@ class PostgresGenerationQueueTest extends GenerationQueueJdbcTest
|
||||
@Override
|
||||
protected void sql(String source) throws Exception
|
||||
{
|
||||
postgres.sql(source);
|
||||
engine().sql(source);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
** The shared pinned engine, or the unpinned one a single test started.
|
||||
***********************************************************************/
|
||||
private PostgresGenerationDatabase engine()
|
||||
{
|
||||
return (current == null ? postgres : current);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
@@ -178,6 +189,86 @@ class PostgresGenerationQueueTest extends GenerationQueueJdbcTest
|
||||
assertThat(queue.status(enqueue("restored"), -1, 10).items()).hasSize(2);
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
** Amazon Aurora PostgreSQL always runs with full_page_writes off (its storage
|
||||
** layer does not write torn pages), so that one setting is accepted there and
|
||||
** nowhere else. The engine is a real PostgreSQL 17 whose settings are really
|
||||
** changed. Only Aurora itself is simulated: a function named aurora_version()
|
||||
** is created in the test database, so the same to_regprocedure detection that
|
||||
** is true on Aurora resolves here.
|
||||
***************************************************************************/
|
||||
@Test
|
||||
void fullPageWritesOffIsAcceptedOnlyOnAuroraAndNothingElseIsRelaxed() throws Exception
|
||||
{
|
||||
current = new PostgresGenerationDatabase(false);
|
||||
try
|
||||
{
|
||||
// Rebuild the instance, schema and definitions on the unpinned engine (durable defaults, so this qualifies).
|
||||
cleanup();
|
||||
setup();
|
||||
assertThat(queue.advance(enqueue("stock-durable")).state()).isEqualTo("GENERATED");
|
||||
|
||||
// Stock PostgreSQL with full_page_writes off is refused, and the refusal states the rule.
|
||||
current.system("full_page_writes", "off");
|
||||
assertThatThrownBy(() -> enqueue("stock-torn-pages")).isInstanceOf(QException.class)
|
||||
.hasMessageContaining("full-page writes (except on Amazon Aurora PostgreSQL)").hasMessageContaining("not satisfied: synchronous commit");
|
||||
|
||||
// The same server, now resolving aurora_version(), is accepted and work really commits.
|
||||
sql("CREATE FUNCTION aurora_version() RETURNS text LANGUAGE sql IMMUTABLE AS $$ SELECT '17.9'::text $$");
|
||||
ConnectionManager.closeConnectionProviders();
|
||||
assertThat(queue.advance(enqueue("aurora")).state()).isEqualTo("GENERATED");
|
||||
|
||||
// Aurora relaxes nothing else: fsync off is refused.
|
||||
current.system("fsync", "off");
|
||||
assertThatThrownBy(() -> enqueue("aurora-no-fsync")).isInstanceOf(QException.class).hasMessageContaining("not satisfied: synchronous commit");
|
||||
current.system("fsync", "on");
|
||||
|
||||
// Aurora relaxes nothing else: asynchronous commit is refused.
|
||||
sql("ALTER ROLE generation_app SET synchronous_commit='off'");
|
||||
ConnectionManager.closeConnectionProviders();
|
||||
assertThatThrownBy(() -> enqueue("aurora-async-commit")).isInstanceOf(QException.class).hasMessageContaining("not satisfied: synchronous commit");
|
||||
sql("ALTER ROLE generation_app SET synchronous_commit='on'");
|
||||
ConnectionManager.closeConnectionProviders();
|
||||
assertThat(enqueue("aurora-restored")).isNotNull();
|
||||
|
||||
// Without the function the server is stock PostgreSQL again and is refused again.
|
||||
sql("DROP FUNCTION aurora_version()");
|
||||
ConnectionManager.closeConnectionProviders();
|
||||
assertThatThrownBy(() -> enqueue("stock-again")).isInstanceOf(QException.class).hasMessageContaining("not satisfied: synchronous commit");
|
||||
}
|
||||
finally
|
||||
{
|
||||
ConnectionManager.closeConnectionProviders();
|
||||
current.close();
|
||||
current = null;
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
** The durability decision itself, for the combinations the live case does
|
||||
** not reach: no server and nothing simulated, only the values of one row.
|
||||
***************************************************************************/
|
||||
@Test
|
||||
void durabilityRuleAcceptsOnlyTheAuroraFullPageWritesException()
|
||||
{
|
||||
assertThat(GenerationPostgres.durable("on", "on", "on", false)).isTrue();
|
||||
assertThat(GenerationPostgres.durable("remote_apply", "on", "on", false)).isTrue();
|
||||
assertThat(GenerationPostgres.durable("on", "on", "on", true)).isTrue();
|
||||
assertThat(GenerationPostgres.durable("on", "on", "off", true)).isTrue();
|
||||
assertThat(GenerationPostgres.durable("remote_apply", "on", "off", true)).isTrue();
|
||||
assertThat(GenerationPostgres.durable("on", "on", "off", false)).isFalse();
|
||||
assertThat(GenerationPostgres.durable("on", "off", "on", false)).isFalse();
|
||||
assertThat(GenerationPostgres.durable("on", "off", "off", true)).isFalse();
|
||||
for(String weak : List.of("off", "local", "remote_write"))
|
||||
{
|
||||
assertThat(GenerationPostgres.durable(weak, "on", "on", false)).isFalse();
|
||||
assertThat(GenerationPostgres.durable(weak, "on", "off", true)).isFalse();
|
||||
}
|
||||
assertThat(GenerationPostgres.durable(null, "on", "on", true)).isFalse();
|
||||
assertThat(GenerationPostgres.durable("on", null, "on", true)).isFalse();
|
||||
assertThat(GenerationPostgres.durable("on", "on", null, true)).isFalse();
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
** The native identity must be the postgres module on a real PostgreSQL17 server, not a vendor label.
|
||||
***************************************************************************/
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
no -Drevision, so a module that declared its own revision would stay at the SNAPSHOT. The root takes qbit-build-parent 2.1.1 (the version foundation already used);
|
||||
modules that had no parent now inherit its defaults and this Apache-2.0 license (the vendored OpenHTML module keeps its explicit LGPL-2.1+; bridges/scripting keeps
|
||||
its previously inherited AGPL-3.0 explicitly). Each module still keeps its own dependency boundary and builds on its own through its relative parent. -->
|
||||
<parent><groupId>com.kingsrook</groupId><artifactId>qbit-build-parent</artifactId><version>2.1.1</version><relativePath/></parent>
|
||||
<parent><groupId>com.kingsrook</groupId><artifactId>qbit-build-parent</artifactId><version>2.2.1</version><relativePath/></parent>
|
||||
<groupId>com.kingsrook.qbits</groupId>
|
||||
<artifactId>qbit-document-generation-reactor</artifactId>
|
||||
<version>${revision}</version>
|
||||
@@ -14,7 +14,7 @@
|
||||
<url>https://github.com/Kingsrook/qbit-bom</url>
|
||||
<scm><connection>scm:git:https://github.com/Kingsrook/qbit-bom.git</connection><developerConnection>scm:git:ssh://git@github.com/Kingsrook/qbit-bom.git</developerConnection><url>https://github.com/Kingsrook/qbit-bom</url><tag>HEAD</tag></scm>
|
||||
<licenses><license><name>Apache License, Version 2.0</name><url>https://www.apache.org/licenses/LICENSE-2.0</url><distribution>repo</distribution></license></licenses>
|
||||
<properties><revision>0.1.0-SNAPSHOT</revision><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><enforcer.skip>true</enforcer.skip></properties>
|
||||
<properties><revision>1.1.0-RC.2</revision><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><enforcer.skip>true</enforcer.skip><qqq.version>4.1.0-RC.26</qqq.version></properties>
|
||||
<!-- Every module of the repository, so one release builds and publishes every GAV. Maven orders the reactor by dependency (the patched OpenHTML
|
||||
module before the renderer; foundation before the bridges). Build a subset with -pl <module> -am. -->
|
||||
<modules>
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@
|
||||
</execution></executions>
|
||||
</plugin>
|
||||
<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.14.1</version><configuration><showWarnings>true</showWarnings><failOnWarning>true</failOnWarning><compilerArgs><arg>-XDcompilePolicy=simple</arg><arg>--should-stop=ifError=FLOW</arg><arg>-XDaddTypeAnnotationsToSymbol=true</arg><arg>-Xplugin:ErrorProne -Xep:UnnecessaryParentheses:OFF -Xep:NotJavadoc:OFF</arg><arg>-Xlint:all</arg><arg>-Werror</arg></compilerArgs><annotationProcessorPaths><path><groupId>com.google.errorprone</groupId><artifactId>error_prone_core</artifactId><version>2.49.0</version></path></annotationProcessorPaths></configuration></plugin>
|
||||
<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><version>3.5.4</version><configuration><dependenciesToScan combine.self="override"/><systemPropertyVariables><java.awt.headless>true</java.awt.headless><pdfbox.fontcache>${project.build.directory}</pdfbox.fontcache></systemPropertyVariables></configuration></plugin>
|
||||
<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><version>3.5.4</version><configuration><dependenciesToScan combine.self="override"/><systemPropertyVariables><kof22.provider.version>${revision}</kof22.provider.version><java.awt.headless>true</java.awt.headless><pdfbox.fontcache>${project.build.directory}</pdfbox.fontcache></systemPropertyVariables></configuration></plugin>
|
||||
<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-dependency-plugin</artifactId><version>3.9.0</version><executions><execution><id>runtime-libraries</id><phase>package</phase><goals><goal>copy-dependencies</goal></goals><configuration><includeScope>runtime</includeScope><outputDirectory>${project.build.directory}/lib</outputDirectory></configuration></execution></executions></plugin>
|
||||
<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-jar-plugin</artifactId><version>3.4.2</version><configuration><archive><manifest><mainClass>com.kingsrook.qbits.renderer.RendererMain</mainClass><addClasspath>true</addClasspath><classpathPrefix>lib/</classpathPrefix></manifest></archive></configuration></plugin>
|
||||
<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-checkstyle-plugin</artifactId><version>3.3.1</version><dependencies><dependency><groupId>com.puppycrawl.tools</groupId><artifactId>checkstyle</artifactId><version>10.12.6</version></dependency></dependencies><configuration><configLocation>${project.basedir}/checkstyle/config.xml</configLocation><propertyExpansion>checkstyle.reactor.root=${project.basedir}</propertyExpansion><includeTestSourceDirectory>true</includeTestSourceDirectory><consoleOutput>true</consoleOutput><violationSeverity>warning</violationSeverity><failOnViolation>true</failOnViolation></configuration><executions><execution><phase>validate</phase><goals><goal>check</goal></goals></execution></executions></plugin>
|
||||
|
||||
@@ -51,6 +51,15 @@ public final class BarcodeCatalog
|
||||
{
|
||||
private static final Map<String, Supplier<Symbol>> FACTORIES = factories();
|
||||
|
||||
/*******************************************************************************
|
||||
** Encoders whose geometry is fixed by their specification (matrix symbols and
|
||||
** postal four-state codes): barHeight changes nothing for them, so it is
|
||||
** neither advertised nor accepted. BarcodeDeclaredCapabilityTest proves this
|
||||
** set is exactly the encoders on which barHeight has no effect.
|
||||
*******************************************************************************/
|
||||
private static final java.util.Set<String> FIXED_GEOMETRY = java.util.Set.of("australia-post", "aztec", "aztec-rune", "code-one", "code16k",
|
||||
"code49", "datamatrix", "grid-matrix", "japan-post", "kix", "maxicode", "micro-qr", "qr", "royal-mail", "swiss-qr", "upn-qr");
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Utility class.
|
||||
@@ -142,7 +151,14 @@ public final class BarcodeCatalog
|
||||
{
|
||||
Symbol symbol = FACTORIES.get(id).get();
|
||||
Map<String, Object> options = new LinkedHashMap<>();
|
||||
options.put("barHeight", Map.of("type", "integer", "minimum", 1, "maximum", 1000));
|
||||
if("gs1-databar".equals(id))
|
||||
{
|
||||
options.put("barHeight", Map.of("type", "integer", "minimum", 1, "maximum", 1000, "notWithMode", "STACKED"));
|
||||
}
|
||||
else if(!FIXED_GEOMETRY.contains(id))
|
||||
{
|
||||
options.put("barHeight", Map.of("type", "integer", "minimum", 1, "maximum", 1000));
|
||||
}
|
||||
if(symbol instanceof Coop2Of5)
|
||||
{
|
||||
options.put("checkDigit", Map.of("type", "string", "values", List.of("NONE", "ADD", "VALIDATE"), "default", "NONE"));
|
||||
@@ -380,6 +396,10 @@ public final class BarcodeCatalog
|
||||
}
|
||||
Symbol symbol = factory.get();
|
||||
structuredAppend(type, symbol, options);
|
||||
if(options.has("barHeight") && (FIXED_GEOMETRY.contains(type) || ("gs1-databar".equals(type) && options.has("mode") && "STACKED".equals(options.get("mode").asText()))))
|
||||
{
|
||||
throw new IllegalArgumentException("UNSUPPORTED_BARCODE_OPTION");
|
||||
}
|
||||
for(Map.Entry<String, JsonNode> field : options.properties())
|
||||
{
|
||||
if(List.of("qr", "datamatrix", "aztec").contains(type) && field.getKey().startsWith("structuredAppend"))
|
||||
|
||||
@@ -96,6 +96,36 @@ public final class PdfRenderer
|
||||
public static void validate(RenderInput input) throws Exception
|
||||
{
|
||||
template(input.source);
|
||||
for(Element element : Jsoup.parse(input.source).select("qqq-barcode"))
|
||||
{
|
||||
refuseUnsupportedOptions(element.attr("type"), element.attr("options"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Literal barcode options are checked against the catalog before any data
|
||||
** exists, so an option the encoder cannot honour is refused at validation as
|
||||
** its own closed code. Templated type or options are checked when rendered,
|
||||
** and any other problem is left for the render to report.
|
||||
*******************************************************************************/
|
||||
private static void refuseUnsupportedOptions(String type, String options)
|
||||
{
|
||||
if(options.isBlank() || type.contains("${") || type.contains("<#") || options.contains("${") || options.contains("<#"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
BarcodeCatalog.create(type, Wire.JSON.readTree(options));
|
||||
}
|
||||
catch(Exception exception)
|
||||
{
|
||||
if(exception instanceof IllegalArgumentException refusal && "UNSUPPORTED_BARCODE_OPTION".equals(refusal.getMessage()))
|
||||
{
|
||||
throw refusal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -103,7 +103,8 @@ public final class RendererMain
|
||||
"BARCODE_FIT_TOO_SMALL", "BARCODE_FIT_UNSUPPORTED", "BARCODE_FIT_BOX_OUTSIDE_STOCK",
|
||||
"BARCODE_FIT_GEOMETRY_MISMATCH", "INVALID_BARCODE_FIT_PROFILE", "INVALID_BARCODE_FIT_REQUEST",
|
||||
"BARCODE_SYMBOLOGY_NOT_ALLOWED", "SYMBOLOGY_ALLOWLIST_REQUIRED", "SCANNER_BARCODE_PROFILE_UNSUPPORTED",
|
||||
"SCANNER_BARCODE_PAYLOAD_UNSUPPORTED", "SCANNER_BARCODE_CHECKSUM_REQUIRED", "SCANNER_BARCODE_CHECKSUM_MISMATCH").contains(message))
|
||||
"SCANNER_BARCODE_PAYLOAD_UNSUPPORTED", "SCANNER_BARCODE_CHECKSUM_REQUIRED", "SCANNER_BARCODE_CHECKSUM_MISMATCH",
|
||||
"UNSUPPORTED_BARCODE_OPTION").contains(message))
|
||||
{
|
||||
return (message);
|
||||
}
|
||||
|
||||
+18
-2
@@ -84,16 +84,32 @@ public final class BackgroundImageQualification
|
||||
cases += 2;
|
||||
}
|
||||
return (Map.of("cases", cases, "surfaces", List.of("element", "pseudo", "page"), "exactRgbAlphaAndDimensions", true,
|
||||
"bothAliasPaintOrders", true, "provider", "com.kingsrook.thirdparty:openhtmltopdf-pdfbox:1.1.105-qqq-p1",
|
||||
"bothAliasPaintOrders", true, "provider", "com.kingsrook.thirdparty:openhtmltopdf-pdfbox:" + providerVersion(),
|
||||
"qualification", "trusted-product-fixtures-only"));
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
** The patched OpenHTML provider is published at the family version of this
|
||||
** reactor, which surefire passes in; there is no second literal to keep in sync.
|
||||
***************************************************************************/
|
||||
private static String providerVersion()
|
||||
{
|
||||
String version = System.getProperty("kof22.provider.version");
|
||||
if(version == null || version.isBlank())
|
||||
{
|
||||
throw new IllegalArgumentException("PDF_PROVIDER_VERSION_PROPERTY_MISSING");
|
||||
}
|
||||
return (version);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
** One provider class must come from the private patched GAV, with unchanged official companion versions.
|
||||
***************************************************************************/
|
||||
static void verifyProvider() throws Exception
|
||||
{
|
||||
verifyArtifact(com.openhtmltopdf.pdfboxout.PdfBoxFastOutputDevice.class, "com.kingsrook.thirdparty", "openhtmltopdf-pdfbox", "1.1.105-qqq-p1");
|
||||
verifyArtifact(com.openhtmltopdf.pdfboxout.PdfBoxFastOutputDevice.class, "com.kingsrook.thirdparty", "openhtmltopdf-pdfbox", providerVersion());
|
||||
verifyArtifact(com.openhtmltopdf.css.parser.CSSParser.class, "io.github.openhtmltopdf", "openhtmltopdf-core", "1.1.105");
|
||||
verifyArtifact(org.apache.pdfbox.pdmodel.PDDocument.class, "org.apache.pdfbox", "pdfbox", "3.0.8");
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user