Now, let us look at some technical challenges when setting up AEM for multiple tenants. To better understand these challenges, we will do an exercise where we use a factory service to connect different API Gateways with various configurations.
+----------------+ +------------------+
| API Gateway 1 | ---> | Instance 1 |
+----------------+ +------------------+
| API Gateway 2 | ---> | Instance 2 |
+----------------+ +------------------+
| API Gateway 3 | ---> | Instance 3 |
+----------------+ +------------------+
...
In a multi-tenant architecture, you might have different instances (or deployments) of an API Gateway for each tenant. Each API Gateway instance is associated with a specific tenant or client, and the instances are logically isolated to cater to the individual needs and configurations of each tenant.
» Define an interface that represents the configuration for your AWS API Gateway services. For example:
@ObjectClassDefinition(
name = "Flagtick - RDS Service Configurations",
description = "AWS RDS Service Configurations"
)
protected @interface Config {
@AttributeDefinition(
name = "Endpoint 1",
description = "URL endpoint for endpoint 1",
type = AttributeType.STRING
)
String firstEndpoint();
@AttributeDefinition(
name = "Endpoint 2",
description = "URL endpoint for endpoint 2",
type = AttributeType.STRING
)
String secondEndpoint();
@AttributeDefinition(
name = "API Key",
description = "APK Key For Endpoint",
type = AttributeType.STRING
)
String apiKey();
}
This annotation interface can then be used to define configuration properties for a class or service in an OSGi environment.
@Designate(ocd = FlagtickIntegrationServiceImpl.Config.class)
public class PromisIntegrationServiceImpl {
// annotation interface
}
Note: In the OSGi framework, configurations are defined using metadata called Object Class Definitions (ocd). Each ocd describes the structure and properties of a configuration object.
If you want to merge the configuration and implementation into one class, you can do so by combining the configuration interface and the implementation into a single class. Here's an example:
(1)
@Component(service = FlagtickIntegrationServiceImpl.class, immediate = true)
@Designate(ocd = FlagtickIntegrationServiceImpl.Config.class)
public class FlagtickIntegrationServiceImpl {
@ObjectClassDefinition(
name = "Flagtick - RDS Service Configurations",
description = "AWS RDS Service Configurations"
)
protected @interface Config {
@AttributeDefinition(
name = "Endpoint 1",
description = "URL endpoint for endpoint 1",
type = AttributeType.STRING
)
String firstEndpoint();
@AttributeDefinition(
name = "Endpoint 2",
description = "URL endpoint for endpoint 2",
type = AttributeType.STRING
)
String secondEndpoint();
@AttributeDefinition(
name = "API Key",
description = "APK Key For Endpoint",
type = AttributeType.STRING
)
String apiKey();
}
private String firstEndpoint;
private String secondEndpoint;
private String apiKey;
@Activate
protected void activate(Config config) {
...
this.firstEndpoint = config.firstEndpoint();
this.secondEndpoint = config.secondEndpoint();
this.apiKey = config.apiKey();
...
}
}
The implementation for script above for single class and if you want to separate configuration service interface can reference the way as below:
(2)
@Designate(ocd = FlagtickIntegrationService.Config.class)
public interface FlagtickIntegrationService {
@AttributeDefinition(
name = "Endpoint 1",
description = "URL endpoint for endpoint 1",
type = AttributeType.STRING
)
String firstEndpoint();
@AttributeDefinition(
name = "Endpoint 2",
description = "URL endpoint for endpoint 2",
type = AttributeType.STRING
)
String secondEndpoint();
@AttributeDefinition(
name = "API Key",
description = "API Key for Endpoint",
type = AttributeType.STRING
)
String apiKey();
// Other API Gateway configuration properties
interface Config {
// Additional configuration properties go here
@AttributeDefinition(
name = "Additional Property",
description = "Additional configuration property",
type = AttributeType.STRING
)
String additionalProperty();
}
}
» If you intend to have multiple instances of the component with different configurations, you should use configurationFactory = true. If you want a singleton instance of the component that uses a single, shared configuration.
+ single instance (1)
@Component(service = FlagtickIntegrationServiceImpl.class, immediate = true)
@Designate(ocd = FlagtickIntegrationServiceImpl.Config.class)
public class FlagtickIntegrationServiceImpl {
// Implementation details
}
This approach is often used when the class directly provides the functionality as a service without the need for a separate interface.
@Component(
immediate = true,
service = PromisIntegrationService.class
)
@Designate(ocd = FlagtickIntegrationServiceImpl.Config.class)
public class FlagtickIntegrationServiceImpl implements FlagtickIntegrationService {
// Implementation details
}
This approach allows for better separation of concerns and makes it easier to provide alternative implementations for the same service contract.
public interface FlagtickIntegrationService {
SampleModel retrieveInformation(Map<String, String> paramMap, String flagtickToken);
}
+ multiple instances (2)
@Component(configurationFactory = true, service = FlagtickIntegrationService.class)
public class FlagtickIntegrationServiceImpl implements FlagtickIntegrationService {
// Implement the methods from the configuration interface
}
» In your main service or servlet that interacts with AWS API Gateways, dynamically bind multiple configurations or single configuration using the @Reference annotation.
@Component(service = Servlet.class, immediate = true)
public class SampleServlet {
@Reference
transient private FlagtickIntegrationService flagtickIntegrationService;
}