Skip to content

Maven plugin that cleans Adobe Experience Manager (AEM) projects by removing runtime generated properties of Java Content Repository (JCR) nodes

License

Notifications You must be signed in to change notification settings

ciechanowiec/jcrcleaner

Repository files navigation

JCR Cleaner

1. Overview

JCR Cleaner is a Maven plugin that cleans Adobe Experience Manager (AEM) projects by removing runtime generated properties of Java Content Repository (JCR) nodes, such as cq:lastReplicated, jcr:lastModified and jcr:created (by default, 13 types of properties are removed). The plugin aims to be a part of a standard toolkit of every AEM project in order to improve code readability, as well as reduce the amount of unnecessary and environment specific data stored in the codebase.

2. Plugin Purpose

2.1. The Problem of Runtime Generated Properties

Code repositories for AEM projects contain a substantial number of files in FileVault Document View (DocView) format, named .content.xml (DocView files). In a typical AEM project there can be several thousand of such files.

DocView files represent serialized JCR nodes and their properties. In many cases those files aren’t created externally, but are transferred into a code repository directly from JCR. However, JCR nodes, besides their normal properties, often have runtime generated properties, such as jcr:lastModifiedBy or cq:lastReplicatedBy. In such cases transfer of DocView files into a code repository directly from JCR results in putting to the code repository DocView files with both normal and runtime generated properties of JCR nodes.

Storing certain runtime generated properties of JCR nodes in a code repository in DocView files is a bad practice at least due to the following reasons:

  1. Reduced Code Readability

    The inclusion of runtime generated properties of JCR nodes into DocView files in a code repository can notably reduce the readability of a code repository. When a repository is saturated with machine-generated data, discerning the important code and configuration details becomes challenging. This obscurity complicates version control, code reviews, debugging, and overall understanding of the codebase. A secondary but still notable aspect of this issue is the impairment of static code analysis. Warnings or alerts generated by these tools due to runtime generated properties create unnecessary noise, making it harder to spot real code-related issues.

  2. Environment Specific Data

    Runtime generated properties of JCR nodes are often specific to the environment from which they were retrieved. Storing these properties in a code repository can lead to the inclusion of environment-specific data in the codebase. This can cause problems when moving the code to a different environment, such as staging or production.

2.2. Removing Runtime Generated Properties by JCR Cleaner

The presence of unwanted runtime generated properties of JCR nodes in a code repository in DocView files and consequences of that problem are resolved by JCR Cleaner. It achieves this by removing those properties from the relevant files. By default, the properties that have the following names are removed:

  • cq:lastReplicated

  • cq:lastReplicatedBy

  • cq:lastReplicationAction

  • cq:lastModified

  • cq:lastModifiedBy

  • cq:lastPublished

  • cq:lastPublishedBy

  • jcr:lastModified

  • jcr:lastModifiedBy

  • jcr:created

  • jcr:createdBy

  • jcr:isCheckedOut

  • jcr:uuid

2.2.1. Example with Default Properties

Before

Here is an example of a DocView file that has 20 runtime generated properties. Note that those properties don’t have a pattern where they are located (no rule one line - one property) and are scattered irregularly:

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
          xmlns:jcr="http://www.jcp.org/jcr/1.0"
          xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
          xmlns:cq="http://www.day.com/jcr/cq/1.0"
          jcr:primaryType="nt:unstructured"
          jcr:title="Properties"
          cq:lastReplicated="{Date}2023-03-15T11:00:00.000Z"
          cq:lastReplicatedBy="admin"
          cq:lastReplicationAction="Activate"
          cq:lastModified="{Date}2023-03-13T09:30:00.000+01:00"
          cq:lastModifiedBy="admin"
          cq:lastPublished="{Date}2023-03-15T11:00:00.000Z"
          cq:lastPublishedBy="admin"
          jcr:lastModified="Thu Jun 17 2021 12:55:05 GMT+0000"
          jcr:lastModifiedBy="admin"
          jcr:created="Thu Jun 17 2021 19:55:05 GMT+0000"
          jcr:createdBy="admin"
          jcr:isCheckedOut="{Boolean}true"
          jcr:uuid="f5b51baa-34a2-47b7-860b-391e885a0f9f"
          sling:resourceType="cq/gui/components/authoring/dialog">
    <content
            jcr:primaryType="nt:unstructured" cq:lastReplicated="{Date}2023-03-15T11:00:00.000Z"
            sling:resourceType="granite/ui/components/coral/foundation/fixedcolumns">
        <items jcr:primaryType="nt:unstructured" cq:lastReplicated="{Date}2023-03-15T11:00:00.000Z">
            <column cq:lastReplicated="{Date}2023-03-15T11:00:00.000Z" jcr:createdBy="admin"
                    jcr:primaryType="nt:unstructured"
                    sling:resourceType="granite/ui/components/coral/foundation/container" jcr:created="Thu Jun 17 2021 19:55:05 GMT+0000">
                <items jcr:primaryType="nt:unstructured">
                    <textUno
                            jcr:primaryType="nt:unstructured"
                            sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
                            fieldLabel="Text Uno"
                            name="./textUno"
                            cq:lastModified="{Date}2023-03-13T09:30:00.000+01:00"/>
                    <textDuo
                            jcr:primaryType="nt:unstructured"
                            sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
                            fieldLabel="Text Duo"
                            name="./textDuo" cq:lastModified="{Date}2023-03-13T09:30:00.000+01:00"/>
                </items>
            </column>
        </items>
    </content>
</jcr:root>

After

Following the cleanup by JCR Cleaner, all the runtime generated properties from the above file will be removed. Besides that, the content of the file wil be prettified in places where the removed properties were located so that the final result will look the following way:

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
          xmlns:jcr="http://www.jcp.org/jcr/1.0"
          xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
          xmlns:cq="http://www.day.com/jcr/cq/1.0"
          jcr:primaryType="nt:unstructured"
          jcr:title="Properties"
          sling:resourceType="cq/gui/components/authoring/dialog">
    <content
            jcr:primaryType="nt:unstructured"
            sling:resourceType="granite/ui/components/coral/foundation/fixedcolumns">
        <items jcr:primaryType="nt:unstructured">
            <column
                    jcr:primaryType="nt:unstructured"
                    sling:resourceType="granite/ui/components/coral/foundation/container">
                <items jcr:primaryType="nt:unstructured">
                    <textUno
                            jcr:primaryType="nt:unstructured"
                            sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
                            fieldLabel="Text Uno"
                            name="./textUno"/>
                    <textDuo
                            jcr:primaryType="nt:unstructured"
                            sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
                            fieldLabel="Text Duo"
                            name="./textDuo"/>
                </items>
            </column>
        </items>
    </content>
</jcr:root>

2.2.2. Example with Custom Properties

It was explained above that JCR Cleaner has a set of default names of runtime generated properties that are removed by default. However - as it will be elaborated in the next section - that default set can be customized. For instance, the file below contains 2 runtime generated properties: jcr:createdBy and jcr:isCheckedOut. Nevertheless, if JCR Cleaner is configured in such way that only jcr:createdBy property should be removed, it will be the only deleted property in this file:

Before:
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
          xmlns:jcr="http://www.jcp.org/jcr/1.0"
          xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
          jcr:primaryType="nt:unstructured"
          jcr:title="Properties"
          jcr:createdBy="admin" (1)
          jcr:isCheckedOut="{Boolean}true" (2)
          sling:resourceType="cq/gui/components/authoring/dialog"/>
After:
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
          xmlns:jcr="http://www.jcp.org/jcr/1.0"
          xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
          jcr:primaryType="nt:unstructured"
          jcr:title="Properties"
          jcr:isCheckedOut="{Boolean}true" (2)
          sling:resourceType="cq/gui/components/authoring/dialog"/>
  1. jcr:createdBy - will be removed

  2. jcr:isCheckedOut - will be kept

3. Usage

3.1. pom.xml

To use JCR Cleaner, add it to the plugins section of a relevant pom.xml file in an AEM project. Typically, it should be a parent pom.xml file located in the repository root.

JCR Cleaner has a single predefined goal: clean-jcr. This goal should be specified in the plugin declaration:

<plugins>
  ...
  <plugin>
    <groupId>eu.ciechanowiec</groupId>
    <artifactId>jcrcleaner-maven-plugin</artifactId>
    <version>1.1.0</version>
    <executions>
      <execution>
        <goals>
          <goal>clean-jcr</goal>
        </goals>
      </execution>
    </executions>
  </plugin>
  ...
<plugins>

3.2. Configuration

JCR Cleaner includes a default configuration that will suffice for the vast majority of AEM projects. However, this configuration can be customized as follows:

<plugins>
  ...
  <plugin>
    <groupId>eu.ciechanowiec</groupId>
    <artifactId>jcrcleaner-maven-plugin</artifactId>
    <version>1.1.0</version>
    <executions>
      <execution>
        <goals>
          <goal>clean-jcr</goal>
        </goals>
      </execution>
    </executions>
    <configuration>
      <isEnabled>false</isEnabled> (1)
      <fileNameRegex>new-.+</fileNameRegex> (2)
      <excludedAbsPathRegex>.*site-archive.*</excludedAbsPathRegex> (3)
      <namesOfPropertiesToRemove> (4)
        <namesOfPropertyToRemove>jcr:created</namesOfPropertyToRemove>
        <namesOfPropertyToRemove>jcr:createdBy</namesOfPropertyToRemove>
      </namesOfPropertiesToRemove>
    </configuration>
  </plugin>
  ...
<plugins>
  1. isEnabled - true if the plugin should be enabled and perform cleaning when executed; false otherwise. By default, this value is true, which means that by default the plugin is enabled.

  2. fileNameRegex - Regex for file names. Only content of files whose names match the specified regex will be subject to cleaning. The default value is .content.xml, which is the default name for DocView files.

  3. excludedAbsPathRegex - Regex for absolute paths to exclude. Content of all files whose absolute paths match the specified regex will be excluded from cleaning. By default, this value is not specified, which means that by default there are no files excluded from cleaning on the base of their absolute paths.

  4. namesOfPropertiesToRemove - Names of properties of JCR nodes that should be removed from the matched files. Only properties that have the names specified in this collection will be removed. By default, those names are:

    • cq:lastReplicated

    • cq:lastReplicatedBy

    • cq:lastReplicationAction

    • cq:lastModified

    • cq:lastModifiedBy

    • cq:lastPublished

    • cq:lastPublishedBy

    • jcr:lastModified

    • jcr:lastModifiedBy

    • jcr:created

    • jcr:createdBy

    • jcr:isCheckedOut

    • jcr:uuid

3.3. Execution

  1. By default, execution of JCR Cleaner is bounded to the generate-resources phase of the default lifecycle of a Maven build. It means that by default the plugin will be executed every time when mvn clean test, mvn clean package or mvn clean install commands are run.

  2. During execution JCR Cleaner will analyze the relevant DocView files and perform targeted removing of runtime generated properties.

  3. To execute JCR Cleaner separately from the build, run the following command:

    mvn jcrcleaner:clean-jcr

    You can add a -X flag to the command above to run it in a debug mode:

    mvn jcrcleaner:clean-jcr -X
  4. Given an average AEM project with 2,000 DocView files, the execution of JCR Cleaner takes approximately 10 seconds. To minimize the build time, you can configure the plugin to be disabled by default (set <isEnabled> to false) and run it manually at regular intervals.

4. License

The program is subject to MIT No Attribution License

Copyright © 2023-2024 Herman Ciechanowiec

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so.

The Software is provided 'as is', without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the Software or the use or other dealings in the Software.

About

Maven plugin that cleans Adobe Experience Manager (AEM) projects by removing runtime generated properties of Java Content Repository (JCR) nodes

Resources

License

Stars

Watchers

Forks

Packages

No packages published