Expand my Community achievements bar.

Dive into Adobe Summit 2024! Explore curated list of AEM sessions & labs, register, connect with experts, ask questions, engage, and share insights. Don't miss the excitement.

best way to implement env vars or similar?

Avatar

Level 9

In the core module, rather than hard code things which might change, or are env specific, we want to pull them in.

E.g rather than 

final private String rootUrl = "https://somesite.com/api" 

In the classes,

We want to use something like.

String rootURL = getEnv("rootURL");

In a typical java app, getEnv would search for var set on java command line, set in a config file, set globally in an env var etc.

Ideally, it would be possible to edit these at runtime for a given env, in a UI, so as to not require a release cycle to edit.  Is there something built in?

 

=== update 1 ===

Thanks for the answers below.  It looks like the only way to do this is via osgi, which I was hoping to avoid due to its complexity and steep learning curve.  It looks like its primarily to implement pluggable services, rather than simple env vars.  I am currently looking for simpler options due to time limitations.  I assume we can extract the run mode in code, so I could put the config a class with some if statements.

 

=== update 2 ===

We have still not found a way to read an env var (configuration variable), as there appear to be no tutorials other than the one which requires hundreds of lines of code to create a custom OSGi service: https://aem.redquark.org/2018/10/day-12-creating-your-custom-osgi.html

There appear to be other methods, we just cant find a tutorial or guide (other than reference pages which only list out part of the picture, and don't contain enough information to implement an env var end to end in code (i.e. not using the various UIs which only affect the local dev instance).

 

15 Replies

Avatar

Community Advisor

Hi @TB3dock ,

 

In AEM, you can fetch these values using run modes.

For different environments, for example, stage, prod, you can set a run mode for the same. Please refer to the below URL for the run mode creation.

https://experienceleague.adobe.com/docs/experience-manager-65/deploying/configuring/configure-runmod...

 

You can create OSGI configuration in the AEM using OSGI annotation and its values can be saved in the AEM using run modes.So basically, runmode will work to set the value for different environments and respective values will be saved in runmodes as "sling:OsgiConfig" node in AEM.

 

URL to refer for creation of OSGI configurations.

http://aemlab.blogspot.com/2019/07/aem-create-osgi-configuration-factory.html

 

 

Configure OSGI with respect to a runmode.

https://helpx.adobe.com/in/experience-manager/6-3/sites/deploying/using/configuring-osgi.html

 

 

Avatar

Level 9

https://experienceleague.adobe.com/docs/experience-manager-65/deploying/configuring/configure-runmod... says how to configure run modes, but it doesnt saywhere vars can be defined, nor how they could be read unfortunately. This is the missing info for several similar articles.

 

I checked the source code for http://aemlab.blogspot.com/2019/07/aem-create-osgi-configuration-factory.html It seems to be creating some soft of service factory - its not setting or reading any env specific vars that I can find. 

Avatar

Community Advisor

@TB3dock 

If you are looking to use a place to configure the VIP url try this - com.day.cq.commons.impl.ExternalizerImpl

snbaem_0-1617716517275.png

You could add config xml files for each environment so that the code deployment handles this

 

You could do

Externalizer externalizer = resolver.adaptTo(Externalizer.class);
String url = externalizer.publishLink(resolver, UrlConstants.HTTP_PROTOCOL, "somepathorslash");

 

Avatar

Level 9

Thanks for the comprehensive reply. There are many posts on the configuration side, where files are located etc. e.g. https://experienceleague.adobe.com/docs/experience-manager-65/deploying/configuring/configure-runmod... and https://experienceleague.adobe.com/docs/experience-manager-65/deploying/configuring/configuring-osgi... 
However none talk about how you actually read these configuration values. Presumably I need to write a complete OSGI service, which is hundreds, or thousands, of lines of code to write and debug, just to read a few env vars?

Avatar

Community Advisor

I am not sure but its not necessary to write a service every time you need this. Wherever you need the full env url for say local, author or public facing VIP, the two lines of code added will suffice. Eg if you need it in a Sightly Model or Servlet or Scheduler, Filter so on..

 

See this if it helps - https://experienceleague.adobe.com/docs/experience-manager-64/developing/platform/externalizer.html?...

 

Avatar

Level 9
Thanks. Unfortunately, we need a few more env vars, such as timeouts, credentials etc.

Avatar

Community Advisor

Hi @TB3dock 

Timeouts, credentials also can be configured as part of the OSGi service like I showed in the above code. Does this not meet the requirements?

Basically this is how it works on AEM.

 

Thanks!

Avatar

Community Advisor

Hi @TB3dock 

You can go with the OSGi configuration approach for your use case.

Let's say you have an endPoint which will be different based on the environments, then you will need to create an OSGi service and and a service implementation class.

 

Now for each of the environment you can create run mode specific configuration which will hold the respective value for the OSGi service and can be modified at runtime either from crx/de or from system/console/configMgr.


Articles from Adobe:

https://experienceleague.adobe.com/docs/experience-manager-65/deploying/configuring/configuring-osgi...

https://experienceleague.adobe.com/docs/experience-manager-65/deploying/configuring/configure-runmod...

 

Please refer the below URL with reference to your use case:

http://www.sgaemsolutions.com/2017/07/migration-of-scr-annotations-to-osgi-r6.html

 

Here is an Example:

Service Class:

@ObjectClassDefinition(name = "Google reCAPTCHA Configuration", description = "Google reCAPTCHA Configuration for Someone")
public @interface GoogleReCAPTCHAConfigService {

@AttributeDefinition(name = "Google reCAPTCHA Site Key", description = "Google reCAPTCHA Site Key", type = AttributeType.STRING)
String siteKey() default "XXXX-kUAAAAAMKXX1234XgyZxMhXXXF";

@AttributeDefinition(name = "Google reCAPTCHA Secret Key", description = "Google reCAPTCHA Secret Key", type = AttributeType.STRING)
String secretKey() default "XXXX-kUAAAAAMKXXXXXXXXXXgyZxMhXXXF";

@AttributeDefinition(name = "Google reCAPTCHA Validation Endpoint", description = "Google reCAPTCHA Validation Endpoint", type = AttributeType.STRING)
String validationEndpoint() default "https://www.google.com/recaptcha/api/siteverify";

@AttributeDefinition(name = "Google reCAPTCHA Script URL", description = "Google reCAPTCHA Script URL", type = AttributeType.STRING)
String scriptURL() default "https://www.google.com/recaptcha/api.js";
}

Service Impl Class:

@Component(immediate = true, service = GoogleReCAPTCHAConfigServiceImpl.class,
property = {"process.label=Google reCAPTCHA Configuration", Constants.SERVICE_DESCRIPTION + "=Google reCAPTCHA Configuration", Constants.SERVICE_VENDOR + "=Someone"})
@Designate(ocd = GoogleReCAPTCHAConfigService.class)
public class GoogleReCAPTCHAConfigServiceImpl {

private GoogleReCAPTCHAConfigService config;

@Activate
protected void activate(GoogleReCAPTCHAConfigService config) {
this.config = config;
}

public String getSiteKey() {
return config.siteKey();
}

public String getSecretKey() {
return config.secretKey();
}

public String getValidationEndpoint() {
return config.validationEndpoint();
}

public String getScriptURL() {
return config.scriptURL();
}
}

 

OSGi configuration which will be kept under different run mode which will hold different values based on environments.

 

<?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"
jcr:primaryType="sling:OsgiConfig"
siteKey="\{4d3acabc434c7de1149d7d960\}" //Specific to dev or QA
secretKey="\{262ddd789fae4a86acb2f32b\}"//Specific to dev or QA
validationEndpoint="https://www.google.com/recaptcha/api/siteverify"
scriptURL="https://www.google.com/recaptcha/api.js"/>

 

Hope this helps!

Thanks!

 

Avatar

Employee Advisor

There are multiple ways to handle and manage configurations in AEM.

All of them have different use cases and a project specific decision depends on quite some different factors.

Let me mention three common methods:

 

  • OSGI Configurations
    As already mentioned, they can be added to your projects source code basis and be rolled-out through the regular deployment process. Configurations can be placed into folders that following a naming convention. Global configurations reside in a folder named "config" but you can bind configurations to run modes by adding them (e.g. "config.author", "config.production", "config.apac" or "config.author.production" - the most specific match has the highest priority). Apart from the built-in run modes, you can add as many custom ones as you need.
    The Apache Felix web console also provides a UI to manage OSGI configurations at runtime (/system/console/configMgr). But please keep in mind that it is recommended to keep your configurations with your projects source code and deploy them to the instance instead of manually managing them through the UI.
    This approach may be the closest one to your mentioned requirements.
  • Sling Context Aware Configurations
    However, there are other options as well, for example Context Aware Configurations (short: CA configs) [1,2]. As the name suggests, they provide a mechanism to make certain configurations aware of their context. Context is referring to the content structure within AEM here, so I guess this may not exactly be the right fit for your use case. I still wanted to mention the option to make you aware of it. There are also some extensions and an editing UI available from wcm.io [3].
  • Configuration Pages/Components
    Another common mechanism to manage configurations is to develop dedicated configuration templates and/or components. These are created and edited just as regular content by either "normal" content editors or some kind of power user (depending on the requirements) through the regular AEM authoring UI. You would usually have some kind of config page per tenant, country, or language. This approach is a good fit for configurations that change often (more frequent than you want to deploy configs from your source code repository) and/or for items that can/should be managed by regular AEM authors instead of developers.

 

Referring to your update:
You can get the current run modes through the SlingSettingsService [4], see also this post [5].

 

[1] https://sling.apache.org/documentation/bundles/context-aware-configuration/context-aware-configurati...

[2] https://experienceleague.adobe.com/docs/experience-manager-core-components/using/developing/context-...

[3] https://wcm.io/caconfig/index.html

[4] https://helpx.adobe.com/experience-manager/6-5/sites/developing/using/reference-materials/javadoc/or...

[5] https://experienceleaguecommunities.adobe.com/t5/adobe-experience-manager/how-to-get-current-run-mod...

Avatar

Level 9
Thanks for the comprehensive reply. Rearding OSGI Configurations, we have seen various articles about files which are used to store configs for OSGi services, but no info on how to read the data. We assume to use these we have to write, in java, our own services, which requires hundreds or thousands of lines of code to read 3 env vars. We have written some site wide config UIs, e.g. containing the key for google anlytics snippet, but this is again a lot of coding, and we have not found a tutorial on how to read these values in servlets (as opposed to actual pages).

Avatar

Level 9
The sling context aware stuff looks interesting, as there is not so much java code, although there are a lot of configuration steps to get it to work. However, we would need the config at the "content" layer, i.e. above tenant, and this doesnt seem to be an option?

Avatar

Level 9

We have come across "configurations" as they are needed to be able to use content fragments. Their relationship to the site structure is a bit of a mystery, and we failed to get content fragments working via configurations (when you try to edit a content framgment model, you just get a blank page - this was a separate post but no resolution). I didnt see in the links provide what these configurations have to do with env vars? i.e. configuration has 6 fields: title, name and 4 checkboxes.  The last half of this page: https://experienceleague.adobe.com/docs/experience-manager-cloud-service/implementing/developing/con... is a mystery - there is a lot of discussion about configuration, but no examples of how we might create a configuration item (an environment variable), where it would live in our GIT file structure, and how the code (e.g. a servlet) might read it. We have not found any end to end tutorials.  Also, the tutorials and docs only tell us how to create a configuration via the UI, so they will only exist in one local dev env. to be useful, we need to find a tutorial which tells us how to create a configuration in our source in git, so it can be deployed to several environments (not hand created on each).

Avatar

Employee Advisor
What I understand from your first question is that you are missing the link between the OSGI service and its according configuration (residing inside the OSGI context) on the one hand and the configuration files/nodes in the repository on the other hand. Correct? I’m not aware of a tutorial on this (maybe I’ll take this as a motivation to write one ;-), but I’ll try to outline the concept: On the one hand you have the OSGI service that needs to be configured. You define certain properties and annotate them accordingly. There are already a couple of links and examples in this thread and it sounds like you already went through them. This part is self-contained. As soon as you have your service up and running, you can configure it through the web console UI and your service can use the configured values. A simple service does not need that much boilerplate, a functioning skeleton can be done with < 100 lines of Java code. (I will refer to an example below.) The second part is about providing configurations to the service without using the web console. 
The first level of abstraction here is that you can create a specific node within AEMs CRX repository (jcr:primaryType should be set to “sling:OsgiConfig”). Usually, you place such a node below /apps/yourproject/config/ (for global configurations). Propagation to the OSGI context will be handled automatically for you based on the node name: it should start with the OSGI service’s PID (so basically the class name, e. g. “com.day.cq.commons.impl.ExternalizerImpl” for one of AEMs OOTB services). 
This is where run modes and environment specifics come into play. The name of the folder can be mapped to the run mode of your instance. So if you place a certain config in the “/apps/yourproject/config” folder, it will always be applied. If you place it into a folder named “config.author” it will only be applied if the instance has the “author” run mode. And so on. Please note: you will always deploy _all_ the configurations and folders to all you environments, but the once with irrelevant run modes will just not be applied and not be propagated to the OSGI context. They stay inactive in your CRX repository. You can now add your configuration nodes to your AEM Maven project and deploy them through the regular process. They will be stored in the repository and propagated to the OSGI context. If you need examples for the above, you may want to check the AEM Maven Archetype [1]. It will create a full AEM Maven project structure for you and also contains some basic examples. You will find a very simple scheduler service skeleton in the “schedulers” package of the “core” module that is configurable through the OSGI context (although it’s just the implementation of the Runnable interface; so to be fair: for a full custom service that can not be based on an existing interface you will need some more code to implement your own one). There is a dedicated Maven module “config” that holds all configurations for the generated project. It also comes with a couple of example configs, e .g. the project-specific logger configs. I hope that makes the mechanism a bit clearer to you. [1] https://github.com/adobe/aem-project-archetype

Avatar

Employee Advisor
FYI: I'll repost my answer to your other thread to allow for better readability.

Avatar

Employee Advisor

The right approach depends on the actual item that you want to configure.



Most relevant aspects IMO are:

  • Scope of your configuration 
(equal on all instances and environments; specific to a all instances of a certain environment; global for a single AEM instance; specific to a tenant on your AEM instance; more specific, e. g. country, language or otherwise tree-specific)
  • Frequency of updates to the configuration (and flexibility on that)
  • Who is responsible to work on the configuration (developer, power user, regular content author).

Looking at your example (that sounds like a remote service endpoint) I would assume that it’s:

  • Environment-specific (e.g. different between DEV, STAGE, PROD)
  • The same for all instances of one environment (author and publish)
  • The same for all tenants of the AEM instance
  • Not changing very often
  • Predefined by a developer

That all points towards OSGI configurations that can be differentiated through run modes to make them specific to environments.


I’ve outlined the general approach in your other thread [1] and pointed to the examples that come with the AEM Maven Archetype [2]. The generated project structure gives you all the boilerplate you need. You can modify it and should be able to get a working an configurable service skeleton in your core module with a matching configuration in your ui.config module with just a few lines of code.

 

Please note: The overall approach follows a different principle than querying environment variables from the operating system (e. g. like [3]) or reading configuration files from the filesystem (and thus moving the specifics to some kind of configuration management tool outside of AEM). The suggestions should fulfill your requirement and also follows AEM best practices but is not a 1:1 answer to the question on how to implement/query OS environment variables within AEM.

 

[1] https://experienceleaguecommunities.adobe.com/t5/adobe-experience-manager/aem-configuration-env-vars...
[2] https://experienceleague.adobe.com/docs/experience-manager-core-components/using/developing/archetyp...
[3] https://docs.oracle.com/javase/tutorial/essential/environment/env.html