Article summary

Summary

Discusses how to use the AEM Replication API to replicate content. This article also describes how to use DS Annotations to create the custom workflow step as opposed to Felix SRC annotations. 

This article was updated at the end to show how to use a custom replication agent in the Replication API. 

A special thank you to Ratna Kumar Kotla and Tamal Bhattacharjee, both members of the AEM community, for testing this article and ensuring it works. 

Digital Marketing Solution(s) Adobe Experience Manager (Adobe CQ)
Audience
Developer (intermediate)
Required Skills
Java, OSGi, Maven
Version  Adobe Experience Manager 6.4

Note:

To follow this article, you need to have an AEM Author and Publish instance runnnig. 

Introduction

When working with Adobe Experience Manager content, such as pages and digital assets, you can use workflows to replicate the content. Furthermore, you can use the AEM Replication API to build a custom step that replicates content from the Author instance to the Publisher instance. For more information, see Replication API.

For example, consider a business requirement when a page has to be replicated using a workflow. However, each time you replicate the page, you have an additional business requirment. For example, you have to track the path (and other information) of the page that is replicated in a database. Using a custom step, you can track the information to address your business requirement and replicate the page.

The following illustration show the workflow model that replicates an AEM page to the Publish instance.

Model
A Workflow model that uses a custom step that replicates content

This development article walks you though how to use the Replication API within a custom AEM workflow step.

Create an Experience Manager 13 archetype project

You can create an Experience Manager archetype project by using the Maven archetype plugin. In this example, assume that the working directory is C:\AdobeCQ. 

ProjectA
An Experience Manager Maven Archetype 13 project

To create an Experience Manager archetype project, perform these steps:

1. Open the command prompt and go to your working directory (for example, C:\AdobeCQ).

2. Run the following Maven command:

mvn org.apache.maven.plugins:maven-archetype-plugin:2.4:generate -DarchetypeGroupId=com.adobe.granite.archetypes -DarchetypeArtifactId=aem-project-archetype -DarchetypeVersion=13 -DarchetypeCatalog=https://repo.adobe.com/nexus/content/groups/public/

3. When prompted, specify the following information:

  • groupId - repWorkflow
  • artifactId - repWorkflow
  • version - 1.0-SNAPSHOT
  • package - com.aem.community
  • appsFolderName - repWorkflow
  • artifactName - repWorkflow
  • componentGroupName - repWorkflow
  • contentFolderName - repWorkflow
  • cssId - repWorkflow
  • packageGroup - repWorkflow
  • siteName -repWorkflow

4. WHen prompted, specify Y.

5. Once done, you will see a message like:

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 01:42 min
[INFO] Finished at: 2016-04-25T14:34:19-04:00
[INFO] Final Memory: 16M/463M
[INFO] ------------------------------------------------------------------------

6. Change the working directory to repWorkflow and then enter the following command.

mvn eclipse:eclipse

After you run this command, you can import the project into Eclipse as discussed in the next section.

Note:

If you have not setup Maven, see this article Creating an Adobe Experience Manager 6.4 Project using Adobe Maven Archetype 13.

Add Java files to the Maven project using Eclipse 

To make it easier to work with the Maven generated project, import it into the Eclipse development environment, as shown in the following illustration.

project
Eclipse Import Project Dialog

Note:

Do not worry about the errors reported in Eclipse. It does not read the POM file where the APIs are resolved. You build the bundle with Maven. Eclipse is used to edit the Java files and the POM file.

The next step is to add a Java file to the com.aem.community.core package named CustomStep

CustomStep

The CustomStep class represents a custom workflow step that uses the Replication API to replicate the payload. First thing to notice is that a Replicator instance is created by using a @Reference instance: 

@Reference

private Replicator replicator;

To replicate the payload, the following line of code is used: 

  replicator.replicate(session,ReplicationActionType.ACTIVATE,path); 

The following code represents the CustomStep Java class. 

package com.aem.community.core;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
    
 
import javax.jcr.Session;
import javax.jcr.Node; 
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
     
import java.io.StringWriter;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
     
import javax.jcr.Repository; 
import javax.jcr.SimpleCredentials; 
import javax.jcr.Node; 
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
 
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.IOException;
import com.day.cq.dam.api.Asset; 
import java.util.Collections;
      
import org.apache.jackrabbit.commons.JcrUtils;
     
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
 
     
import javax.jcr.Session;
import javax.jcr.Node; 
import org.osgi.framework.Constants;
   
import com.adobe.granite.workflow.WorkflowException;
import com.adobe.granite.workflow.WorkflowSession;
import com.adobe.granite.workflow.exec.WorkItem;
import com.adobe.granite.workflow.exec.WorkflowData;
import com.adobe.granite.workflow.exec.WorkflowProcess;
import com.adobe.granite.workflow.metadata.MetaDataMap;
 
//AssetManager
import com.day.cq.dam.api.AssetManager; 
 
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream ; 
import java.io.OutputStream ; 
import java.io.ByteArrayInputStream ; 
import java.io.FileOutputStream ; 
   
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

//Replication APIs	
import com.day.cq.replication.ReplicationActionType;
import com.day.cq.replication.ReplicationException;
import com.day.cq.replication.ReplicationOptions;
import com.day.cq.replication.Replicator;
   
    
//Sling Imports
import org.apache.sling.api.resource.ResourceResolverFactory ; 
import org.apache.sling.api.resource.ResourceResolver; 
import org.apache.sling.api.resource.Resource; 
import com.day.cq.wcm.api.Page; 
    
 
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
 
import com.adobe.granite.workflow.model.WorkflowNode;
import com.adobe.granite.workflow.exec.WorkflowData;
 



//This custom workflow step will use the AEM Replication API to replicate content from Author to Publish 
@Component(service=WorkflowProcess.class, property = {"process.label=My Replication Step"})
 
public class CustomStep implements WorkflowProcess 
{
         
        
/** Default log. */
protected final Logger log = LoggerFactory.getLogger(this.getClass());
        
@Reference
private ResourceResolverFactory resolverFactory;

@Reference
private Replicator replicator;
  
private Session session;
        
public void execute(WorkItem item, WorkflowSession wfsession,MetaDataMap args) throws WorkflowException {
            
try
{
    log.info("**** Here in execute method");    //ensure that the execute method is invoked    
          
   
    WorkflowNode myNode = item.getNode(); 
    String myTitle = myNode.getTitle(); //returns the title of the workflow step
    log.info("**** The title is "+myTitle);  
      
    WorkflowData workflowData = item.getWorkflowData(); //gain access to the payload data
    String path = workflowData.getPayload().toString();//Get the path of the asset
      
      
    replicationConent(path, wfsession)  ;       
   log.info("**** This was was written to the Trash folder ");  
}
    
    catch (Exception e)
    {
    e.printStackTrace()  ; 
    }
 }
  
  
//Replicate the playload using the Replication API
private String replicationConent(String path, WorkflowSession wfsession)
{
try
{
    //Inject a ResourceResolver - make sure to whitelist the bundle
    Session session = wfsession.adaptTo(Session.class);
    ResourceResolver resourceResolver = resolverFactory.getResourceResolver(Collections.singletonMap("user.jcr.session", (Object) session));  
         
    // Create leanest replication options for activation
    ReplicationOptions options = new ReplicationOptions();
    // Do not create new versions as this adds to overhead
    options.setSuppressVersions(true);
    // Avoid sling job overhead by forcing synchronous. Note this will result in serial activation.
    options.setSynchronous(true);
    // Do NOT suppress status update of resource (set replication properties accordingly)
    options.setSuppressStatusUpdate(false);  

    log.info("**** ABOUT TO REPLICATE" ) ;  
    //Rep the content  	replicate(Session session, ReplicationActionType type, String path)
    replicator.replicate(session,ReplicationActionType.ACTIVATE,path); 
    log.info("**** REPLICATED" ) ;  
    
   
}
catch(Exception e)
{
    e.printStackTrace();
    log.info("**** Error: " +e.getMessage()) ;  
}
return null;
}
}

Modify the Maven POM file

Add the following POM dependency to the POM file located at C:\AdobeCQ\repWorkflow.

<dependency>
    <groupId>com.adobe.aem</groupId>
    <artifactId>uber-jar</artifactId>
    <version>6.4.0</version>
    <classifier>apis</classifier>
    <scope>provided</scope>
</dependency>
               
  <dependency>
       <groupId>org.apache.geronimo.specs</groupId>
       <artifactId>geronimo-atinject_1.0_spec</artifactId>
       <version>1.0</version>
       <scope>provided</scope>
   </dependency>

When you add new Java classes under core, you need to modify a POM file to successfully build the OSGi bundle. You modify the POM file located at C:\AdobeCQ\repWorkflow\core. The following code represents this POM file.

<?xml version="1.0" encoding="UTF-8"?>
<!--
 |  Copyright 2017 Adobe Systems Incorporated
 |
 |  Licensed under the Apache License, Version 2.0 (the "License");
 |  you may not use this file except in compliance with the License.
 |  You may obtain a copy of the License at
 |
 |      http://www.apache.org/licenses/LICENSE-2.0
 |
 |  Unless required by applicable law or agreed to in writing, software
 |  distributed under the License is distributed on an "AS IS" BASIS,
 |  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 |  See the License for the specific language governing permissions and
 |  limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>repWorkflow</groupId>
        <artifactId>repWorkflow</artifactId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <artifactId>repWorkflow.core</artifactId>
    <packaging>bundle</packaging>
    <name>repWorkflow - Core</name>
    <description>Core bundle for repWorkflow</description>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.sling</groupId>
                <artifactId>maven-sling-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <extensions>true</extensions>
                <configuration>
                    <instructions>
                        <!-- Import any version of javax.inject, to allow running on multiple versions of AEM -->
                        <Import-Package>javax.inject;version=0.0.0,*</Import-Package>
                        <Sling-Model-Packages>
                            com.aem.community.core
                        </Sling-Model-Packages>
                    </instructions>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <!-- OSGi Dependencies -->
        <dependency>
            <groupId>com.adobe.aem</groupId>
            <artifactId>uber-jar</artifactId>
            <classifier>apis</classifier>
        </dependency>
     
        <dependency>
            <groupId>org.apache.geronimo.specs</groupId>
            <artifactId>geronimo-atinject_1.0_spec</artifactId>
        </dependency>
        <dependency>
            <groupId>org.osgi</groupId>
            <artifactId>osgi.core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.osgi</groupId>
            <artifactId>osgi.cmpn</artifactId>
        </dependency>
        <dependency>
            <groupId>org.osgi</groupId>
            <artifactId>osgi.annotation</artifactId>
        </dependency>
        <!-- Other Dependencies -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </dependency>
        <dependency>
            <groupId>javax.jcr</groupId>
            <artifactId>jcr</artifactId>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
        </dependency>
        <dependency>
            <groupId>com.adobe.aem</groupId>
            <artifactId>uber-jar</artifactId>
            <classifier>apis</classifier>
        </dependency>
        <dependency>
            <groupId>org.apache.sling</groupId>
            <artifactId>org.apache.sling.models.api</artifactId>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
        </dependency>
        <dependency>
            <groupId>junit-addons</groupId>
            <artifactId>junit-addons</artifactId>
        </dependency>
    </dependencies>
</project>

Build the OSGi bundle using Maven

To build the OSGi bundle by using Maven, perform these steps:

  1. Open the command prompt and go to the C:\AdobeCQ\repWorkflow.
  2. Run the following maven command: mvn -PautoInstallPackage install.
  3. The OSGi component can be found in the following folder: C:\AdobeCQ\repWorkflow\core\target. The file name of the OSGi component is repWorkflow.core-1.0-SNAPSHOT.jar.

The command -PautoInstallPackage automatically deploys the OSGi bundle to AEM.

View the Active OSGi bundle

After you deploy the OSGi bundle, you can see it in the Apache Felix Web Console.

OSGi
OSGi bundle

View your OSGi bundle by performing these steps:

  1. Login to Adobe Apache Felix Web Console at http://server:port/system/console/bundles (default admin user = admin with password= admin).
  2. Click the Bundles tab, sort the bundle list by Id, and note the Id of the last bundle.

Create an AEM Workflow that uses the custom workflow step that approves an asset

In this step, you are going to modify an existing workflow to it uses the custom step that you created. Open the existing workflow named Request for Activatation

http://localhost:4502/cf#/conf/global/settings/workflow/models/request_for_activation.html

Delete the last step and then add a Project Step. Edit the step and select My Replication Step, as shown in this illustration. 

RepStep
An AEM Workflow Custom Step that uses the Replication API

One done, save the workflow. Now the workflow model looks like the following illustration. 

 

Model
The Request for Activation workflow model

Replicate Content using the Workflow

The final task to peform is to replicate content using the Workflow. In this example, content from We Retail is replicated using the custom workflow step. Open the following page in AEM: 

http://localhost:4502/editor.html/content/we-retail/us/en.html

Modify the some text on the page and use the workflow to replicate the content, as shown in the following video. 

Using the AEM Replication API in a workflow

Using the AEM Replication API in a workflow

Using a custom Replication Agent

Suppose that you have a custom Replicaton Agent and you want to use that instead of the default agent. In this case, you can use the custom Agent by modifying a few lines of code in your Java class. Suppose you have an agent as shown in this illustration. 

CustomAgent
A Custom Replication Agent

Notice that this custom replication agent is named scottrep.  You can use this custom replication agent in your Java code.

 

 // Create leanest replication options for activation
    ReplicationOptions options = new ReplicationOptions();
    // Do not create new versions as this adds to overhead
    options.setSuppressVersions(true);
    // Avoid sling job overhead by forcing synchronous. Note this will result in serial activation.
    options.setSynchronous(true);
    // Do NOT suppress status update of resource (set replication properties accordingly)
    options.setSuppressStatusUpdate(false); 
    
    
  
    
    //Use a custom Replication Agent 
 
   options.setFilter(new AgentFilter(){
       public boolean isIncluded(final Agent agent) {
           return "scottrep".equals(agent.getId());
       }
   });
    

    log.info("**** ABOUT TO REPLICATE" ) ;  
    //Rep the content  	replicate(Session session, ReplicationActionType type, String path)
    replicator.replicate(session,ReplicationActionType.ACTIVATE,path,options); 
    log.info("**** REPLICATED" ) ;  

Note:

The Java code shown earlier in this article uses the default replication agent. This uses the custom replication agent. 

See also

This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License  Twitter™ and Facebook posts are not covered under the terms of Creative Commons.

Legal Notices   |   Online Privacy Policy