orbinson / aem-groovy-console

The AEM Groovy Console provides an interface for running Groovy scripts in the AEM container. Scripts can be created to manipulate content in the JCR, call OSGi services, or execute arbitrary code using the CQ, Sling, or JCR APIs.
https://orbinson.github.io/aem-groovy-console/
Other
16 stars 3 forks source link

Enforce creation of /var/groovyconsole with the right node type #50

Closed kwin closed 5 months ago

kwin commented 5 months ago

Right now in https://github.com/orbinson/aem-groovy-console/blob/main/ui.config/src/main/content/jcr_root/apps/groovyconsole-config/osgiconfig/config/org.apache.sling.jcr.repoinit.RepositoryInitializer-groovyconsole.config you enforce creation of subnodes within /var/groovyconsole which require that to be a lenient type (e.g. sling:Folder). If for some reason the node /var/groovyconsole is of type nt:folder starting the repository fails with the following exception:

javax.jcr.RepositoryException: Applying repoinit operation failed despite retry; set loglevel to DEBUG to see all exceptions. Last exception message was: Session.save failed: javax.jcr.nodetype.ConstraintViolationException: OakConstraint0001: /var/groovyconsole[[nt:folder]]: No matching definition found for child node audit with effective type [nt:unstructured]
    at org.apache.sling.jcr.repoinit.impl.RepositoryInitializerFactory.applyOperations(RepositoryInitializerFactory.java:176) [org.apache.sling.jcr.repoinit:1.1.38]
    at org.apache.sling.jcr.repoinit.impl.RepositoryInitializerFactory.processRepository(RepositoryInitializerFactory.java:129) [org.apache.sling.jcr.repoinit:1.1.38]
    at org.apache.sling.jcr.base.AbstractSlingRepositoryManager.executeRepositoryInitializers(AbstractSlingRepositoryManager.java:627) [org.apache.sling.jcr.base:3.1.14]
    at org.apache.sling.jcr.base.AbstractSlingRepositoryManager.initializeAndRegisterRepositoryService(AbstractSlingRepositoryManager.java:575) [org.apache.sling.jcr.base:3.1.14]
    at org.apache.sling.jcr.base.AbstractSlingRepositoryManager.access$300(AbstractSlingRepositoryManager.java:96) [org.apache.sling.jcr.base:3.1.14]
    at org.apache.sling.jcr.base.AbstractSlingRepositoryManager$4.run(AbstractSlingRepositoryManager.java:544) [org.apache.sling.jcr.base:3.1.14]
Caused by: org.apache.sling.jcr.repoinit.impl.RepoInitException: Session.save failed: javax.jcr.nodetype.ConstraintViolationException: OakConstraint0001: /var/groovyconsole[[nt:folder]]: No matching definition found for child node audit with effective type [nt:unstructured]
    at org.apache.sling.jcr.repoinit.impl.DoNothingVisitor.report(DoNothingVisitor.java:66) [org.apache.sling.jcr.repoinit:1.1.38]
    at org.apache.sling.jcr.repoinit.impl.AclVisitor.visitCreatePath(AclVisitor.java:207) [org.apache.sling.jcr.repoinit:1.1.38]
    at org.apache.sling.repoinit.parser.operations.CreatePath.accept(CreatePath.java:71) [org.apache.sling.repoinit.parser:1.6.14]
    at org.apache.sling.jcr.repoinit.impl.JcrRepoInitOpsProcessorImpl.apply(JcrRepoInitOpsProcessorImpl.java:56) [org.apache.sling.jcr.repoinit:1.1.38]
    at org.apache.sling.jcr.repoinit.impl.RepositoryInitializerFactory.lambda$applyOperations$0(RepositoryInitializerFactory.java:151) [org.apache.sling.jcr.repoinit:1.1.38]
    at org.apache.sling.jcr.repoinit.impl.RetryableOperation.apply(RetryableOperation.java:57) [org.apache.sling.jcr.repoinit:1.1.38]
    at org.apache.sling.jcr.repoinit.impl.RepositoryInitializerFactory.applyOperations(RepositoryInitializerFactory.java:149) [org.apache.sling.jcr.repoinit:1.1.38]
    ... 5 common frames omitted
Caused by: javax.jcr.nodetype.ConstraintViolationException: OakConstraint0001: /var/groovyconsole[[nt:folder]]: No matching definition found for child node audit with effective type [nt:unstructured]
    at org.apache.jackrabbit.oak.api.CommitFailedException.asRepositoryException(CommitFailedException.java:226) [org.apache.jackrabbit.oak-api:1.22.17]
    at org.apache.jackrabbit.oak.api.CommitFailedException.asRepositoryException(CommitFailedException.java:213) [org.apache.jackrabbit.oak-api:1.22.17]
    at org.apache.jackrabbit.oak.jcr.delegate.SessionDelegate.newRepositoryException(SessionDelegate.java:669) [org.apache.jackrabbit.oak-jcr:1.22.17]
    at org.apache.jackrabbit.oak.jcr.delegate.SessionDelegate.save(SessionDelegate.java:495) [org.apache.jackrabbit.oak-jcr:1.22.17]
    at org.apache.jackrabbit.oak.jcr.session.SessionImpl$9.performVoid(SessionImpl.java:453) [org.apache.jackrabbit.oak-jcr:1.22.17]
    at org.apache.jackrabbit.oak.jcr.delegate.SessionDelegate.performVoid(SessionDelegate.java:273) [org.apache.jackrabbit.oak-jcr:1.22.17]
    at org.apache.jackrabbit.oak.jcr.session.SessionImpl.save(SessionImpl.java:450) [org.apache.jackrabbit.oak-jcr:1.22.17]
    at com.adobe.granite.repository.impl.CRX3SessionImpl.save(CRX3SessionImpl.java:208) [com.adobe.granite.repository:1.6.28.CQ650-B0001]
    at org.apache.sling.jcr.repoinit.impl.AclVisitor.visitCreatePath(AclVisitor.java:205) [org.apache.sling.jcr.repoinit:1.1.38]
    ... 10 common frames omitted
Caused by: org.apache.jackrabbit.oak.api.CommitFailedException: OakConstraint0001: /var/groovyconsole[[nt:folder]]: No matching definition found for child node audit with effective type [nt:unstructured]
    at org.apache.jackrabbit.oak.plugins.nodetype.TypeEditor$1.onConstraintViolation(TypeEditor.java:109) [org.apache.jackrabbit.oak-core:1.22.17]
    at org.apache.jackrabbit.oak.plugins.nodetype.TypeEditor.constraintViolation(TypeEditor.java:234) [org.apache.jackrabbit.oak-core:1.22.17]
    at org.apache.jackrabbit.oak.plugins.nodetype.TypeEditor.childNodeChanged(TypeEditor.java:312) [org.apache.jackrabbit.oak-core:1.22.17]
    at org.apache.jackrabbit.oak.plugins.nodetype.TypeEditor.childNodeAdded(TypeEditor.java:285) [org.apache.jackrabbit.oak-core:1.22.17]
    at org.apache.jackrabbit.oak.spi.commit.VisibleEditor.childNodeAdded(VisibleEditor.java:89) [org.apache.jackrabbit.oak-store-spi:1.22.17]
    at org.apache.jackrabbit.oak.spi.commit.CompositeEditor.childNodeAdded(CompositeEditor.java:107) [org.apache.jackrabbit.oak-store-spi:1.22.17]
    at org.apache.jackrabbit.oak.spi.commit.EditorDiff.childNodeAdded(EditorDiff.java:115) [org.apache.jackrabbit.oak-store-spi:1.22.17]
    at org.apache.jackrabbit.oak.segment.SegmentNodeState.compareAgainstBaseState(SegmentNodeState.java:639) [org.apache.jackrabbit.oak-segment-tar:1.22.17]
    at org.apache.jackrabbit.oak.spi.commit.EditorDiff.childNodeChanged(EditorDiff.java:147) [org.apache.jackrabbit.oak-store-spi:1.22.17]
    at org.apache.jackrabbit.oak.segment.MapRecord$4.childNodeChanged(MapRecord.java:471) [org.apache.jackrabbit.oak-segment-tar:1.22.17]
    at org.apache.jackrabbit.oak.segment.MapRecord.compare(MapRecord.java:517) [org.apache.jackrabbit.oak-segment-tar:1.22.17]
    at org.apache.jackrabbit.oak.segment.MapRecord.compareBranch(MapRecord.java:595) [org.apache.jackrabbit.oak-segment-tar:1.22.17]
    at org.apache.jackrabbit.oak.segment.MapRecord.compare(MapRecord.java:496) [org.apache.jackrabbit.oak-segment-tar:1.22.17]
    at org.apache.jackrabbit.oak.segment.MapRecord.compare(MapRecord.java:462) [org.apache.jackrabbit.oak-segment-tar:1.22.17]
    at org.apache.jackrabbit.oak.segment.SegmentNodeState.compareAgainstBaseState(SegmentNodeState.java:651) [org.apache.jackrabbit.oak-segment-tar:1.22.17]
    at org.apache.jackrabbit.oak.spi.commit.EditorDiff.childNodeChanged(EditorDiff.java:147) [org.apache.jackrabbit.oak-store-spi:1.22.17]
    at org.apache.jackrabbit.oak.segment.MapRecord$4.childNodeChanged(MapRecord.java:471) [org.apache.jackrabbit.oak-segment-tar:1.22.17]
    at org.apache.jackrabbit.oak.segment.MapRecord.compare(MapRecord.java:517) [org.apache.jackrabbit.oak-segment-tar:1.22.17]
    at org.apache.jackrabbit.oak.segment.MapRecord.compare(MapRecord.java:462) [org.apache.jackrabbit.oak-segment-tar:1.22.17]
    at org.apache.jackrabbit.oak.segment.SegmentNodeState.compareAgainstBaseState(SegmentNodeState.java:651) [org.apache.jackrabbit.oak-segment-tar:1.22.17]
    at org.apache.jackrabbit.oak.spi.commit.EditorDiff.process(EditorDiff.java:51) [org.apache.jackrabbit.oak-store-spi:1.22.17]
    at org.apache.jackrabbit.oak.spi.commit.EditorHook.processCommit(EditorHook.java:54) [org.apache.jackrabbit.oak-store-spi:1.22.17]
    at org.apache.jackrabbit.oak.spi.commit.CompositeHook.processCommit(CompositeHook.java:60) [org.apache.jackrabbit.oak-store-spi:1.22.17]
    at org.apache.jackrabbit.oak.spi.commit.CompositeHook.processCommit(CompositeHook.java:60) [org.apache.jackrabbit.oak-store-spi:1.22.17]
    at org.apache.jackrabbit.oak.segment.scheduler.Commit.apply(Commit.java:99) [org.apache.jackrabbit.oak-segment-tar:1.22.17]
    at org.apache.jackrabbit.oak.segment.scheduler.LockBasedScheduler.execute(LockBasedScheduler.java:299) [org.apache.jackrabbit.oak-segment-tar:1.22.17]
    at org.apache.jackrabbit.oak.segment.scheduler.LockBasedScheduler.schedule(LockBasedScheduler.java:270) [org.apache.jackrabbit.oak-segment-tar:1.22.17]
    at org.apache.jackrabbit.oak.segment.SegmentNodeStore.merge(SegmentNodeStore.java:211) [org.apache.jackrabbit.oak-segment-tar:1.22.17]
    at org.apache.jackrabbit.oak.core.MutableRoot.commit(MutableRoot.java:251) [org.apache.jackrabbit.oak-core:1.22.17]
    at org.apache.jackrabbit.oak.jcr.delegate.SessionDelegate.commit(SessionDelegate.java:346) [org.apache.jackrabbit.oak-jcr:1.22.17]
    at org.apache.jackrabbit.oak.jcr.delegate.SessionDelegate.save(SessionDelegate.java:493) [org.apache.jackrabbit.oak-jcr:1.22.17]
    ... 15 common frames omitted
kwin commented 5 months ago

To me it is not clear which code is supposed to create/creating the parent node /var/groovyconsole in the first place. Any idea @bdhoine or @royteeuwen?

Update: So repoinit itself creates non-existing ancestors with default type (and in case that fails with sling:Folder): https://github.com/apache/sling-org-apache-sling-jcr-repoinit/blob/41509793b720eac9328bef47c61745d6f94e197b/src/main/java/org/apache/sling/jcr/repoinit/impl/NodeVisitor.java#L182-L186. The real solution is to switch to enforce path (https://issues.apache.org/jira/browse/SLING-11736). Unfortunately that is not available in AEM 6.5.x. We face a race condition in our packages when we deploy from scratch our all container package which contains both

  1. https://github.com/valtech/aem-easy-content-upgrade/tree/develop/complete (including aem-groovy-console) and
  2. A custom package containing only the child /var/groovyconsole/customer/script with no .content.xml for uncovered ancestor nodes

on AEM 6.5.x (still shipping with a very outdated JR FileVault version). In case Groovy Console's OSGi configuration for repoinit kicks in first, everything is fine, but in case our package is installed first /var/groovyconsole is created as nt:folder and never modified afterwards. Restarting such an AEM instance leads to the repository not starting fully with the exception message from above.

royteeuwen commented 5 months ago

@kwin didn't even know that create path was deprecated, thanks for that!

This does indeed make it a hard case to fix in AEM 6.5 though... (or any AEM instance where you use an all package through installation of the package manager, you always get race conditions)

What I could do is rewrite this to BundleActivator code, this way we can change the primary type of /var/groovyconsole even in AEM 6.5.

The only other alternative I can think of is putting package dependencies between the config package of aem-groovy-console and your customer scripts, but even then you aren't certain the OSGi config will actually be executed before installing the depending package :/

Do you have any other suggestions?

kwin commented 5 months ago

I don’t have a good idea either. The proper solution is „enforce path“ but only available in AEMaaCS unfortunately. Maybe the node /var/groovyconsole/audit can be converted to sling:Folder as that is allowed in the pretty restricted nt:folder as well. WDYT?

royteeuwen commented 4 months ago

@kwin sorry for the long wait, we had issues with releasing this repo because of GPG issues. This is now released.

FYI, I'm planning to release a version 20.x in the upcoming weeks with the changes from Barry and to upgrade groovy to JDK 22 support (Groovy 4.0.18) and a new feature inspired from Jenkins pipeline libraries where you can do @Library('path/in/crx') to load in extra groovy "snippets" that you'd like to share over multiple groovy files at runtime instead of having to do a code deploy to add extra functionality