Skip to content

Commit e920421

Browse files
committed
build(engine-java): emit the JCR Query Object Model types
The type generator skipped the Query Object Model, so a module could not call the factory that Jahia hands to its Lucene translation. The blocked entries are now generated, together with the query row types that a count query reads. A subtype only inherits a member that its parent type emitted, so the five execution methods are whitelisted on the parent interface as well. The patch script now fails when one of its search patterns matches no file, which is how five dead patterns went unnoticed.
1 parent c9ad374 commit e920421

3 files changed

Lines changed: 96 additions & 17 deletions

File tree

‎JCR-QUERY-BUILDER-PLAN.md‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -349,8 +349,8 @@ Every construct exists in the model and in the factory, and support has three si
349349

350350
- A `Literal` becomes `f.literal(vf.createValue(String(value), code))`, because `createValue(42, 3)` has no applicable overload and `createValue('42', 3)` works. The `code` is the `PropertyType` constant from the table of section 6.2.
351351
- A `BindVariableValue` is replaced by its bound value as a typed literal before the factory call. A missing binding throws `UNBOUND_VARIABLE` before any host call.
352-
- Empty slots receive an explicit `null`, and JavaScript arrays convert to `Ordering[]` and `Column[]`.
353-
- An empty column list becomes one `f.column(sel)` per selector, as the Parser does for `SELECT *` over a join. The i18n rewrite then sees a property-based node on each selector at `QueryModifierAndOptimizerVisitor.java:674-681`.
352+
- Empty slots receive an explicit `null`, and JavaScript arrays convert to `Ordering[]` and `Column[]`. The generated signatures are not nullable, so `qom.ts` declares a local `QOMFactoryLike` that narrows `createQuery` and `column`, and casts the factory once. This is the technique section 7.2 already uses for `QueryResultLike`. Phase 0 verified that patching the generated declarations instead would work, and rejected it: the patch would change the published types of the whole library for two call sites.
353+
- An empty column list becomes one `f.column(sel, null, null)` per selector, as the Parser does for `SELECT *` over a join. The emitted `column()` takes three parameters, so the two empty slots are explicit. The i18n rewrite then sees a property-based node on each selector at `QueryModifierAndOptimizerVisitor.java:674-681`.
354354
- The only text form of a built query is `getStatement()`, the `QOMFormatter` output. `executeQuery` logs it at debug level, and the lab tests snapshot it.
355355

356356
The value factory is type-only, so the TypeScript grammar check is the only name and path validation on this sink. A `REFERENCE` literal executes as `WEAKREFERENCE`, see `JCRValueFactoryImpl.java:96-99`, and `diagnose()` reports it as `partial`.
@@ -398,8 +398,8 @@ java-ts-bind changes, phase 0 and on the critical path, in `EJ/.java-ts-bind/pac
398398

399399
1. Remove the `blacklist` entries `javax.jcr.query.Query`, `javax.jcr.query.qom.QueryObjectModel` and `javax.jcr.query.qom.QueryObjectModelFactory` at `:429`, `:431` and `:432`, after the JS-modules team confirms that no product decision is behind them. Keep `:430 QueryManager`.
400400
2. Add `javax.jcr.query.qom` and `javax.jcr.query.Query` to `include`. `javax.jcr.Value` at `:68` already prefix-covers `ValueFactory`.
401-
3. Add to `methodWhitelist`: `QueryManagerWrapper.getQOMFactory`, `JCRSessionWrapper.getValueFactory`, `javax.jcr.ValueFactory.createValue` and `org.jahia.services.query.QueryResultWrapper.getRows`. Also add every method of `javax.jcr.query.qom.QueryObjectModelFactory`, every getter of the `javax.jcr.query.qom` types, and the `execute`, `setLimit`, `setOffset`, `bindValue` and `getStatement` methods of `javax.jcr.query.qom.QueryObjectModel`. The fork copies an inherited method into a subtype when `<subtype>.<method>` matches, so the subtype-named entries are correct.
402-
4. Add regen assertions: `tsc --noEmit --skipLibCheck false` over `target/types`, because `skipLibCheck: true` at `LIB/tsconfig.json:11` hides a broken `.d.ts`. Make `apply-patch.sh:63-69` fail when a search matches zero files, and remove the entries that match nothing on the worktree output.
401+
3. Add to `methodWhitelist`: `QueryManagerWrapper.getQOMFactory`, `JCRSessionWrapper.getValueFactory`, `javax.jcr.ValueFactory.createValue` and `org.jahia.services.query.QueryResultWrapper.getRows`. Also add `javax.jcr.query.Query.bindValue`, `execute`, `getStatement`, `setLimit` and `setOffset`, because a subtype only inherits a member that the parent type itself emitted, so the `QueryObjectModel` entries alone produce the four getters and none of the five execution methods. Also add `javax.jcr.query.Row.get.*`, `RowIterator.nextRow` and `RowIterator.getSize`, without which `getRows()` returns a type with no members. Also add every method of `javax.jcr.query.qom.QueryObjectModelFactory`, every getter of the `javax.jcr.query.qom` types, and the `execute`, `setLimit`, `setOffset`, `bindValue` and `getStatement` methods of `javax.jcr.query.qom.QueryObjectModel`. The fork copies an inherited method into a subtype when `<subtype>.<method>` matches, so the subtype-named entries are correct.
402+
4. Add regen assertions: `tsc --noEmit --skipLibCheck false` over `target/types`, because `skipLibCheck: true` at `LIB/tsconfig.json:11` hides a broken `.d.ts`. Phase 0 found five pre-existing errors on an untouched baseline, four dangling imports and one missing `java.util` member, so this check cannot gate the build until they are fixed. Phase 0 recorded the baseline instead and asserts that a change adds no new error. Make `apply-patch.sh:63-69` fail when a search matches zero files, and remove the entries that match nothing on the worktree output.
403403

404404
Documentation follows the worktree conventions. `LIB/README.md` groups exports under `##` headings with one `###` per export, so it gains a `## Query builder` group. The guide is `docs/2-guides/4-querying/README.md`, with the academy front matter of `docs/2-guides/3-i18n/README.md:1-8`, because `docs/2-guides/` holds three guides today. `docs/adr/` does not exist on `main`, so phase 2 creates it and writes the ADR in MADR form, numbered at merge time. The changelog entry is a `.chachalog/<id>.md` file with `javascript-modules: minor`, in the format of the existing entries.
405405

‎javascript-modules-engine-java/.java-ts-bind/apply-patch.sh‎

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,15 @@ DIR="target/java-ts-bind/types"
88
searches=(
99
"ServiceReference\\[\\]"
1010
"Map\\[\\]"
11-
"RangeIterator extends Iterator"
1211
".*org\\.graalvm\\.polyglot.*;"
1312
"loadPropertiesResource(bundle: Bundle, path: string): ProxyObject;"
1413
"transformToJsNode(node: JCRNodeWrapper, includeChildren: boolean, includeDescendants: boolean, includeAllTranslations: boolean): ProxyObject;"
1514
"getRenderParameters(resource: Resource): ProxyObject;"
16-
"executeQuerySync(parameters: Map): string;"
1715
"getConfigPids(): ProxyArray;"
1816
"getConfigFactoryIdentifiers(factoryPid: string): ProxyArray;"
1917
"getConfigValues(configPid: string): ProxyObject;"
2018
"getConfigFactoryValues(factoryPid: string, factoryIdentifier: string): ProxyObject;"
21-
"import { ContextProvider } from 'org.jahia.modules.javascript.modules.engine.jsengine';"
22-
"export class JCRNodeDecorator implements JCRNodeWrapper"
2319
"export class JCRSiteNode {"
24-
"export class JCRWorkspaceWrapper implements Workspace {"
2520
"renderComponent(attr: Map<string, any>, renderContext: RenderContext): string;"
2621
"render(attr: Map<string, any>, renderContext: RenderContext, currentResource: Resource): string;"
2722
"addResources(attr: Map<string, any>, renderContext: RenderContext): string;"
@@ -35,20 +30,15 @@ searches=(
3530
replaces=(
3631
"ServiceReference<any>[]"
3732
"any[]"
38-
"RangeIterator extends Iterator<Item>"
3933
""
4034
"loadPropertiesResource(bundle: Bundle, path: string): any;"
4135
"transformToJsNode(node: JCRNodeWrapper, includeChildren: boolean, includeDescendants: boolean, includeAllTranslations: boolean): any;"
4236
"getRenderParameters(resource: Resource): any;"
43-
"executeQuerySync(parameters: any): string;"
4437
"getConfigPids(): string[];"
4538
"getConfigFactoryIdentifiers(factoryPid: string): string[];"
4639
"getConfigValues(configPid: string): any;"
4740
"getConfigFactoryValues(factoryPid: string, factoryIdentifier: string): any;"
48-
""
49-
"export interface JCRNodeDecorator extends JCRNodeWrapper"
5041
"export interface JCRSiteNode extends JCRNodeWrapper {"
51-
"export interface JCRWorkspaceWrapper extends Workspace {"
5242
"renderComponent(attr: any, renderContext: RenderContext): string;"
5343
"render(attr: any, renderContext: RenderContext, currentResource: Resource): string;"
5444
"addResources(attr: any, renderContext: RenderContext): string;"
@@ -60,10 +50,25 @@ replaces=(
6050
)
6151

6252
# Iterate over the arrays and perform the replacements
53+
# A search that matches no file is an error: the declaration it was written for has changed
54+
# name or shape, so the patch silently stops being applied and the generated types drift.
55+
# grep runs with basic regular expressions, the same dialect as the sed calls below.
56+
unmatched=()
6357
for i in "${!searches[@]}"; do
58+
if ! grep -rl --include="*.d.ts" "${searches[$i]}" $DIR > /dev/null; then
59+
unmatched+=("${searches[$i]}")
60+
continue
61+
fi
6462
if [[ "$OSTYPE" == "darwin"* ]]; then
6563
find $DIR -name "*.d.ts" -exec sed -i '' -e "s/${searches[$i]}/${replaces[$i]}/g" {} \;
6664
else
6765
find $DIR -name "*.d.ts" -exec sed -i -e "s/${searches[$i]}/${replaces[$i]}/g" {} \;
6866
fi
6967
done
68+
69+
if [ ${#unmatched[@]} -ne 0 ]; then
70+
echo "apply-patch.sh: no generated .d.ts file matches these search patterns:" >&2
71+
printf ' %s\n' "${unmatched[@]}" >&2
72+
echo "Fix each pattern to match the current output, or remove the obsolete entry and its replacement." >&2
73+
exit 1
74+
fi

‎javascript-modules-engine-java/.java-ts-bind/package.json‎

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,11 @@
6868
"javax.jcr.Value",
6969
"javax.jcr.Workspace",
7070
"javax.jcr.query.InvalidQueryException",
71+
"javax.jcr.query.Query",
7172
"javax.jcr.query.QueryResult",
7273
"javax.jcr.query.Row",
7374
"javax.jcr.query.RowIterator",
75+
"javax.jcr.query.qom",
7476
"javax.servlet.ServletInputStream",
7577
"javax.servlet.ServletOutputStream",
7678
"javax.servlet.ServletRequest",
@@ -199,7 +201,79 @@
199201
"javax.jcr.RangeIterator.getSize",
200202
"javax.jcr.Value.get.*",
201203
"javax.jcr.Value.is.*",
204+
"javax.jcr.ValueFactory.createValue",
205+
"javax.jcr.query.Query.bindValue",
206+
"javax.jcr.query.Query.execute",
207+
"javax.jcr.query.Query.getStatement",
208+
"javax.jcr.query.Query.setLimit",
209+
"javax.jcr.query.Query.setOffset",
202210
"javax.jcr.query.QueryResult.get.*",
211+
"javax.jcr.query.Row.get.*",
212+
"javax.jcr.query.RowIterator.getSize",
213+
"javax.jcr.query.RowIterator.nextRow",
214+
"javax.jcr.query.qom.And.get.*",
215+
"javax.jcr.query.qom.BindVariableValue.get.*",
216+
"javax.jcr.query.qom.ChildNode.get.*",
217+
"javax.jcr.query.qom.ChildNodeJoinCondition.get.*",
218+
"javax.jcr.query.qom.Column.get.*",
219+
"javax.jcr.query.qom.Comparison.get.*",
220+
"javax.jcr.query.qom.DescendantNode.get.*",
221+
"javax.jcr.query.qom.DescendantNodeJoinCondition.get.*",
222+
"javax.jcr.query.qom.EquiJoinCondition.get.*",
223+
"javax.jcr.query.qom.FullTextSearch.get.*",
224+
"javax.jcr.query.qom.FullTextSearchScore.get.*",
225+
"javax.jcr.query.qom.Join.get.*",
226+
"javax.jcr.query.qom.Length.get.*",
227+
"javax.jcr.query.qom.Literal.get.*",
228+
"javax.jcr.query.qom.LowerCase.get.*",
229+
"javax.jcr.query.qom.NodeLocalName.get.*",
230+
"javax.jcr.query.qom.NodeName.get.*",
231+
"javax.jcr.query.qom.Not.get.*",
232+
"javax.jcr.query.qom.Or.get.*",
233+
"javax.jcr.query.qom.Ordering.get.*",
234+
"javax.jcr.query.qom.PropertyExistence.get.*",
235+
"javax.jcr.query.qom.PropertyValue.get.*",
236+
"javax.jcr.query.qom.QueryObjectModel.bindValue",
237+
"javax.jcr.query.qom.QueryObjectModel.execute",
238+
"javax.jcr.query.qom.QueryObjectModel.getColumns",
239+
"javax.jcr.query.qom.QueryObjectModel.getConstraint",
240+
"javax.jcr.query.qom.QueryObjectModel.getOrderings",
241+
"javax.jcr.query.qom.QueryObjectModel.getSource",
242+
"javax.jcr.query.qom.QueryObjectModel.getStatement",
243+
"javax.jcr.query.qom.QueryObjectModel.setLimit",
244+
"javax.jcr.query.qom.QueryObjectModel.setOffset",
245+
"javax.jcr.query.qom.QueryObjectModelFactory.and",
246+
"javax.jcr.query.qom.QueryObjectModelFactory.ascending",
247+
"javax.jcr.query.qom.QueryObjectModelFactory.bindVariable",
248+
"javax.jcr.query.qom.QueryObjectModelFactory.childNode",
249+
"javax.jcr.query.qom.QueryObjectModelFactory.childNodeJoinCondition",
250+
"javax.jcr.query.qom.QueryObjectModelFactory.column",
251+
"javax.jcr.query.qom.QueryObjectModelFactory.comparison",
252+
"javax.jcr.query.qom.QueryObjectModelFactory.createQuery",
253+
"javax.jcr.query.qom.QueryObjectModelFactory.descendantNode",
254+
"javax.jcr.query.qom.QueryObjectModelFactory.descendantNodeJoinCondition",
255+
"javax.jcr.query.qom.QueryObjectModelFactory.descending",
256+
"javax.jcr.query.qom.QueryObjectModelFactory.equiJoinCondition",
257+
"javax.jcr.query.qom.QueryObjectModelFactory.fullTextSearch",
258+
"javax.jcr.query.qom.QueryObjectModelFactory.fullTextSearchScore",
259+
"javax.jcr.query.qom.QueryObjectModelFactory.join",
260+
"javax.jcr.query.qom.QueryObjectModelFactory.length",
261+
"javax.jcr.query.qom.QueryObjectModelFactory.literal",
262+
"javax.jcr.query.qom.QueryObjectModelFactory.lowerCase",
263+
"javax.jcr.query.qom.QueryObjectModelFactory.nodeLocalName",
264+
"javax.jcr.query.qom.QueryObjectModelFactory.nodeName",
265+
"javax.jcr.query.qom.QueryObjectModelFactory.not",
266+
"javax.jcr.query.qom.QueryObjectModelFactory.or",
267+
"javax.jcr.query.qom.QueryObjectModelFactory.propertyExistence",
268+
"javax.jcr.query.qom.QueryObjectModelFactory.propertyValue",
269+
"javax.jcr.query.qom.QueryObjectModelFactory.sameNode",
270+
"javax.jcr.query.qom.QueryObjectModelFactory.sameNodeJoinCondition",
271+
"javax.jcr.query.qom.QueryObjectModelFactory.selector",
272+
"javax.jcr.query.qom.QueryObjectModelFactory.upperCase",
273+
"javax.jcr.query.qom.SameNode.get.*",
274+
"javax.jcr.query.qom.SameNodeJoinCondition.get.*",
275+
"javax.jcr.query.qom.Selector.get.*",
276+
"javax.jcr.query.qom.UpperCase.get.*",
203277
"javax.servlet.ServletInputStream.isFinished",
204278
"javax.servlet.ServletInputStream.isReady",
205279
"javax.servlet.ServletInputStream.read",
@@ -284,6 +358,7 @@
284358
"org.jahia.services.content.JCRSessionWrapper.getProperty.*",
285359
"org.jahia.services.content.JCRSessionWrapper.getRootNode",
286360
"org.jahia.services.content.JCRSessionWrapper.getUser.*",
361+
"org.jahia.services.content.JCRSessionWrapper.getValueFactory",
287362
"org.jahia.services.content.JCRSessionWrapper.getWorkspace",
288363
"org.jahia.services.content.JCRSessionWrapper.nodeExists",
289364
"org.jahia.services.content.JCRSessionWrapper.propertyExists",
@@ -298,6 +373,7 @@
298373
"org.jahia.services.content.JCRWorkspaceWrapper.getQueryManager",
299374
"org.jahia.services.content.JCRWorkspaceWrapper.getSession",
300375
"org.jahia.services.content.QueryManagerWrapper.createQuery",
376+
"org.jahia.services.content.QueryManagerWrapper.getQOMFactory",
301377
"org.jahia.services.content.decorator.JCRSiteNode.get.*AsDate",
302378
"org.jahia.services.content.decorator.JCRSiteNode.get.*InstalledModules.*",
303379
"org.jahia.services.content.decorator.JCRSiteNode.get.*Language",
@@ -331,6 +407,7 @@
331407
"org.jahia.services.content.decorator.JCRSiteNode.is.*",
332408
"org.jahia.services.query.QueryResultWrapper.getApproxCount",
333409
"org.jahia.services.query.QueryResultWrapper.getNodes",
410+
"org.jahia.services.query.QueryResultWrapper.getRows",
334411
"org.jahia.services.query.QueryWrapper.bindValue",
335412
"org.jahia.services.query.QueryWrapper.execute",
336413
"org.jahia.services.query.QueryWrapper.execute",
@@ -426,10 +503,7 @@
426503
"javax.jcr.nodetype.NodeType",
427504
"javax.jcr.nodetype.PropertyDefinition",
428505
"javax.jcr.observation.ObservationManager",
429-
"javax.jcr.query.Query",
430506
"javax.jcr.query.QueryManager",
431-
"javax.jcr.query.qom.QueryObjectModel",
432-
"javax.jcr.query.qom.QueryObjectModelFactory",
433507
"javax.jcr.retention.RetentionManager",
434508
"javax.jcr.security.AccessControlManager",
435509
"javax.jcr.version.Version",

0 commit comments

Comments
 (0)