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