Expand my Community achievements bar.

Submissions are now open for the 2026 Adobe Experience Maker Awards.
SOLVED

Context Aware Configuration Extension

Avatar

Level 1

Hello Community

 

We have a required to Customize CA Config where we need to have property in all the CA Configs and use this for our business functionality

 

Example as below. As shown isGlobalPropetry is repeating for N CAConfigs. Is there a way to have this available for any CA Config we will have for both present and the future ones?

public @interface CAConfig1 {

	@Property(label = "Some label 1", description = "Some description 1", order = 30)
	String property1();

	@Property(label = "Global Property", description = "Some description", order = 50)
	boolean isGlobalPropetry() default true;
}


public @interface CAConfig2 {

	@Property(label = "Some label 2", description = "Some description 2", order = 30)
	String property2();

	@Property(label = "Global Property", description = "Some description", order = 50)
	boolean isGlobalPropetry() default true;
}


public @interface CAConfig3 {

	@Property(label = "Some label 3", description = "Some description 3", order = 30)
	String property3();

	@Property(label = "Global Property", description = "Some description", order = 50)
	boolean isGlobalPropetry() default true;
}

.
.
.
.
.
.
.


public @interface CAConfigN {

	@Property(label = "Some label N", description = "Some description N", order = 30)
	String propertyN();

	@Property(label = "Global Property", description = "Some description", order = 50)
	boolean isGlobalPropetry() default true;
}

 

1 Accepted Solution

Avatar

Correct answer by
Level 3

hi @SantoshSai 

Had a good idea from @arunpatidar where we can use filters to add a specific property to be available in all the CAs. Attached a sample code which worked. 

Thanks @arunpatidar 

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.sling.engine.EngineConstants;
import org.osgi.service.component.annotations.Component;

import javax.json.*;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringReader;

import static org.apache.sling.engine.EngineConstants.SLING_FILTER_PATTERN;
import static org.apache.sling.engine.EngineConstants.SLING_FILTER_SCOPE;

@Component(
  service = Filter.class,
  immediate = true,
  name = "CaConfigFilter",
  property = {
    SLING_FILTER_SCOPE + "=" + EngineConstants.FILTER_SCOPE_REQUEST,
    SLING_FILTER_PATTERN + "=" + "^/content/.*/jcr:content.configData.json"
  }
)
@Slf4j
public class CaConfigFilter implements Filter {

  private static final String GLOBAL_CONFIG_NAME = "isGlobalConfig";

  @Override
  public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
    throws IOException, ServletException {

    final CharResponseWrapper wrappedResponse = new CharResponseWrapper((HttpServletResponse) response);
    chain.doFilter(request, wrappedResponse);

    final String originalContent = wrappedResponse.toString();
    String configSourcePath = StringUtils.EMPTY;
    boolean globalConfigExists = false;

    try (final JsonReader jsonReader = Json.createReader(new StringReader(originalContent))) {
      final JsonObject jsonObject = jsonReader.readObject();
      final JsonArrayBuilder updatedProperties = Json.createArrayBuilder();

      for (final JsonValue val : jsonObject.getJsonArray("properties")) {
        final JsonObject prop = val.asJsonObject();
        final String name = prop.getString("name", StringUtils.EMPTY);

        if (GLOBAL_CONFIG_NAME.equals(name)) {
          globalConfigExists = true;

          final JsonObjectBuilder builder = Json.createObjectBuilder();
          for (final String key : prop.keySet()) {
            if (!"metadata".equals(key)) {
              builder.add(key, prop.get(key));
            }
          }
          builder.add("metadata", createGlobalConfigMetadata());
          updatedProperties.add(builder.build());
        } else {
          updatedProperties.add(prop);
        }

        if (StringUtils.isBlank(configSourcePath) && prop.containsKey("configSourcePath")) {
          configSourcePath = prop.getString("configSourcePath");
        }
      }

      if (!globalConfigExists) {
        updatedProperties.add(createGlobalConfigProperty(configSourcePath));
      }

      final JsonObject modifiedJson = Json.createObjectBuilder(jsonObject)
        .add("properties", updatedProperties)
        .build();

      response.setContentType("application/json");
      response.getWriter().write(modifiedJson.toString());

    } catch (final Exception e) {
      log.error("Error modifying configData JSON: {}", e.getMessage(), e);
      response.getWriter().write(originalContent); // fallback
    }
  }

  private JsonObject createGlobalConfigProperty(final String configSourcePath) {
    return Json.createObjectBuilder()
      .add("name", GLOBAL_CONFIG_NAME)
      .add("value", false)
      .add("effectiveValue", false)
      .add("configSourcePath", configSourcePath)
      .add("inherited", false)
      .add("overridden", false)
      .add("metadata", createGlobalConfigMetadata())
      .add("default", false)
      .build();
  }

  private JsonObject createGlobalConfigMetadata() {
    return Json.createObjectBuilder()
      .add("type", "Boolean")
      .add("defaultValue", false)
      .add("label", "Global Configuration")
      .add("description", "Indicates if this is a global configuration.")
      .build();
  }

  private static class CharResponseWrapper extends HttpServletResponseWrapper {
    private final CharArrayWriter charWriter = new CharArrayWriter();

    public CharResponseWrapper(final HttpServletResponse response) {
      super(response);
    }

    @Override
    public PrintWriter getWriter() {
      return new PrintWriter(charWriter);
    }

    @Override
    public String toString() {
      return charWriter.toString();
    }
  }
}
 

View solution in original post

3 Replies

Avatar

Community Advisor

Hi @ArunG_,

To avoid repeating the same @Property (like isGlobalPropetry) across all Context-Aware Configs (CA Configs) in AEM, inheritance or composition would seem like a natural solution - Unfortunately, Java annotations do not support inheritance the same way classes do. That means you cannot extend or inherit annotation properties directly.


Santosh Sai

AEM BlogsLinkedIn


Avatar

Level 3

Agree. and we were thinking if there is any good solution for this rather than repeating

Avatar

Correct answer by
Level 3

hi @SantoshSai 

Had a good idea from @arunpatidar where we can use filters to add a specific property to be available in all the CAs. Attached a sample code which worked. 

Thanks @arunpatidar 

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.sling.engine.EngineConstants;
import org.osgi.service.component.annotations.Component;

import javax.json.*;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringReader;

import static org.apache.sling.engine.EngineConstants.SLING_FILTER_PATTERN;
import static org.apache.sling.engine.EngineConstants.SLING_FILTER_SCOPE;

@Component(
  service = Filter.class,
  immediate = true,
  name = "CaConfigFilter",
  property = {
    SLING_FILTER_SCOPE + "=" + EngineConstants.FILTER_SCOPE_REQUEST,
    SLING_FILTER_PATTERN + "=" + "^/content/.*/jcr:content.configData.json"
  }
)
@Slf4j
public class CaConfigFilter implements Filter {

  private static final String GLOBAL_CONFIG_NAME = "isGlobalConfig";

  @Override
  public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
    throws IOException, ServletException {

    final CharResponseWrapper wrappedResponse = new CharResponseWrapper((HttpServletResponse) response);
    chain.doFilter(request, wrappedResponse);

    final String originalContent = wrappedResponse.toString();
    String configSourcePath = StringUtils.EMPTY;
    boolean globalConfigExists = false;

    try (final JsonReader jsonReader = Json.createReader(new StringReader(originalContent))) {
      final JsonObject jsonObject = jsonReader.readObject();
      final JsonArrayBuilder updatedProperties = Json.createArrayBuilder();

      for (final JsonValue val : jsonObject.getJsonArray("properties")) {
        final JsonObject prop = val.asJsonObject();
        final String name = prop.getString("name", StringUtils.EMPTY);

        if (GLOBAL_CONFIG_NAME.equals(name)) {
          globalConfigExists = true;

          final JsonObjectBuilder builder = Json.createObjectBuilder();
          for (final String key : prop.keySet()) {
            if (!"metadata".equals(key)) {
              builder.add(key, prop.get(key));
            }
          }
          builder.add("metadata", createGlobalConfigMetadata());
          updatedProperties.add(builder.build());
        } else {
          updatedProperties.add(prop);
        }

        if (StringUtils.isBlank(configSourcePath) && prop.containsKey("configSourcePath")) {
          configSourcePath = prop.getString("configSourcePath");
        }
      }

      if (!globalConfigExists) {
        updatedProperties.add(createGlobalConfigProperty(configSourcePath));
      }

      final JsonObject modifiedJson = Json.createObjectBuilder(jsonObject)
        .add("properties", updatedProperties)
        .build();

      response.setContentType("application/json");
      response.getWriter().write(modifiedJson.toString());

    } catch (final Exception e) {
      log.error("Error modifying configData JSON: {}", e.getMessage(), e);
      response.getWriter().write(originalContent); // fallback
    }
  }

  private JsonObject createGlobalConfigProperty(final String configSourcePath) {
    return Json.createObjectBuilder()
      .add("name", GLOBAL_CONFIG_NAME)
      .add("value", false)
      .add("effectiveValue", false)
      .add("configSourcePath", configSourcePath)
      .add("inherited", false)
      .add("overridden", false)
      .add("metadata", createGlobalConfigMetadata())
      .add("default", false)
      .build();
  }

  private JsonObject createGlobalConfigMetadata() {
    return Json.createObjectBuilder()
      .add("type", "Boolean")
      .add("defaultValue", false)
      .add("label", "Global Configuration")
      .add("description", "Indicates if this is a global configuration.")
      .build();
  }

  private static class CharResponseWrapper extends HttpServletResponseWrapper {
    private final CharArrayWriter charWriter = new CharArrayWriter();

    public CharResponseWrapper(final HttpServletResponse response) {
      super(response);
    }

    @Override
    public PrintWriter getWriter() {
      return new PrintWriter(charWriter);
    }

    @Override
    public String toString() {
      return charWriter.toString();
    }
  }
}