Features I Like About CQ5 - Content Packages

In this blog series, I will point out some not so well known features of CQ5 and how to put them to use in your projects. This article is a longer one, but well worth reading.

Agreed, the content packages aren't really 'not so well known'. At least I hope. But they definitely are great, although sometimes tricky. I put a small article together for our internal training and knowledge base material, and I'll just republish it. Thanks a lot to Konrad Windszus and Santiago Pimentel for correcting and extending! Here we go.

What is a CRX package?

A CRX package is used to package up arbitrary content from a CRX repository. 'Packaging up' in this case means that you can select branches inside the content repositories which is then added to a file downloadable from the repository. That file can be uploaded to any instance and the content contained therein can be installed.

By this, you can easily backup content and copy it to any instance. And as CQ5 goes: Everything is content, therefore you can put users, configurations, code, binaries, content and anything else into those packages.

The file itself is a zip file, so you can even unpack it and review the content offline.

CRX Package Manager

The CRX Package Manager is the interface provided by CRX / CQ5 to manage and upload your packages. It is available at http://localhost:4502/crx/packmgr/index.jsp (given your instance is running at localhost port 4502).

 

The interface should be pretty intuitive; with the search options to the left and the packages mapping to those settings to the right. If you click on a package, you get some information and can perform actions:

The most important actions are:

  • Build - if you change the package, this is NOT reflected in the zip file immediately. The zip file is only created upon the build command, and always represents the state of the repository and package definition at the time the build was executed. Therefore: If you want to get the current state, you have to build the package first.
  • Install - this installs the content inside the package into the repository, overwriting all the content therein. If you for example have a path /apps/netcentric defined and that path does not contain any content in the package (it was empty when the package was built), then installing the package will remove all content from /apps/netcentric. There are no partial updates as well; if the package has the filter /apps/netcentric and some content therein (e.g. /apps/netcentric/components) then after install only that content will be available. If there is another content branch in the repository below the filter definition, then that content branch will be removed by installing the package (as long as the branch is not defined in the package as well).
  • Rewrap - The rewrap option triggers to update the configuration of the package (i.e., which filters are defined, title, description, meta-data, etc.), but it does not update the content therein.
    Coverage - The coverage shows what content of the repository is covered by the filter definition of the package.
  • Contents - The contents shows the content which is contained in the package at the moment.
  • Replicate - This publishes the package using the default replication agents - and installs it directly into those systems. After replicating, not only the package is available on the publish instances, but also the content inside the package is automatically deployed into the publish instance.

Package definitions

The main part of a package definition are the filter definitions:

Using the filter definitions it is possible to define which parts of the repository are included into the package.

Each filter consists of one path, which makes the package include all the content below that path, and a list of rules. A rule allows to define that some content below the path should be excluded or included; which content that is is defined by a globbing, i.e. you can define for example to exclude certain nodes which have some name by using an exclude globbing like .*/name(/.*)? It is important to know that this globbing is always mapped against the entire path, not starting from the path defined. If you start with an include rule, you define a white-listing approach, i.e. everything else is explicitly excluded. If you use an exclude as first rule, a black-listing approach is used, i.e. everything is added in but the excluded parts. It is very tricky to mix those two types of rules up, so better don't do it.

To further complicate things with the rules: They actually change the traversal behaviour. The typical pitfall is to have include rules for pages deeper down in the hierarchy only. This implicitly defines an exclude for all, and as no page is traversed if it is excluded, the included pages are not even considered. Enjoy your empty package.

Most of the other information is beautification, like title, description, thumbnail and so on.

Within the advanced tab of the package there are some functional settings, though:

  • The 'Requires' defines a notification which is displayed upon installing the package; it is mostly used by Adobe for hotfixes.
  • The 'AC Handling' defines how access control rules are installed. The default is to ignore them, so you do not transfer any access rights with packages.

How packages are used

The packages definitely are a developer’s tool, but a very powerful one.

Obviously, packages are pretty handy to manually copy some example content from one instance to another, or to create a backup if you want to play around with the content a bit.

But there's more to packages, but let's first highlight two more features of packages:

  • Contained Packages: If you add a filter definition to another package, that package is added to your 'master package' as well. If you install this master package, the packages contained therein are installed as well (if you tick the proper checkbox, that is). By this, you can aggregate multiple packages into one and deploy the entire code on the fly. Take care though, if you build the master package, it does not build the contained packages, but just takes the state of the latest build.
  • OSGi installation: If you add a jar file into a folder called 'install' in your package, then upon installing the package the .jar is deployed to the OSGi container - i.e. installed and run automatically. Therefore it is possible to deploy entire bundles using packages. Additionally, it is possible to configure OSGi services as well by providing certain nodes. The install folder may only be on the third level, though - if it's deeper down in the hierarchy, the bundle won't get installed.

Using packages with maven 

These two capabilities enable us to use packages for code deployment: An hierarchical structure reflecting the project setup is available via contained packages, and code implemented as bundles gets deployed in the install folder. That's what happens if you execute your maven build targets: The packaging 'bundle' generates an OSGi bundle, and the packaging 'war' list the content-package-maven-plugin as used plugin, therefore with the build two different artifacts get generated: the WAR file (only there to make Eclipse happy, as Eclipse does not know anything about content-packages) and the content-package itself.

Now it should be pretty obvious what the 'package.root' property in the pom.xml represents: It's the definition of the filter for the package. But a property is single value, while a list of filters may be required. These additional filters can be set up in the plugin configuration:

<configuration>
    <filters combine.children="append">
        <filter>
            <root>/a/second/path</root>
        </filter>
    </filters>
</configuration>

More information regarding the content-package-maven-plugin can be found in http://dev.day.com/docs/en/cq/current/core/how_to/how_to_use_the_vlttool/vlt-mavenplugin.html

Whenever you change something about the filter rules in your pom (either implicitly by adapting the package.* properties or by setting something within the plugin configuration, you have to do a maven clean, otherwise the changes will not be applied!

Dependencies to bundle projects can be embedded into the install folder of the final package by adding an <embeddeds> configuration. To this end, the target location (i.e. the path to the install folder) has to be defined as a <target> in the configuration of the content-package-maven-plugin. You also need to define the group-id, and you may define artifact-ids. The nice thing about it is the maven inheritance: The configuration of the embeddeds is inherited, and the group-id is automatically updated to the current group-id. So, you set the embeddeds definition up only once in your parent project pointing to the parent group-id; and then all bundles with the same group-id get embedded automatically into the folder defined in the parent package. To see how it works, just have a short look at the Effective POM of your local pom.xml. If you need to add multiple embeddeds configurations, be sure to give them unique names though.

Short example:

<configuration>
    <embeddeds>
        <embedded1>
            <groupId>your-groupid-1</groupId>
            <artifactId>your.artifact.id.1</artifactId>
            <target>/apps/project/install</target>
        </embedded1>
        <embedded2>
            <groupId>your-groupid-2</groupId>
            <artifactId>your.artifact.id.2</artifactId>
            <target>/apps/project/install</target>
        </embedded2>
    </embeddeds>
</configuration>

How content gets merged 

By default during installation of a package, the content existing in the corresponding paths in the content repository is removed first. As a second step, new content contained in the package and allowed by the filter is added. That installation mode is called "replace". You can use this mode to remove things with a package, by including an appropriate filter but not adding any actual content to the package.

You can influence the merge behaviour of new content and old content within the maven-content-package-plugin with the help of the element mode (sibling of element root within a filter) which can be either "replace", "merge" or "update". "replace" is the default as described above (unfortunately there is a bug in CQ 5.5 before Update 1, which made "merge" the default merging behaviour). Using "merge", the installation will never touch existing nodes, and only add new nodes. The "update" updates but doesn't remove existing nodes.

If some of the content is not covered by the filter rules but part of the project code (e.g. because you forget an additional filter rule), this content also will be installed in the "merge" mode. So if you come upon strange duplications of nodes in the repository upon installing you project via maven, make sure all the paths of the code are actually covered by the filters.

You cannot influence the merge mode within the Package Manager.

Package share

Finally, there is the package share. The package share is like an apps store for packages, provided by Adobe. Actually, it is not used by many, as CQ5 is not as wide spread as music or casual games. Not many companies have identified business models around the package share (e.g. by providing the integration to a webservice like translations.com for free, but having a fee on the actual usage of that service). And there are also some hotfixes, feature packs (hybris integration, language packs) and patches available for download from the package share.

Aside from that, there is no real community around the package share yet.

Next Blog Post

by Stefan Franck

Data Management,  Developer

Importing and Exporting Content

Importing and exporting structured content for bulk editing is one of the common requirements of every project. Check out these tips to make this easier.

Read more...
Stefan Franck