Expand my Community achievements bar.

Join expert-led, customer-led sessions on Adobe Experience Manager Assets on August 20th at our Skill Exchange.
SOLVED

Multiple sitemapgenerators overlap

Avatar

Level 1

We have multiple sitemapgenerators in project some are extended with ResourceTreesitemapGenerator class and some implements SitemapGenerator class and service ranking is kept same for all generators. So I want to invoke a specific generator for a specific root path and but It was observed that the sitemapgenerator with lowest service id is invoked and being used for generating urls in sitemap files. Do I need to do some config to tackle that issue.

Topics

Topics help categorize Community content and increase your ability to discover relevant content.

1 Accepted Solution

Avatar

Correct answer by
Community Advisor

Hi @tanuj05,

When the same service ranking are registered, and OSGi will invoke the one with the lowest service.id by default, unless a better ranking or filtering mechanism is in place.

Since you're working with multiple generators - some extending ResourceTreeSitemapGenerator and others implementing SitemapGenerator directly - here's how you can correctly control which generator handles which root path.

Solution Overview

You should configure each SitemapGenerator to only apply to specific root paths using the sitemap.resourceTypes or sitemap.paths property. The Apache Sling Sitemap framework relies on this configuration to determine which generator to invoke.

1. Use paths property in your generator service

Each custom generator should explicitly declare the root path(s) it supports:

@Component(
  service = SitemapGenerator.class,
  property = {
    SitemapGeneratorConstants.PROPERTY_PATHS + "=/content/site-a"
  }
)
public class SiteASitemapGenerator implements SitemapGenerator {
    // implementation
}

Use /content/site-b for another generator, and so on.

2. Avoid relying solely on service.ranking

Since service.ranking is only used to resolve ties, and service.id is non-deterministic, it's better to delegate control based on path rather than hoping ranking resolves your issue.

3. Alternative - use a delegator generator

If logic is very dynamic, implement a delegating generator that routes requests based on the root path:

@Component(service = SitemapGenerator.class)
public class DelegatingSitemapGenerator implements SitemapGenerator {
    @Reference
    private List<SitemapGenerator> generators;

    @Override
    public void generate(Resource resource, SitemapGeneratorContext context) {
        for (SitemapGenerator generator : generators) {
            if (generator.appliesTo(resource)) {
                generator.generate(resource, context);
                return;
            }
        }
    }
}

Each generator can implement appliesTo() logic internally based on root path.

4. If you use ResourceTreeSitemapGenerator

That class is built for auto-generating URLs based on resource trees, and it uses resource types to decide what to include. Make sure to configure:

property = {
    "sitemap.resourceTypes=mysite/components/page",
    SitemapGeneratorConstants.PROPERTY_PATHS + "=/content/mysite"
} 

Santosh Sai

AEM BlogsLinkedIn


View solution in original post

2 Replies

Avatar

Correct answer by
Community Advisor

Hi @tanuj05,

When the same service ranking are registered, and OSGi will invoke the one with the lowest service.id by default, unless a better ranking or filtering mechanism is in place.

Since you're working with multiple generators - some extending ResourceTreeSitemapGenerator and others implementing SitemapGenerator directly - here's how you can correctly control which generator handles which root path.

Solution Overview

You should configure each SitemapGenerator to only apply to specific root paths using the sitemap.resourceTypes or sitemap.paths property. The Apache Sling Sitemap framework relies on this configuration to determine which generator to invoke.

1. Use paths property in your generator service

Each custom generator should explicitly declare the root path(s) it supports:

@Component(
  service = SitemapGenerator.class,
  property = {
    SitemapGeneratorConstants.PROPERTY_PATHS + "=/content/site-a"
  }
)
public class SiteASitemapGenerator implements SitemapGenerator {
    // implementation
}

Use /content/site-b for another generator, and so on.

2. Avoid relying solely on service.ranking

Since service.ranking is only used to resolve ties, and service.id is non-deterministic, it's better to delegate control based on path rather than hoping ranking resolves your issue.

3. Alternative - use a delegator generator

If logic is very dynamic, implement a delegating generator that routes requests based on the root path:

@Component(service = SitemapGenerator.class)
public class DelegatingSitemapGenerator implements SitemapGenerator {
    @Reference
    private List<SitemapGenerator> generators;

    @Override
    public void generate(Resource resource, SitemapGeneratorContext context) {
        for (SitemapGenerator generator : generators) {
            if (generator.appliesTo(resource)) {
                generator.generate(resource, context);
                return;
            }
        }
    }
}

Each generator can implement appliesTo() logic internally based on root path.

4. If you use ResourceTreeSitemapGenerator

That class is built for auto-generating URLs based on resource trees, and it uses resource types to decide what to include. Make sure to configure:

property = {
    "sitemap.resourceTypes=mysite/components/page",
    SitemapGeneratorConstants.PROPERTY_PATHS + "=/content/mysite"
} 

Santosh Sai

AEM BlogsLinkedIn


Avatar

Community Advisor

Hi @tanuj05 ,

Try below steps:

1. Generator A - handles /content/site-a

package com.example.core.sitemap;

import org.apache.sling.sitemap.generator.SitemapGenerator;
import org.apache.sling.sitemap.generator.SitemapGeneratorContext;
import org.apache.sling.api.resource.Resource;
import org.osgi.service.component.annotations.Component;

import static org.apache.sling.sitemap.SitemapGeneratorConstants.PROPERTY_PATHS;

@Component(
    service = SitemapGenerator.class,
    property = {
        PROPERTY_PATHS + "=/content/site-a"
    }
)
public class SiteASitemapGenerator implements SitemapGenerator {

    @Override
    public void generate(Resource resource, SitemapGeneratorContext context) {
        // Your logic to generate URLs for /content/site-a
    }
}

2. Generator B - handles /content/site-b

@Component(
    service = SitemapGenerator.class,
    property = {
        PROPERTY_PATHS + "=/content/site-b"
    }
)
public class SiteBSitemapGenerator implements SitemapGenerator {

    @Override
    public void generate(Resource resource, SitemapGeneratorContext context) {
        // Your logic to generate URLs for /content/site-b
    }
}

Note:

NEVER rely on service.ranking or service.id for sitemap routing.

Sling Sitemap automatically invokes the generator based on the path match (sitemap.paths) if defined.

You can use multiple paths like:

property = {
    PROPERTY_PATHS + "=/content/site-a",
    PROPERTY_PATHS + "=/content/another-site"
}


Regards,
Amit