- Most often used OOB Factory configurations for example:
- Logger Factory/writer configuration (for log statements)
- Service user Mapper service Amendment (for using service resource resolver in projects)
- For multi-tenant projects, if we have generic service used across tenants with properties/attributes specific to the tenant.
- For single-tenant, generic service targeting all locales with properties/attributes specific to the locale. (where generic service -> holds logic in the lines of calling third party service for retrieving/updating information.)
- Create Object Class Definition(OCD) defining desired properties for the OSGI service. - OSGIFactoryConfigOCD.java
- OSGi Service/Factory service to retrieve config values. - OSGIFactoryConfigDemo/OSGIFactoryConfigDemoImpl.java
- Business logic to be developed as OSGI service/Servlet/Sling Model or any java class where OSGI factory service(created in #2) can be referenced. - In this example, have used servlet, OSGIFactoryConfigDemoServlet.java
OCD:
For the sample real-time use case mentioned above, let's say
@ObjectClassDefinition(name="OCD for Factory Config Demo", description="OCD for Factory config demo")
public @interface OSGIFactoryConfigOCD {
@AttributeDefinition(name="Endpoint URL", description="Sample Endpoint URL for connecting to third party", type=AttributeType.STRING)
String apiEndpoint() default "http://xyz.com";
@AttributeDefinition(name="API Key", description="Sample API key for
For the sample real-time use case mentioned above, let's say
- We need to configure API endpoint and AppId/any similar properties to access the same.
- Configure another property named Sitename or locale or any identifier based on the actual requirement and existing project set up.
@ObjectClassDefinition(name="OCD for Factory Config Demo", description="OCD for Factory config demo")
public @interface OSGIFactoryConfigOCD {
@AttributeDefinition(name="Endpoint URL", description="Sample Endpoint URL for connecting to third party", type=AttributeType.STRING)
String apiEndpoint() default "http://xyz.com";
@AttributeDefinition(name="API Key", description="Sample API key for
connecting to third party", type=AttributeType.STRING)
String apiKey() default "XYZ";
@AttributeDefinition(name="SiteName", description="Sample Site name used as an identifier for respective factory config", type=AttributeType.STRING)
String siteNameIdentifier() default "demosite";
}
OSGi/OSGi Factory service:
@Component(service=OSGIFactoryConfigDemo.class,immediate=true,configurationPolicy = ConfigurationPolicy.REQUIRE)
@Designate(ocd=OSGIFactoryConfigOCD.class, factory=true)
public class OSGiFactoryConfigDemoImpl implements OSGIFactoryConfigDemo {
@Activate
protected void activate(OSGIFactoryConfigOCD config) {
this.apiEndPoint = config.apiEndpoint();
this.apiKey = config.apiKey();
this.siteName = config.siteNameIdentifier();
}
}
At this stage when we build and deploy the code, we are ready to create multiple OSGI configs via Felix console or in the config folders by creating a node of type sling:OsgiConfig in the name of Service PID with a unique identifier, say -> Service PID-identifier. In this example, it is learnings.core.serviceimpl.OSGiFactoryConfigDemoImpl-demosite
Java class referencing Factory service:
String apiKey() default "XYZ";
@AttributeDefinition(name="SiteName", description="Sample Site name used as an identifier for respective factory config", type=AttributeType.STRING)
String siteNameIdentifier() default "demosite";
}
OSGi/OSGi Factory service:
- OSGi service can be declared as factory service as below (factory=true on @Designate annotation)
- Defines methods to retrieve the config values which will be available once the service is activated. (config in the below snippet holds the entire OSGI config values)
@Component(service=OSGIFactoryConfigDemo.class,immediate=true,configurationPolicy = ConfigurationPolicy.REQUIRE)
@Designate(ocd=OSGIFactoryConfigOCD.class, factory=true)
public class OSGiFactoryConfigDemoImpl implements OSGIFactoryConfigDemo {
@Activate
protected void activate(OSGIFactoryConfigOCD config) {
this.apiEndPoint = config.apiEndpoint();
this.apiKey = config.apiKey();
this.siteName = config.siteNameIdentifier();
}
}
At this stage when we build and deploy the code, we are ready to create multiple OSGI configs via Felix console or in the config folders by creating a node of type sling:OsgiConfig in the name of Service PID with a unique identifier, say -> Service PID-identifier. In this example, it is learnings.core.serviceimpl.OSGiFactoryConfigDemoImpl-demosite
Java class referencing Factory service:
Targeting a specific instance:
- Let's say we have created 5 OSGI configs of the same PID with a unique identifier, one each for the tenant and if there is a need to target one such tenant, we can reference as below.
- Out of 5, config which has sitenameIdentifier property value as the demo site will be picked up.
@Reference(target="(siteNameIdentifier=demosite)")
private OSGIFactoryConfigDemo osgiDemo;
private OSGIFactoryConfigDemo osgiDemo;
Multiple instance reference:
- bind method will be called when the factory service is available and configs are stored in the collection. In this case, the map is used where “key” -> Sitename, one of the config property, and “value” -> entire config object.
- Let say, we have created 5 OSGI configs of this PID and if the service is active -> All 5 configs will be available in the map object.
private Map<String,OSGIFactoryConfigDemo> configMap;
@Reference(name = "osgiFactoryConfigDemo", cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC)
protected synchronized void bindOSGIFactoryConfigDemo(final OSGIFactoryConfigDemo config) {
if (configMap == null) {
configMap = new HashMap<>();
}
configMap.put(config.getSiteName(), config);
}
protected synchronized void unbindOSGIFactoryConfigDemo(final OSGIFactoryConfigDemo config) {
configMap.remove(config.getSiteName());
}
@Reference(name = "osgiFactoryConfigDemo", cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC)
protected synchronized void bindOSGIFactoryConfigDemo(final OSGIFactoryConfigDemo config) {
if (configMap == null) {
configMap = new HashMap<>();
}
configMap.put(config.getSiteName(), config);
}
protected synchronized void unbindOSGIFactoryConfigDemo(final OSGIFactoryConfigDemo config) {
configMap.remove(config.getSiteName());
}
Let say, we are able to extract the sitename(key used in the map) from servlet request. (Changes to be taken care of in the way we call the servlet accordingly) Using that, we can retrieve the respective map value(its entire config) and use it accordingly.
On a similar line, based on the overall requirement + existing project set up + with an understanding of factory config implementation, we can decide on the ways of retrieving multiple configs of the same PID and arriving at generic service.
Code on GitHub Repo:
aemlearnings-samples (Above mentioned java files in a respective package inside core)
On a similar line, based on the overall requirement + existing project set up + with an understanding of factory config implementation, we can decide on the ways of retrieving multiple configs of the same PID and arriving at generic service.
Code on GitHub Repo:
aemlearnings-samples (Above mentioned java files in a respective package inside core)
No comments:
Post a Comment
If you have any doubts or questions, please let us know.