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:

  • /content/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.

 

 

 

Sling Context-Aware configuration

In the previous blog posting I outlined why there’s a need to store some configuration outside of OSGI and I named Sling Context-Aware Configuration (CA-Config) as a way to implement it.

CA-Config is a relative new feature of AEM (introduced with AEM 6.2) which stores the configuration inside the repository; but besides that it offers some features, which are handy in a lot of usecases:

  • You don’t deal with nodes and resources anymore, but only with POJOs. This kind of implementation completly hides where the configuration is stored, but the location of the config is stored as property on the node.
  • CA-Config implements an approach based on the repository hierarchy. If a configuration value is not found at the specified config location, the hierarchy is walked towards the root and further configurations are consulted, until a configuration is found or the root node is hit (in that case a default value for the configuration should be used).
  • the lookup process is very configurable, although the default one is sufficient in most cases.

A good example for this are content-related settings in a multi-tenant system. Imagine a multi-country and multi-language use-case (where for some countries multiple languages have to be supported); and in each country you should have different user groups, which act as approver in the the content activation process. In the past this was often done by storing it as part of the pages itself, but from a permission point of view it was possible for the authors to change it themselves (which typically only causes trouble). But with CA-Config this can be stored outside of the /content tree in /conf, but closely aligned to the content, and being able to match the weirdest requirements from a structural point of view; that means it is easily possible then have in one country 3 different approver groups for sections of the page, while in another just having 1 group covering all.

Another large usecase are sites, which are based on MSM livecopies. There is always content which is site-specific (e.g. contact adresses, names, logos, etc). And if this content is baked into a”regular” content page which is rolled out by MSM, the blueprint content will always overwrite the content which is specific to the livecopy. With CA-config it is easy possible to maintain these site-specific configurations outside of the regular content tree.

Before CA-Config all of this was implemented for very simple usecases using the InheritanceValueMap, which also implemented such a “walk-up-the-tree-if-the-property-is-not-found” algorithm. But it only looked up the direct parents and did not consider more structured property data as it is possible with CA-Config.

And there were a lot of custom implementations, which created “per site” configuration pages, typically next to the “homepage” page, and restricted access to it via ACLs or dispatcher/webserver/mapping rules. And of course these continue to work, but the access to these configuration data could be managed via the CA-Config as well (depending on the way how the data is stored either by just specifiying these as config path or by a custom lookup logic which can be configured for the CA-Config). And on top of that, these per-site configurations were per-site and not supporting the above mentioned case of having 3 different approver on different parts of the site.

So CA-Config is very flexible and efficient way to retrieve configuration data, which is stored in the repository. But it’s just for retrieving, there’s no prescribed way how the configuration data gets there.
While AEM comes with a configuration editor, it does not support writing the settings for CA-Config (at the moment it’s only designed for editable templates and workflows). WCM.io has a content package for download which allows you to edit these configurations in the authoring UI.

In the next post I will outline how easy it is to actually use CA-Config.

Content and configuration

When developing for AEM, you are probably aware of the OSGI configuration. Every service stores its configuration within OSGI, and it’s very easy to extract the configuration from there for consumption. Also there are a number of best practices around deploying and maintaining these configurations.
This is very handy way to store information, and many developers tend to store all kind of configurable data inside OSGI.

But should all configuration stored inside OSGI? Let me show you some downsides of this approach:

  • OSGI configuration can be only changed through the “admin” user (or a member of the administrator group) and through the OSGI webconsole. Typically these accounts are owned by IT, because any access to the OSGI webconsole grants you a lot of chances to mess up the system. A normal user should not have access to the OSGI webconsole, neither from a permission point of view nor from the networking point of view (lock down /system/console URLs! Limit access to it to an admin network!)
  • Modifying configurations directly in the OSGI webconsole is not best practice; it’s OK to test something in the staging environment that way, but it should never be used to make changes in production. OSGI configuration should always be deployed in a content package, and not be changed via webconsole.
  • In many cases changing the configuration of a service causes a restart of this service and all other services depending on this service. That means, that doing such a change in a running system can cause lots of errors while services are stopping and starting.

Based on that, it is clearly visible, that not every configuration piece should be stored inside OSGI configuration.

The question is then: What should be stored inside OSGI configuration?

  • Configuration which has a global impact. For example the search path of the SlingMainServlet has the impact to break the complete application.
  • Configuration which should be tested. Testing means not only validating that a single operation is working (like a backend connection test), but which rather requires the run of some test cases.

And what should not be stored as OSGI configuration?

  • Configuration which is supposed to change not in accordance with any release or deployment cycle. For example an access token for an external system, which needs to be updated every month.
  • Configuration which should be changed by regular authors, but should not carried out by the admin user. The admin user is often owned by IT, and IT is typically very happy if they are not bothered with unrelated tasks from content authors.
  • Configuration which pertains to content. For example configuration if a contact email should be displayed as part of the page footer. Although it might be a global decision (probably not!) it should not be configured via OSGI.
  • Configuration is tight to a specific environment. For example you need to have the hostname of your site, but on UAT this name is of course different from production.

Starting with AEM 6.2 Sling Context-Aware Configuration (short: CA-Config) can be used to store and retrieve such configurations in a very efficient and sling-ish manner. More details in the followup posting. So stayed tuned.