As of AEM 6.1 you may have noticed the new
/conf
node at the root level along with
/content
,
/etc
,
/apps
and
/libs
. Over time the
/etc
folder's responsibility has expanded and configurations have been moved in an effort to clean it up a little. Whereas prior to 6.1 you would have stored site configurations in
/etc
, it's now recommended that you store those configurations under
/conf
. Along with the new node structure Adobe has provided a valuable, yet simple, utility to help manage these new site configurations in
com.adobe.granite.confmgr.ConfMgr
and
com.adobe.granite.confmgr.Conf
.
Read the Adobe provided documentation for
Conf and
ConfMgr.
As an example, let's host a multi-tenant AEM instance with three projects and one subproject:
Aviato,
Pied Piper,
Hooli and subproject
Hooli XYZ.
/content/aviato
/content/piedpiper
/content/hooli
/content/hooli/hoolixyz
Add a
cq:conf
property of type
String
at the root of every project page and point it to the site's corresponding configuration path. A global configuration will be used if a
cq:conf
property is not specified as is the case for
Aviato in this example.
/content/piedpiper/jcr:content/@cq:conf = "/conf/tentants/piedpiper"
/content/hooli/jcr:content/@cq:conf = "/conf/tentants/hooli"
/content/hooli/hoolixyz/jcr:content/@cq:conf = "/conf/tentants/hooli/hoolixyz"
Conf
respects relative paths as it pertains to the
cq:conf
property. Therefore, the following settings could also be used:
/content/hooli/jcr:content/@cq:conf = "/conf/tentants/hooli"
/content/hooli/hoolixyz/jcr:content/@cq:conf = "hoolixyz"
The tree structure of
/conf
should closely resemble
/content
. The configuration nodes should be of type
cq:Page
with the configuration properties on the
jcr:content
sub-nodes. All configurations start below the path specified in
cq:conf
with the addition of
/settings
.
/conf/global/settings/socialmedia/facebook/jcr:content/@enabled = false
/conf/tenants/piedpiper/settings/socialmedia/facebook/jcr:content/@enabled = true
/conf/tenants/hooli/settings/socialmedia/facebook/jcr:content/@enabled = true
/conf/tenants/hooli/hoolixyz/settings/socialmedia/facebook/jcr:content/@enabled = true
/conf/global
is special as it provides an ultimate fallback in the
Conf
inheritance mechanism. It this example
Aviato does not have its own config, so it would use the global configs.
/conf/tenants
is not dictated by AEM, but is a convention worth following to help prevent namespace collision in multi-tenant scenarios.
Use a
Conf
object to get a site configuration. The
Conf
object is usually obtained by adapted a Resource or Page using Apache Sling's adaptTo() method. Once you have the site's
Conf
object, you can get any number of predefined ValueMaps and properties. Notice that the ValueMaps cannot be modified – they are read-only. Properties are retrieved from the ValueMap in the familiar way of passing in either a default value or the class type.
The
Conf
object you retrieve is based on the path of the resource. So if have a Sling Model or WCMUsePojo and you adapt the current page, you will get a different
Conf
than if you adapt the current resource.
As you can see, the
ConfMgr
is a fairly lightweight and transparent helper. Everything is in the JCR, so configurations can be accessed through normal channels such as a Resource Resolver. However, there are three main benefits to using
com.adobe.granite.confmgr.Conf
.
The most obvious advantage is the built in inheritance management. For instance, the site for
Hooli might have ten configurations. If the subproject
Hooli XYZ overwrites only one of those configurations, it will use the other nine provided by
Hooli. If the
Conf
object is looking for a config that doesn't exist in the subproject or project, it will look under
/global
.
Conf
inherits and overwrites at the node level, not the property level.
Conf
handles null values for you. If the configuration, ValueMap or individual properties are null, you can continue without null checks and you won't get a Null Pointer Exception.
Conf
respects ACLs and multi-tenant privacy. If you setup your ACLs correctly, a shared component between two tenants will still only be able to access the configurations of the current tenant.
Conf
does not allow relative paths that try to move up the tree and into another tenant (i.e. "../../differentSite/foo/bar").
ConMgr
can be used as a service.
When used as a service, a Resource Resolver can be passed in which allows the use of Sling Service Accounts, thereby respecting ACLs on the configurations.
All inherited configurations can be obtained by using the
Conf#getList()
method, which returns a List of ValueMaps. Configurations can also be chosen explicitly by using a Resource Resolver to get the specific resource.
Thanks to Alex Klimetschek (
@alexkli) for sharing his knowledge of /conf of which much of this blog post is based on.