avatar
implement AEM in multi-tenant instances AEM

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;
}
You need to login to do this manipulation!