Working with Properties in DX

Properties are a way to configure your application.  Most properties are stored as key/value pairs in a file that can be defined as one monolith property file or broken down into multiple property files.  Using property files have two major advantages:

  • Centralized location to manage all configurations settings
  • Eliminate hard coded configurations in code

With the two above advantages, working with properties is very important especially in large enterprise applications.  In Jahia DX, there are three main ways to work with properties:

  • OSGi Configuration
  • Spring Configuration
  • Site Settings

At this time, we will be covering on how to work with OSGi configurations.  But before we begin, we will use two Apache Felix subprojects:

NOTE: iPOJO and Blueprint can be used in replace of SCR to manage configurations.  However, this is outside of this blog and may be revisited later using the above APIs.

OSGi Console

The OSGi console will allow us to upload and set up the SCR OSGi bundle.  In order to get to the console, please go to http://localhost:8080/tools/osgi/console/bundles.  This will allow us to install and activate the SCR sub project.

sajid-1.png

sajid-2.png

POM.xml

Next we need to prepare our project to include a dependency so that we can use annotations to map listeners and declare services.  Here we are including the SCR annotations dependencies.

<dependencies>

  <dependency>

      <groupId>org.apache.felix</groupId>

      <artifactId>org.apache.felix.scr.annotations</artifactId>

      <version>1.10.0</version>

      <scope>provided</scope>

  </dependency>

</dependencies>

Next we set up the build plugin to generate the OSGi descriptor file that will be declare and bind the services in an XML format. It will also create a configuration file that will allow the configuration admin to use to allow mapping to the properties file and set up a form UI for each property.

<plugin>

  <groupId>org.apache.felix</groupId>

  <artifactId>maven-scr-plugin</artifactId>

  <version>1.22.0</version>

  <executions>

      <execution>

          <id>generate-scr-scrdescriptor</id>

          <goals>

              <goal>scr</goal>

          </goals>

      </execution>

  </executions>

</plugin>

Configuration Admin

Using configuration admin in OSGi is not just setting up the services on startup, It’s powerful enough to update all services that rely on this properties when it's updated real time.  The configurations are all stored in the digital-data/modules folder that is scanned for bundles or configurations changes.  Using the configuration admin console to change the properties on the fly will only update the properties in the digital-data/modules and not the properties files in your source location.  Therefore, limit the use of real time updates to quick POC since property changes will not get persisted in your source safe.  Also, allow the configuration admin to manage the properties for service since it restart services when it detects property changes.

private void loadConfiguration(final Bundle bundle, final Enumeration<URL> urls) throws IOException {

  if (urls != null) {

      ...


          // Loop through the urls.

          while (urls.hasMoreElements()) {

              ...


              // Remove the CFG from the file name to use to get the configuration object for the service.

              final Configuration configuration = configurationAdmin

                      .getConfiguration(fileName.replace(CFG_EXTENSTION, ""));

              if (configuration != null) {

                  final Properties properties = new Properties();

                  properties.load(resource.getInputStream());

                  final Dictionary dictionary = new Hashtable<Object, Object>(properties);

                  configuration.update(dictionary);

              }

              ...

          }

      }

  }

}

The loadConfiguration method processes the properties files found in your OSGi bundle.  The user defined properties filename is the fully qualified class name that services the class.  The filename, excluding the extension, is the persistent identifier, PID, of the configuration and can be used to get the configuration object of the service.  This allows properties to be injected into the service to get ready for use, which is persisted in the digital-data/modules folder.

A service class can be registered as an OSGi service using the SCR annotations.  Below, this example service has one property that is set on start of this service.  The update method has a @Modified that is executed when configuration has changed.  The update method sets the apiUrl instance variable to be used by the service or can be used by other services or component through the getter method.

org.jahia.modules.configuration.example.service.ExampleService.java

@Component(

      label = "Example Service",

      description = "This is an example service to demo configuration",

      immediate = true,

      metatype = true

)

@Service(value = ExampleService.class)

public class ExampleServiceImpl implements ExampleService {


@Property(

      label = "API Url",

      description = "This is the api url key.",

      value="http://localhost:8080"

)

private static final String API_URL_PARAM_NAME = "api.url";

private String apiUrl;


public String getApiUrl() {

  return apiUrl;

}


public void setApiUrl(final String apiUrl) {

  this.apiUrl = apiUrl;

}


/**

*

* @param ctx

*/

@Activate

protected void start(final ComponentContext ctx) {

  LOGGER.info("Successfully registered ExampleService");

  ...

  update(map);

}



/**

* This method will be executed when the .cfg is updated.

*

* @param properties

*/

@Modified

protected void update(final Map<String, Object> properties) {

  this.apiUrl = (String) properties.get(API_URL_PARAM_NAME);

  LOGGER.info("apiUrl is {}", apiUrl);

}

}

When the maven goal install is executed, the SCR build plugin generates this service definition file that registers this example service as an OSGi service in the OSGi container.

org.jahia.modules.configuration.example.service.ExampleService.xml

<?xml version="1.0" encoding="UTF-8"?>

<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" immediate="true" name="org.jahia.modules.configuration.example.service.ExampleService" activate="start" deactivate="stop" modified="update">

  <implementation class="org.jahia.modules.configuration.example.service.ExampleService"/>

  <service servicefactory="false">

      <provide interface="org.jahia.modules.configuration.example.service.ExampleService"/>

  </service>

  <property name="api.url" value="http://localhost:8080"/>

  <property name="service.pid" value="org.jahia.modules.configuration.example.service.ExampleService"/>

</scr:component>

When using @Property the SCR plugin will create a metatype XML and properties file for the service during build time. These two files are used by the configuration admin present a ui to the user for visibility of the properties.

metatype/org.jahia.modules.configuration.example.service.ExampleService.xml

<?xml version="1.0" encoding="UTF-8"?><metatype:MetaData xmlns:metatype="http://www.osgi.org/xmlns/metatype/v1.0.0" localization="OSGI-INF/metatype/org.jahia.modules.configuration.example.service.ExampleService">

  <OCD id="org.jahia.modules.configuration.example.service.ExampleService" name="%org.jahia.modules.configuration.example.service.ExampleService.name" description="%org.jahia.modules.configuration.example.service.ExampleService.description">

      <AD id="api.url" type="String" default="http://localhost:8080" name="%org.jahia.modules.configuration.example.service.ExampleService.api.url.name" description="%org.jahia.modules.configuration.example.service.ExampleService.api.url.description"/>

  </OCD>

  <Designate pid="org.jahia.modules.configuration.example.service.ExampleService">

      <Object ocdref="org.jahia.modules.configuration.example.service.ExampleService"/>

  </Designate>

</metatype:MetaData>

 

metatype/org.jahia.modules.configuration.example.service.ExampleService.properties

org.jahia.modules.configuration.example.service.ExampleService.api.url.description=This is the api url key.

org.jahia.modules.configuration.example.service.ExampleService.name=Example Service

org.jahia.modules.configuration.example.service.ExampleService.description=This is an example service to demo configuration

org.jahia.modules.configuration.example.service.ExampleService.api.url.name=API Url

The configuration admin will provide the below ui for users to manage the properties for each service.


sajid-3.png

This property file below is defined by a developer.  The properties values are used to update the service.  This configuration update is handled but the loadConfiguration method in the Activator class.

org.jahia.modules.configuration.example.service.ExampleService.cfg

api.url=http://172.31.135.100:8081

Using the configuration admin really makes managing properties easier in the OSGi container.  This approach can be improved to be environment observant to load environment specific properties for complex deployment pipeline.

Next Release

In Jahia DX 7.2, we will be incorporating a more powerful way to manage configuration using Karaf.  This will result in another blog working with configuration in Karaf and Blueprint.

References


 

Author : Sajid Momin

Lead Solutions Architect

Sajid joined Jahia to help build a prominent Jahia presence in North America by supporting Jahia partners and providing elegant solutions with the Jahia platform.  He brings over 20 years of IT experience which includes 10 years in CMS to his current role.  He has played many roles in development from front-end to back-end which allows him to provide optimized solutions to a problem.  His experience comes from exposure to high profile project in industries which includes Automotive, Finance, Insurance, and Technology.