best way to implement env vars or similar?

Avatar

Avatar

TB3dock

Avatar

TB3dock

TB3dock

06-04-2021

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).

 

Replies

Avatar

Avatar

Rohit_Utreja

Avatar

Rohit_Utreja

Rohit_Utreja

06-04-2021

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

Avatar

snbaem

Avatar

snbaem

snbaem

06-04-2021

@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

Avatar

asutosh_j3

Avatar

asutosh_j3

asutosh_j3

06-04-2021

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

Avatar

TB3dock

Avatar

TB3dock

TB3dock

06-04-2021

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

Avatar

TB3dock

Avatar

TB3dock

TB3dock

07-04-2021

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

Avatar

snbaem

Avatar

snbaem

snbaem

07-04-2021

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

Avatar

TB3dock

Avatar

TB3dock

TB3dock

07-04-2021

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

Avatar

Avatar

asutosh_j3

Avatar

asutosh_j3

asutosh_j3

07-04-2021

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

Avatar

markus_bulla_adobe

Employee

Avatar

markus_bulla_adobe

Employee

markus_bulla_adobe
Employee

07-04-2021

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...