The Service User Mapping That Worked Only on Author
A custom bundle with symbolic name com.acme.audit.core contains an OSGi service that writes audit records under /var/audit/acme on publish. The service obtains a resource resolver with subservice audit-writer. On author it works. After deploying the same bundle and configuration to publish, every write fails with LoginException, and the log shows Cannot obtain service resource resolver for com.acme.audit.core:audit-writer. The service user mapping is deployed through a config package containing myproject.audit:audit-writer=audit-svc. The user audit-svc exists on both author and publish. The bundle starts without errors, and there are two publish instances receiving the same package.
1. What is the most likely cause, and what checks would you perform in what order?
Read the worked answer
The mapping string is the first suspect because the log identifies the bundle as com.acme.audit.core, while the deployed mapping uses myproject.audit. Service-user mapping keys are bundle symbolic name plus subservice, not an arbitrary project name. If the key does not match, the resolver factory cannot find a user for that bundle/subservice pair and throws LoginException.
Check order:
- Read the active ServiceUserMapper configuration and confirm the exact key and user. Compare the bundle symbolic name from the OSGi console with the mapping key.
- Confirm the subservice name in code matches
audit-writerexactly. - Confirm the configuration is present in the correct run mode for publish, not only author.
- Confirm
audit-svcexists on both publish instances and is not disabled. - Only then investigate permissions.
If the key is corrected to com.acme.audit.core:audit-writer=audit-svc, retest on one publish instance before rolling both. A config package being deployed is not proof that the active configuration matches the bundle identity.
2. Suppose the mapping is corrected but the service still fails. What permission model would you apply, and why not use an administrator user?
Read the worked answer
Once mapping is correct, the failure would point to authorization. The service only needs to create and update audit nodes under /var/audit/acme. Grant audit-svc read and write permissions on that subtree, typically via jcr:read and rep:write on /var/audit/acme, and avoid granting repository-wide access.
Using an administrator user would make the symptom disappear but would violate least privilege and hide future path mistakes. It would also give the audit service the ability to modify content, users, and configuration if the code were misused. A dedicated service user with a narrow ACL is auditable and limits blast radius.
I would verify permissions by impersonating or testing through the same service-user path, not by logging in as an author. If the service writes to a path that does not yet exist, confirm that the service user has the required permission on the parent path as well. Then test on both publish instances, because ACLs can drift if applied manually.
3. What would you check in the code to prevent resolver leaks from this service?
Read the worked answer
I would verify that every service resource resolver is closed in a finally block or try-with-resources, and that the resolver is not stored in a field or cached across requests. A service resolver represents a repository session; leaking it under load eventually exhausts sessions or memory even if the service appears functional.
The code should follow this shape:
- Obtain the resolver only when work is about to be performed.
- Use it for the shortest possible scope.
- Close it deterministically, including on exception paths.
- Never close a resolver that was supplied by Sling to a request or model; only close resolvers the code obtained itself.
I would also review whether the service opens a resolver per audit record. Under high volume, that can be expensive even if each resolver is closed. Batching writes or using a single resolver per bounded operation may be appropriate, but the resolver must still be closed at the end of that operation. The judgement is to balance session lifetime against leak risk, not simply to cache resolvers for performance.