During the course of project development, there would be many scenarios where some content (property/node) needs to be modified throughout the content hierarchy. That content fixup needs to be done in all the environments and many times its just a one time job. To cover this very common use case, ACS Commons (3.15+) came up with OnDeploy Scripts. They allow developers to create one-time scripts that execute upon deployment of code to an AEM server. Multiple scripts can be used for any customization in JCR and can be run in a Queue as per need.
Implementation Steps :
- 1. The first step is to add a configuration com.adobe.acs.commons.ondeploy.impl.OnDeployExecutorImpl.xml for an environment where OnDeploy script is to be used as per the runmode.
- 2. Add an OnDeployScriptProviderImpl.java class to initiate the OnDeploy Scripts.
import com.adobe.acs.commons.ondeploy.OnDeployScriptProvider;
import com.adobe.acs.commons.ondeploy.scripts.OnDeployScript;
import com.hashim.core.services.admin.scripts.Script1;
import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Component;
import java.util.Arrays;
import java.util.List;
/**
* Class to add onDeploy Scripts in an environment.
* Status of scripts can be checked at /var/acs-commons/on-deploy-scripts-status
* To execute Multiple time - delete the node of the script from /var/acs-commons/on-deploy-scripts-status
*
*/
@Component(
immediate = true,
service = OnDeployScriptProvider.class,
property = {
"process.label=OnDeployScript Provider Service.",
Constants.SERVICE_DESCRIPTION + "=Developer service that identifies code scripts to execute upon deployment"
})
public class OnDeployScriptProviderImpl implements OnDeployScriptProvider {
@Override
public List getScripts() {
return Arrays.asList(
new Script1()
/* Sample List of script instances - e.g. new MyScript1(), new MyScript2(), new MyScript3()*/
);
}
}
- 3. Create OnDeploy Script extending OnDeployScriptBase and add it in the OnDeployScriptProviderImpl Provider Class.
import com.adobe.acs.commons.ondeploy.scripts.OnDeployScriptBase;
import com.day.cq.search.PredicateGroup;
import com.day.cq.search.Query;
import com.day.cq.search.QueryBuilder;
import com.day.cq.search.result.SearchResult;
import javax.jcr.*;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
*This Sample OnDeploy Script is used to change all Multifield properties with Checkbox in Classic UI to Touch UI compatible.
*/
public class Script1 extends OnDeployScriptBase {
@Override
protected void execute() throws Exception {
/*Add Your Logic here. */
this.searchAndUpdateCheckboxProperty("/content/we-retail","weretail/components/content/legal", "links"); //Sample Content & Component.
}
/** Method to search the Repository in a provided contentPath with a resourceType
*
* @param contentPath
* @param resourcePath
* @param propertyName
* @throws RepositoryException
*/
protected final void searchAndUpdateCheckboxProperty(String contentPath , String resourcePath, String propertyName) throws RepositoryException {
Map map = new HashMap();
map.put("p.limit", "-1");
map.put("path", contentPath);
map.put("1_property", "sling:resourceType");
map.put("1_property.value", resourcePath);
Query query = ((QueryBuilder)getResourceResolver().adaptTo(QueryBuilder.class)).createQuery(PredicateGroup.create(map), getSession());
SearchResult result = query.getResult();
logger.info("[Script1] Total Node Results :"+ result.getTotalMatches() );
int count = 0 ;
boolean isNodeUpdated = false ;
Iterator nodeItr = result.getNodes();
if (nodeItr.hasNext()) {
while(nodeItr.hasNext()) {
Node node = (Node)nodeItr.next();
isNodeUpdated = false ;
isNodeUpdated = this.updateMultifieldProperty(node, propertyName);
if(isNodeUpdated){
count++;
if(count%100 ==0){
logger.info("[Script1] Still Processing. Current Count :"+ count);
}
}
}
} else {
logger.error("[Script1] No nodes found with resource type: {}", resourcePath);
}
logger.info("[Script1] Total Nodes Updated :"+ count);
}
/**
* Method to updated the Multifield Property to remove [] or [ or ] from the properties.
* @param node
* @param propertyName
* @throws RepositoryException
*/
protected final boolean updateMultifieldProperty(Node node , String propertyName) throws RepositoryException {
boolean hasProperty = node.hasProperty(propertyName);
boolean isNodeUpdated = false ;
try {
if (hasProperty) {
Property multifieldProperty = node.getProperty(propertyName);
if (multifieldProperty.isMultiple()) {
ValueFactory valueFactory = node.getSession().getValueFactory();
Value[] propertyValues = multifieldProperty.getValues();
Value[] newValues = new Value[propertyValues.length];
int i = 0;
for (Value value : propertyValues) {
String processedValue = value.getString();
processedValue = processedValue.replaceAll("\\[\\]", "\"\"");
processedValue = processedValue.replaceAll("\\[|\\]", "");
newValues[i++] = valueFactory.createValue(processedValue);
}
node.setProperty(propertyName, newValues);
isNodeUpdated = true ;
logger.debug("[Script1] Node Updated :" + node.getPath());
} else {
Value propertyValue = multifieldProperty.getValue();
String processedValue = propertyValue.getString();
processedValue = processedValue.replaceAll("\\[\\]", "\"\"");
processedValue = processedValue.replaceAll("\\[|\\]", "");
node.setProperty(propertyName, processedValue);
isNodeUpdated = true ;
logger.debug("[Script1] Node Updated :" + node.getPath());
}
} else {
logger.error("[Script1] Property doesn't exist :" + node.getPath());
}
}catch (Exception e){
logger.error("[Script1] Exception in updateMultifieldProperty() method :",e.getMessage());
}
return isNodeUpdated ;
}
}
Monitoring:
- Once the code with OnDeploy script is Deployed to AEM server, logs can be viewed to check the status of the script. Another way to verify the Success/Failure status of Script is to check the node: /var/acs-commons/on-deploy-script-status
- If you want to re-run the script, just delete the node for your script and deploy the code again.
- If a Script is failed in one build, it will be marked as failed and would automatically re-run in subsequent deployment.
- A failing script will prevent any later scripts in the list from running, in case later scripts depend on the success of earlier scripts.
- You can also monitor the duration of the script execution and status at this node.
Reference :
https://adobe-consulting-services.github.io/acs-aem-commons/features/on-deploy-scripts/index.html