For AI agents: the complete documentation index is available at https://nhanbe.id.vn/llms.txt, the full documentation bundle is available at https://nhanbe.id.vn/llms-full.txt, and this page is available as Markdown at https://nhanbe.id.vn/aem/backend/osgi-configuration.md.

OSGi configuration must be deployed via code (content package), not manually edited in Felix Console. Easy PR review Git history tracking

OSGi Configuration

1. Configuration Convention

OSGi configuration must be deployed via code (content package), not manually edited in Felix Console.

  • Easy PR review
  • Git history tracking
  • Consistency across environments
  • Prevents configuration drift from manual operations

Felix Console is for quick debugging only; any changes must be "converted" back to .cfg.json files in the repository.

2. ui.config Directory Structure and Run Modes

Standard location:

ui.config/
└── src/main/content/jcr_root/apps/<app>/osgiconfig/
    ├── config/                 # all
    ├── config.author/          # author only
    ├── config.publish/         # publish only
    ├── config.author.dev/      # author + dev
    ├── config.author.stage/    # author + stage
    ├── config.author.prod/     # author + prod
    ├── config.publish.dev/     # publish + dev
    ├── config.publish.stage/   # publish + stage
    └── config.publish.prod/    # publish + prod

Resolution Rule

AEM selects configuration based on specificity level from highest to lowest. When the same PID appears in multiple folders, the more specific folder takes precedence.

Example: If the same PID is placed in config/ and config.author.dev/, Author running with run modes author,dev will use the one from config.author.dev/.

Run Mode Folder Case Sensitivity

Use lowercase only. config.Author/ will be ignored.

3. File Naming Rules (PID)

Use .cfg.json for all files.

Singleton Configuration

Single configuration instance:

com.myproject.core.services.impl.MyServiceImpl.cfg.json

Factory Configuration

Multiple instances, use ~<id>:

com.myproject.core.services.impl.MyServiceImpl~site-a.cfg.json
com.myproject.core.services.impl.MyServiceImpl~site-b.cfg.json

<id> is for differentiation only and does not affect runtime logic.

Most Dangerous Error: PID Typo

One incorrect character in the filename = configuration is silently ignored. Prevention methods:

  • Copy-paste the FQCN of component/@Designate PID from code
  • Add CI checks (see Section 10)

4. Data Types in .cfg.json

JSONOSGiExample
stringString"https://api.example.com"
booleanBooleantrue
numberInteger/Long100
floatFloat/Double3.14
arrayString[]["jpg","png"]

Complete example:

{
  "enabled": true,
  "syncIntervalMinutes": 30,
  "endpoint": "https://dam-api.example.com/v2",
  "allowedMimeTypes": [
    "image/jpeg",
    "image/png",
    "application/pdf"
  ],
  "maxRetries": 3,
  "connectionTimeoutMs": 5000,
  "verboseLogging": false
}

5. Typed Configuration (AEM 6.5 Standard)

Objective: Clear schema, UI support in /system/console/configMgr, type-safe injection.

5.1. @ObjectClassDefinition

package com.myproject.core.config;

import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;

@ObjectClassDefinition(
    name = "MyProject - Asset Sync Service",
    description = "Controls the remote DAM synchronisation service."
)
public @interface AssetSyncConfig {

    @AttributeDefinition(
        name = "Enabled",
        description = "Enable or disable the sync service."
    )
    boolean enabled() default false;

    @AttributeDefinition(
        name = "Sync Interval (minutes)",
        description = "How often the sync job runs."
    )
    int syncIntervalMinutes() default 30;

    @AttributeDefinition(
        name = "Remote Endpoint",
        description = "Base URL of the remote DAM API."
    )
    String endpoint() default "https://dam-api.example.com/v2";

    @AttributeDefinition(
        name = "Allowed MIME Types",
        description = "List of MIME types that will be synchronised."
    )
    String[] allowedMimeTypes() default { "image/jpeg", "image/png" };

    @AttributeDefinition(
        name = "Max Retries",
        description = "Number of retry attempts on failure."
    )
    int maxRetries() default 3;
}

5.2. @Designate + @Activate/@Modified

package com.myproject.core.services.impl;

import com.myproject.core.config.AssetSyncConfig;
import com.myproject.core.services.AssetSyncService;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Modified;
import org.osgi.service.metatype.annotations.Designate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component(service = AssetSyncService.class, immediate = true)
@Designate(ocd = AssetSyncConfig.class)
public class AssetSyncServiceImpl implements AssetSyncService {

    private static final Logger LOG = LoggerFactory.getLogger(AssetSyncServiceImpl.class);

    private volatile boolean enabled;
    private volatile int syncIntervalMinutes;
    private volatile String endpoint;
    private volatile String[] allowedMimeTypes;
    private volatile int maxRetries;

    @Activate
    @Modified
    protected void activate(AssetSyncConfig config) {
        this.enabled = config.enabled();
        this.syncIntervalMinutes = config.syncIntervalMinutes();
        this.endpoint = config.endpoint();
        this.allowedMimeTypes = config.allowedMimeTypes();
        this.maxRetries = config.maxRetries();

        LOG.info("AssetSync enabled={}, interval={}m, endpoint={}",
            enabled, syncIntervalMinutes, endpoint);
    }

    @Override
    public void sync() {
        if (!enabled) {
            return;
        }
        // sync logic...
    }
}

Add @Modified for AEM to apply configuration at runtime without bundle restart.

6. Secret Values on AEM 6.5 (on-prem)

Do not commit plaintext secrets to Git.

Common approach on AEM 6.5:

  1. Navigate to http://<host>:4502/system/console/crypto
  2. Encrypt secret → obtain string like \{enc:...\}
  3. Place \{enc:...\} value in .cfg.json

Operational notes:

  • The crypto key is instance-specific → migrating encrypted strings to another instance may fail decryption; often need to re-encrypt per environment.
  • In production, manage secrets through operational processes (vault/secrets manager + automation). If not available, crypto console is the minimum baseline.

7. Felix Console—Use for Observation, Not "Production Configuration"

Commonly used URLs:

URLPurpose
/system/console/configMgrView/edit runtime configuration (debug)
/system/console/bundlesBundle status
/system/console/componentsDS component status
/system/console/servicesOSGi services list

If edited in the console, configuration resides in JCR (typically under /apps/system/config) and will be overwritten on the next deployment.

8. Common AEM Configuration PIDs (AEM 6.5)

ServicePIDCommon Adjustments
Sling Referrer Filterorg.apache.sling.security.impl.ReferrerFilterAllowed hosts, allow empty
Externalizercom.day.cq.commons.impl.ExternalizerImplDomain mappings per run mode
Sling Loggingorg.apache.sling.commons.log.LogManagerLevel, file, pattern
Sling Logger (factory)org.apache.sling.commons.log.LogManager.factory.configPer-package log level
CSRF Filtercom.adobe.granite.csrf.impl.CSRFFilterExcluded paths
CORS Policycom.adobe.granite.cors.impl.CORSPolicyImplOrigins/methods/headers

9. Pitfalls and Deployment Checklist

Pitfalls:

  • Wrong run mode folder (placing publish config in config.author/)
  • Missing ~id for factory configuration
  • PID typo (configuration ignored)
  • Missing .cfg.json
  • filter.xml does not include /apps/<app>/osgiconfig

Checklist:

  • Files are in correct run mode folder
  • Filename PID is exactly correct
  • Factory configuration includes ~<id>
  • Package filter includes .../osgiconfig path
  • After deployment, verify configuration is applied in /system/console/configMgr

10. Validate Configuration in CI (Reduce Production Errors)

Objective: Catch PID typos before deployment to any environment.

Practical approach:

  • Parse filenames under ui.config/.../osgiconfig/**
  • For singleton: get PID = filename (remove .cfg.json)
  • For factory: get PID = part before ~
  • Match PID against:
    • @Component FQCN (if using class-based PID)
    • or @Designate(ocd=...) PID as defined by project convention

Failing the build when any PID does not match (or is not on the whitelist) significantly reduces "configuration not applied" errors.

References