Tag Archives: content architecture

Ways to achieve content reuse in AEM

Whenever an AEM project starts, you have a few important decisions to make. I already wrote about content architeture (here and here) and its importance to a succesful project and an efficient content development and maintenance process. A part of this content architecture discussion is the aspect of content reuse.

Content reuse happens on every AEM project, often it plays a central role. And because requirements are so different, there are many ways to achieve content reuse. In this blog post I want to outline some prominent ways you can use to reuse content in AEM. Each one comes with some unique properties, thus pay attention to them.

I identified 2 main concepts of content reuse: Reuse by copy and Reuse by reference.

Reuse by copy

The AEM multisite manager (MSM) is probably the most prominent approach for content reuse in AEM. It’s been part of the product for a long time and therefor a lot of people know it (even when you just have started with AEM, you might came across its idioms). It’s an approach which creates independent copies of the source, and helps you to keep these copies (“livecopies”) in sync with the original version (“blueprint”) . On the other hand side you still can work with the copies as you like, that means modify them, create and delete parts of the pages or even complete pages. With the help of the MSM you can always get back to the original state, or a change on the blueprint can be propagated to all livecopies (including conflict handling). So you can could call this approach “managed copy”.

The MSM is a powerful tool, but comes with its own set of complexity and error cases; you and your users should understand how it works and what situations can arise out of it. It also has performance implications, as copies are created; also rolling out changes on the blueprint to livecopies can be complex and consume quite some server resources. If you don’t need have the requirement to modify the copies, the MSM is the wrong approach for you!

Unlike the MSM the language copy approach just creates simple copies; and when these copies have been created there is no relationship anymore between the source and the target of the language copy. It’s an “un-managed copy”. Personally I don’t see much use in it in a standalone way (if used as part of a translation workflow, the situation is different).

Reuse by reference

Reuse by reference is a different approach. It does not duplicate content, but just adds references and then the reference target is injected or displayed. Thus a reference will always display the same content as the reference target, deviations and modifications are not possible. Referencing larger amount of content (beyond the scope of parts of a single page) can be problematic and hard to manage, especially if these references are not marked explicitly as such.

The main benefit of reuse by reference is that any change to the reference target is picked up immediately and reflected in the references; and that the performance impact is negligible. Also the consistency of the display of the reference with the reference target is guaranteed (when caching effects are ignored).

This approach is often used for page elements, which have to be consistent all over a site, for example for page headers of footers. But also the DAM is used in this way, even if you don’t embed the asset itself into the page, but rather just add a reference to it into the page).

If you implement reuse by reference, you always have to think about dispatcher cache invalidation, as in many cases a change to a reference target it not propagated to all references, thus the dispatcher will not know about it. You often have to take care of that by yourself.

Having said that, what are the approaches in AEM to implement reuse by reference?


Do it on your own: In standard page rendering scripts you already do includes, typically of child nodes inside the page itself. But you can also include nodes from different parts of the repository, no problem. That’s probably the simplest approach and widely used.

Another approach are Content Fragments and Experience Fragments. They are more sophisticated approaches, and also come with proper support in the authoring interface, plus components to embed them. That makes it much easier to use and start with, and it also offers some nice features on top like variants. But from a conceptual point if view it’s still a reference.

A special form of reuse by reference is “reuse by inheritance“. Typically it is implement by components like the iparsys or (when you code your own components) by using the InheritanceValueMap of Sling. In this case the reference target is always the parent (page/node). This approach is helpful when you want to inherit content down the tree (e.g from the homepage of the site to all individual pages); with the iparsys it’s hte content of a parsys, with the InheritanceValueMap it’s properties.

What approach should I choose?

The big differentiator of the reuse by copy and reuse by reference is the question if reused content should be adapted or changed at the location where it should be reused. As soon as you need to have the requirement “I would like to change the content provided to me”, the you need to have reuse by copy. And in AEM this normally mans “MSM”. Because content is not created once, but needs to be maintained and updated. At scale MSM is the best way to do it. But if you don’t have that requirement, use reuse by reference.
You might even use both approaches, “reuse by copy” to manage the reuse of content over different sites, and “reuse by reference” for content within a site.

Your friendly business consultant is a can help you find out which reuse strategy makes sense for your requirements.

Sling Context-Aware configuration (part 7): A conclusion

This the part 7 of my small series on Sling Context-Aware configuration, and probably the final posting of it. Time for a conclusion.

CAC adresses a need I often see in projects: having configuration which is neither OSGI config nor “real”content, but something in between. Something a super-author is supposed to work with, which can be changed on runtime without the need to have admin privileges. And it solves these requirements quite well.

The design is elegant and extensible, the API easy to use (of course the documentation could be improved, but that’s probably something which every opensource project could benefit from. And the fact, that there is the wcm.io toolbox which adds even more functionality is absolutely great; thanks Stefan and team!

The only problem I see is AEM itself. The fact that you need to add a thirdparty software to write the configuration in a way that it can be integrated into the workflow is something one can question. I would expect that AEM offers this capability out of the box. And when we are it, I would hope that the use of CAC within the product itself will increase over time.

But besides that, it’s an overall cool thing. Finally a good way to get rid of these homegrown administration frameworks. Enjoy Sling Context Aware Configuration!

Sling Context-Aware configuration (part 6): replicate CA-Config

In the last blog posts we solely focused on the authoring case, where an author edits content and config. But I haven’t discussed yet how this CA-Config gets to the publish. As such config can be changed on the fly by authors, we cannot rely on deployment processes, but it has to fit into regular processes triggered and monitored by regular users.

And to make this happen, we will use an out-of-the-box functionality of AEM, replication. Replication is limited from an UI perspective to assets and pages, and we don’t want to change that for config.
That means, that we have to wrap all config into a cq:Page to make that work. Unfortunately Sling does not know anything about AEM pages and their structure (the page node and the jcr:content node), thus writing the configuration into an AEM page is not possible (since AEM 6.3 reading from pages is supported).

Fortunately the wcm.io toolbox can help us here as well. With the optional “PagePersistenceStrategy” ) you can read and write the configuration in the jcr:content node of the page. The code for is part of the “caconfig.extension” package and should be deployed into AEM, and then a OSGI configuration for io.wcm.caconfig.extensions.persistence.impl.PagePersistenceStrategy needs to be set (enabled=true).

(I udated my github project on ca-config and added all relevant pieces, you just need to enable the configuration for this service in the ui.apps module; I left it turned off so it does not break the functionality I outlined in earlier posts).

But how does this help us here? The problem are the references to the configuration; when you activate a page by default AEM wants to activate also all referenced pages and assets of the page which are not yet activated. But if a reference to a simple node is not considered and never listed.
But this does only work for directly referenced pages. If change a page, and its parent holds the sling:configRef node, the simple ReferenceSearch functionality of AEM does not help. But for this there is a custom ReferenceSearchProvider by wcm.io, which is turned on by default.

Now you have a chance to edit your configuration on author and then activate it afterwards. It’s still a bit clumsy, but at least mangeable as part of an authoring workflow.

And then I discovered a small feature, which has been recently added to the wcm.io Configuration Editor (version 1.3.0). The editor has now a “Publish this page” button, which claims to activate the configuration and all referenced CA Configurations. But unfortunately, it’s not working properly yet. But when it’s fixed, it can help a lot.

Sling Context-Aware configuration (part 5): thoughts on a production layout

In the past blog posts of this series I outlined some of the capabilities of the Sling Context-Aware configuration. I was focussing on the demo aspect of it, and ignored aspects of it which are relevant when we talk about the use of it in an production environment.

(Note: There is not too much experience using Sling CA Config in wider production use. In this blog post I discuss these based on my experience with the product I gained in many projects; and I assume that it will work. If you have different experience with, I am interested to hear from them!)

For example I placed the configuration editor in /content/ca-config/en/configuration-page, while the configuration itself was located somewhere in /conf. Also the WCM.io editor can only edit the configuration for the context it is located in; that means, that your editor at /content/ca-config/configuration-page cannot modify the configuration applicable to the content context /content/ca-config/en, but you have to have a dedicated configuration editor located in that context.

Let’s stick with /conf in this blog post . Every code and every user which needs to consume configuration has to have read access to /conf; and to make inheritance working, you have to have read access to the configuration you are inheriting from as well. Generally spoken, you need to provide read access to a large part of /conf to all users. Which is typically not a problem.

But if you want to prevent access to certain parts of /conf but still be able to use them as part of Context Aware Configuration you have to trick a bit.
Imagine the case, that you want to store password data for certain backend system in CA-Config. You want to make them read- and writable only to the group which administers this backend access, but not for other users. But nevertheless all users should be able to use this backend service.

In that case the best way is to hide the access to the backend system in an OSGI service which utilizes a service-user to access these CA-Config resource in /conf. Whenever an user access this backend system, he will utilize this service and its service-user to read the configuration, and therefor this region of /conf can be locked down.

In the next blog post I want to show you another aspect of “making CA-Config production ready”: Publishing configuration.

Sling Context-Aware Configuration (part 4) — using inheritance

Till now this series of blog posts has only worked with configuration which has been explicitly set. But when working in hierarchical systems (like the AEM content tree) it’s often useful when you can inherit things from a parent container/concept. That means, that a specific setting on a node has effect on a complete subtree below that node instead of just the single node.

And of course Inheritance works with CA-Config as well. For easier demonstration I updated the demo content at https://github.com/joerghoh/cqdump/tree/master/ca-config-example in a way, that we can edit the configuration at 3 different locations:

  • /contnet/ca-config/configuration.html
  • /content/ca-config/en/configuration.html
  • /content/ca-config/fr/configuration.html

We always use the WCM.io configuration editor I introduced in the last post.

For demo purposes I also created a new configuration “de.joerghoh.cqdump.caconfig.inheritance.InheritanceDemoConfig” (code). Be aware that the configuration editor only works when the Pojo classes for the configuration are properly annotated.

Ok, let’s get started.

Open the configuration at /contnet/ca-config/configuration.html, hit the “add” button at the top and select the “Configuration to demo the inheritance” configuration. Then change the value of the “aProperty” configuration, check the “enable property inheritance” box and press “Save”.

caconfig-inheritance-globallevel

You have created a configuration which should be inherited down to the other levels. Specifically we should be able to see it at /content/ca-config/en/configuration.html. So let’s check it.

caconfig-inheritance-locallevel

On a repository level only the property “sling:configPropertyInherit” has been added to /conf/ca-config/content/sling:configs/de.joerghoh.cqdump.caconfig.inheritance.InheritanceDemoConfig.

caconfig-inheritance-crxdeview

It is that simple to allow configuration inheritance. And there are many usecases where this can be useful.

If you look back, we can build this functionality also the InheritanceValueMap. But right now we only scratched the surface of the CA-Config functionality. But before we dig deeper into it, I will discuss some aspects of Sling CA-Config for production usecases.

Sling Context-Aware configuration (part 3): using a UI

In the last post I gave you a small tour how you can create your context-aware configuration. And you always used CRXDE Lite to do changes on your configuration. Which is ok for testing and during development, but as this configuration is made to be changed (otherwise we might have put it into OSGI, do you remember?), we need to provide a better way to do it. Something suited also for less-technical users.

Steffen Seifert (of the Adobe partner Pro!Vision) started the project wcm.io and contributed there a lot of interesting stuff and extensions for AEM. And they also developed an editor for CA-Config (http://wcm.io/caconfig/editor/index.html).

Ok, so let’s use it. The integration is very simple:

  • Add the dependency to the io.wcm.caconfig.editor.page package and add it as subpackage to your ui.apps package.
  • Add a static template which uses “wcm-io/caconfig/editor/components/page/editor” as resourceType.
  • And use this template.

(I added these steps to my github example code at https://github.com/joerghoh/cqdump/tree/master/ca-config-example).

When you open this page at /content/ca-config/en/configuration.html, you see all available configurations for this bucket and when you click on the configuration you can edit it (including saving back to the correct location in the repository).

 

 

 

 

Cases like “the user does not have the permission to write this configuration” are also handled gracefully.

So the WCM.io configuration editor gives you a nice way to let users edit configuration without using CRXDE Light.

In the next blog post I want to introduce configuration inheritance which can help you to reduce the amount of configuration work.

Sling Context-Aware configuration (part 2)

Update (April 30th, 2018): I changed the name of the configuration item to align with with future development. Next to this change I also added a few bugfixes in the git repo.

In the last 2 blog (1,2) posts I talked about why a more content-oriented and content-centric way to store configuration is useful. And I also described 2 scenarios in which CA-Config can actually be used. Now I want to demonstrate how Sling Context-Aware Configuration can actually be used.

Let’s take the example from last blog post: For the approval workflow we want to configure the approval group externally via Sling CA-Config, so it can be configured per subtree/page. I use AEM 6.3 here, but it should work with 6.2 as well.

In the next steps I will guide you through the steps required to implement these steps. For reference the complete project is available at https://github.com/joerghoh/cqdump/tree/master/ca-config-example.

  • Create a structure in /conf where you would like to store your configuration. In my example the content lives in /content/ca-config with the subites “en” and “fr”, and for /conf I choose /conf/ca-config/content/.
  • Create a property “sling:configRef” with the value “/conf/ca-config/content” at /content/ca-config/jcr:content/. This links your content to the configuration.

In the CA-Config lingua you created a configuration bucket at /conf/ca-config/content, which is referenced by /content/ca-config.

caconfig-configRefs

And that’s it. Now every lookup through Sling CA-Config will lookup there.

Now let’s create some code which is using such a configuration. I created a DynamicParticipant WorkflowStep named “CaConfigParticipantStepChooser“, which assigns the workflow to a group defined by CA-Config. As you can see there a POJO class “CaConfigParticipantStepChooserConfig” is used to store the config read from CA-Config.

caconfig-wfstep

When you run this code, it will try to lookup the config property “approverGroup” (which is derived from the field name in CaConfigurationParticipantStepChooserConfig), but it returns the default value “admin”, as we haven’t provided any configuration value yet.

Now let’s assume that this workflow step, if it is executed on content below /content/ca-config, should assign the workflow to the group “content-authors“. Create a folder “/conf/ca-config/content/sling:configs” and add a node with the name “de.joerghoh.cqdump.caconfig.workflow.CaConfigParticipantStepChooserConfig“; add the property “approverGroup” and set it to the value “content-authors“.

caconfig-configsetting

To validate this setting there is a small tool in the OSGI webconsole.

  • Goto localhost:4502/system/console/slingcaconfig (in the navigation it’s “Sling” => “Context-Aware Configuration”)
  • enter “/content/ca-config” as content path
  • Enter “de.joerghoh.cqdump.caconfig.workflow.CaConfigParticipantStepChooserConfig” as “Other config name”.
  • And press “OK”.

 

It will display that that for this configuration name a property “approverGroup” with the value “content-authors” can be resolved. Which is exactly what we want.

When you test the workflow now, the workflow will be assigned correctly to the “content-authors” group.

To demonstrate the flexibility of context-aware configuration, let’s create a new folder /conf/ca-config/content/fr/sling:configs, add a node “de.joerghoh.cqdump.caconfig.workflow.CaConfigParticipantStepChooserConfig” and add a property “approverGroup” with the value “contributors“. Additionally add the property”sling:configRef” with the value “/conf/ca-config/content/fr” to the /content/ca-config/fr page. When you start the workflow now on /content/ca-config/fr the workflow will be assigned to the contributors group. And of course this also works for every page below /content/ca-config/fr.

(An important remark here: The paths are choosen arbitrarily. Especially in /conf you are very flexible, and the structure there does not necessarily need to match the structure below /content. I just choose the same structure for a better understanding of this example, and for convenience reason I recommend you to do the same.)

Congratulations, you are now able to use Sling Context-Aware Configuration! You can now freely package /content to move it to another stage, and the per-stage approver groups do not need to be re-adjusted.

But to be honest, this is a very simple way, and it has some drawbacks:

  • You need to use CRXDE to maintain the properties in /conf.
  • The need to create sling:configRef nodes for all buckets.

Let’s try to address these topics another time, in the next blog post.