OnDeploy Scripts


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

Advertisement

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s