AEM error - Usage of org.apache.sling.api.resource.ResourceResolver as a field is not thread safe. | Community
Skip to main content
Level 6
January 22, 2026
Question

AEM error - Usage of org.apache.sling.api.resource.ResourceResolver as a field is not thread safe.

  • January 22, 2026
  • 2 replies
  • 42 views

hi folks,

 

I  had to write a link rewriter but I get the above warning with it.

 

So the jist is, I stored the resource Resolver in the init() and use it in the startElement()

 

I don’t know any other way of doing a link rewriter except this :-)

Any suggestions welcome.

thanks

FIoa

 

 

@Component(
        label = "External Link Transformer Factory",
        description = "Rewrites external links by ...")
@Service(value = TransformerFactory.class)
public class ExternalLinkTransformerFactory implements TransformerFactory {

    @Property(value = "extlink", propertyPrivate = true)
    private static final String PROPERTY_PIPELINE_TYPE = "pipeline.type";

    @Override
    public Transformer createTransformer() {
        return new ExternalLinkTransformer();
    }
    
    private ResourceResolver resourceResolver;

    private class ExternalLinkTransformer implements Transformer {

        private static final String TAG_ANCHOR = "a";
        private static final String ATTR_HREF = "href";

        private ContentHandler contentHandler;

        @Override
        public void init(ProcessingContext processingContext,
                         ProcessingComponentConfiguration processingComponentConfiguration)
                throws IOException {
            resourceResolver = processingContext.getRequest().getResourceResolver();
        }

        @Override
        public void setContentHandler(ContentHandler contentHandler) {
            this.contentHandler = contentHandler;
        }

        
        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
            contentHandler.startElement(uri, localName, qName, rebuildAttributes(localName, attributes));
        }

        private Attributes rebuildAttributes(String elementName, Attributes currentAttrs) {
            if(!TAG_ANCHOR.equalsIgnoreCase(elementName)) {
                return currentAttrs;
            }

            String url = currentAttrs.getValue(ATTR_HREF);
            try {
               
                    final AttributesImpl newAttrs = new AttributesImpl(currentAttrs);
                    if ( resourceResolver.isLive() == true ) {
                        
                        String modifiedHref = resourceResolver.map(pathname);
                        modifiedHref = modifiedHref + other stuff...
                        newAttrs.setValue(currentAttrs.getIndex(ATTR_HREF), modifiedHref);
                        return newAttrs;
                    }
                
            }

    2 replies

    PGURUKRISHNA
    Level 4
    January 23, 2026

    Hi ​@fionas76543059 

    The issue is that 

    ResourceResolver

     is being stored as a field in 

    ExternalLinkTransformerFactory

    , which is a singleton service. Since 

    Transformer

     instances can be used concurrently across multiple threads, this creates a thread-safety problem.

    Solution: Store the 

    ResourceResolver

     in the 

    ExternalLinkTransformer

     instance (not the factory), since a new transformer is created per request.

    Here's the corrected code:

    @Component(
    label = "External Link Transformer Factory",
    description = "Rewrites external links by ...")
    @Service(value = TransformerFactory.class)
    public class ExternalLinkTransformerFactory implements TransformerFactory {

    @Property(value = "extlink", propertyPrivate = true)
    private static final String PROPERTY_PIPELINE_TYPE = "pipeline.type";

    @Override
    public Transformer createTransformer() {
    return new ExternalLinkTransformer();
    }

    private class ExternalLinkTransformer implements Transformer {

    private static final String TAG_ANCHOR = "a";
    private static final String ATTR_HREF = "href";

    private ContentHandler contentHandler;
    private ResourceResolver resourceResolver; // Move here

    @Override
    public void init(ProcessingContext processingContext,
    ProcessingComponentConfiguration processingComponentConfiguration)
    throws IOException {
    resourceResolver = processingContext.getRequest().getResourceResolver();
    }

    @Override
    public void setContentHandler(ContentHandler contentHandler) {
    this.contentHandler = contentHandler;
    }

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
    contentHandler.startElement(uri, localName, qName, rebuildAttributes(localName, attributes));
    }

    private Attributes rebuildAttributes(String elementName, Attributes currentAttrs) {
    if(!TAG_ANCHOR.equalsIgnoreCase(elementName)) {
    return currentAttrs;
    }

    String url = currentAttrs.getValue(ATTR_HREF);
    try {
    final AttributesImpl newAttrs = new AttributesImpl(currentAttrs);
    if (resourceResolver != null && resourceResolver.isLive()) {
    String modifiedHref = resourceResolver.map(pathname);
    // modifiedHref = modifiedHref + other stuff...
    newAttrs.setValue(currentAttrs.getIndex(ATTR_HREF), modifiedHref);
    return newAttrs;
    }
    } catch (Exception e) {
    // Handle exception
    }
    return currentAttrs;
    }

    // Implement other Transformer methods...
    }
    }

    Key change: Moved 

    resourceResolver

     from the factory class to the 

    ExternalLinkTransformer

     inner class. Each transformer instance now has its own 

    ResourceResolver

    , making it thread-safe.

    Anudeep_Garnepudi
    Community Advisor
    Community Advisor
    January 28, 2026

    @fionas76543059  Do not declare ResourceResolver at class level field/variable. Either get it wherever required or pass it as a parameter.

    AG