In Adobe Experience Manager (AEM), Live Copy functionality—backed by the Multi-Site Manager (MSM)—allows for synchronized content replication across regional or site-specific structures, while retaining the flexibility for localized changes.
For web pages, AEM provides public APIs that enable developers to programmatically create Live Copies using standard interfaces. However, no such public API exists for creating Live Copies of DAM assets. This lack of direct support creates challenges in automation scenarios involving the DAM.
To work around this, we leveraged AEM’s internal servlet mechanism to programmatically create Asset Live Copies, as explained below.
🛠️ Solution Overview
To programmatically create asset Live Copies:
- We simulate a POST request to the internal servlet
/bin/wcmcommand - Use a system service user via
ResourceResolverFactory - Leverage
SlingRequestProcessorandSlingInternalRequestto build and execute the call internally within the AEM instance
🧪 Code Walkthrough
Here’s the implementation:
import java.io.IOException;
import org.apache.sling.api.resource.LoginException;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.engine.SlingRequestProcessor;
import org.apache.sling.servlethelpers.internalrequests.SlingInternalRequest;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.techrevel.core.service.AssetLiveCopyService;
/**
* Creates Live Copies for assets using a POST servlet call to the /bin/wcmcommand endpoint.
*
*/
@Component(service = CreateAssetLiveCopy.class, immediate = true)
public class CreateAssetLiveCopyImpl implements AssetLiveCopyService {
@Reference
private SlingRequestProcessor slingRequestProcessor;
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public boolean createLiveCopy(String srcPath, String destPath, String title, String label) {
Map<String, Object> param = new HashMap<>();
param.put(ResourceResolverFactory.SUBSERVICE, SUBSERVICE_NAME);
try (ResourceResolver resolver = resolverFactory.getServiceResourceResolver(param)) {
// Build and execute internal POST request
String response = new SlingInternalRequest(resourceResolver, slingRequestProcessor, "/bin/wcmcommand")
.withRequestMethod("POST")
.withParameter("cmd", "createLiveCopy")
.withParameter("srcPath", srcPath)
.withParameter("destPath", destPath)
.withParameter("title", title)
.withParameter("label", label)
.withParameter("cq:rolloutConfigs", "/libs/msm/wcm/rolloutconfigs/default")
.execute()
.checkStatus(200)
.getResponseAsString();
logger.info("LiveCopy created successfully. Response: {}", response);
return true;
} catch (IllegalArgumentException | IOException | LoginException e) {
logger.error("LiveCopy creation failed due to error: {}", e.getMessage());
}
return false;
}
}
Explanation of Request Parameters
cmd: Always set to"createLiveCopy"srcPath: Full path of the source assetdestPath: Target path for the Foldertitle: Title for the Live Copylabel: Asset name for the live copycq:rolloutConfigs: Defines the rollout strategy
To know more about SlingInternalRequest, refer to SlingInternalRequest: A Cleaner Way to Reuse AEM Servlets with Internal API Access
I am more interesting in knowing how you know about internal servlet
/bin/wcmcommand, because if we do not know about these kind of internal servlet existence we do not even think of using SlingInternalRequest to call these servlets .LikeLike