Adobe-Consulting-Services/acs-aem-commons

View on GitHub
bundle/src/main/java/com/adobe/acs/commons/indesign/dynamicdeckdynamo/workflow/processes/impl/DynamicDeckBackTrackProcess.java

Summary

Maintainability
A
0 mins
Test Coverage
/*
 * ACS AEM Commons
 *
 * Copyright (C) 2013 - 2023 Adobe
 *
 * 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.
 */

package com.adobe.acs.commons.indesign.dynamicdeckdynamo.workflow.processes.impl;

import com.adobe.acs.commons.indesign.dynamicdeckdynamo.constants.DynamicDeckDynamoConstants;
import com.adobe.acs.commons.indesign.dynamicdeckdynamo.exception.DynamicDeckDynamoException;
import com.adobe.acs.commons.indesign.dynamicdeckdynamo.utils.DynamicDeckUtils;
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.WorkflowProcess;
import com.adobe.granite.workflow.metadata.MetaDataMap;
import com.day.cq.dam.api.DamConstants;
import com.day.cq.dam.commons.util.DamUtil;
import com.day.cq.wcm.api.NameConstants;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.jackrabbit.core.fs.FileSystem;
import org.apache.jackrabbit.webdav.DavConstants;
import org.apache.sling.api.resource.ModifiableValueMap;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ValueMap;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.*;
import org.xml.sax.SAXException;

import javax.jcr.Session;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerFactoryConfigurationError;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

/**
 * This process tracks back the properties which are changed in the generated deck and updates its respective properties in all the assets.
 */
@Component(service = WorkflowProcess.class, property = {"process.label=Dynamic Deck Dynamo Write Back Process"})
public class DynamicDeckBackTrackProcess implements WorkflowProcess {
    private static final Logger LOGGER = LoggerFactory.getLogger(DynamicDeckBackTrackProcess.class);

    @Override
    public void execute(WorkItem workItem, WorkflowSession workflowSession, MetaDataMap metaDataMap) throws WorkflowException {
        ResourceResolver resourceResolver;
        try {

            resourceResolver = workflowSession.adaptTo(ResourceResolver.class);

            Session jcrSession = workflowSession.adaptTo(Session.class);
            if (null == jcrSession) {
                LOGGER.error("JCR Session is null");
                return;
            }

            DynamicDeckUtils.updateUserData(jcrSession);
            DynamicDeckUtils.updateUserData(resourceResolver.adaptTo(Session.class));

            Resource assetResource = DynamicDeckUtils.getAssetResourceFromPayload(workItem, resourceResolver);

            if (null == assetResource) {
                LOGGER.error("Asset resource from payload is null");
                return;
            }

            if (isFileEligibleToProcess(assetResource)) {
                InputStream xmlInputStream = DynamicDeckUtils.getInddXmlRenditionInputStream(assetResource);
                if (null == xmlInputStream) {
                    LOGGER.debug("File xml input stream is null, hence skipping the parsing process.");
                    return;
                }
                parseXML(xmlInputStream, resourceResolver);
            } else {
                LOGGER.info("File is not eligible to be parsed, hence skipping the parsing process.");
            }

        } catch (DynamicDeckDynamoException e) {
            throw new WorkflowException("Back track: Error while parsing asset xml", e);
        }


    }

    private void parseXML(InputStream xmlInputStream, ResourceResolver resourceResolver) {
        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            dbf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
            dbf.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); 
            DocumentBuilder dBuilder = dbf.newDocumentBuilder();
            Document doc = dBuilder.parse(xmlInputStream);

            if (doc.hasChildNodes()) {
                final String assetPath = StringUtils.EMPTY;
                readNode(doc.getChildNodes(), assetPath, resourceResolver);
            }

        } catch (ParserConfigurationException | SAXException | IOException | DOMException | TransformerFactoryConfigurationError | DynamicDeckDynamoException e) {
            LOGGER.error("Error while processing the xml template ", e);
        }
    }

    private void readNode(NodeList nodeList, String assetPath, ResourceResolver resourceResolver) throws DOMException, DynamicDeckDynamoException {

        for (int count = 0; count < nodeList.getLength(); count++) {

            Node tempNode = nodeList.item(count);

            // make sure it's element node.
            if (tempNode.getNodeType() != Node.ELEMENT_NODE) {
                continue;
            }

            if (tempNode.hasAttributes()) {
                // get attributes names and values
                NamedNodeMap nodeMap = tempNode.getAttributes();
                Node sectionType = nodeMap != null ? nodeMap.getNamedItem(DynamicDeckDynamoConstants.XML_ATTR_SECTION_TYPE) : null;

                if (sectionType != null) {
                    assetPath = getAssetPath(nodeMap, assetPath);

                    if (StringUtils.isNotBlank(assetPath)) {
                        retrieveFieldValues(assetPath, tempNode, resourceResolver);
                    }
                }
            }

            if (tempNode.hasChildNodes()) {
                // loop again if has child nodes
                readNode(tempNode.getChildNodes(), assetPath, resourceResolver);

            }
        }
        DynamicDeckUtils.commit(resourceResolver);
    }

    private void retrieveFieldValues(String assetPath, Node sectionNode, ResourceResolver resourceResolver) throws DynamicDeckDynamoException {
        NodeList childNodes = sectionNode.getChildNodes();
        for (int i = 0; i < childNodes.getLength(); i++) {
            Node childNode = childNodes.item(i);
            if (childNode.getNodeType() != Node.ELEMENT_NODE) {
                continue;
            }
            if (childNode.hasAttributes()) {
                // get attributes names and values
                NamedNodeMap nodeMap = childNode.getAttributes();
                Node fieldTypeAttr = nodeMap != null ? nodeMap.getNamedItem(DynamicDeckDynamoConstants.XML_ATTR_FIELD_TYPE) : null;
                Node dataSyncAttr = nodeMap != null ? nodeMap.getNamedItem(DynamicDeckDynamoConstants.XML_ATTR_DATA_SYNC) : null;
                Node isArrayAttr = nodeMap != null ? nodeMap.getNamedItem(DynamicDeckDynamoConstants.XML_ATTR_IS_ARRAY) : null;
                Boolean isArray = isArrayAttr != null && "true".equals(isArrayAttr.getNodeValue());

                if (fieldTypeAttr != null && dataSyncAttr != null && "true".equals(dataSyncAttr.getNodeValue())) {

                    Resource assetResource = resourceResolver.getResource(assetPath);
                    ModifiableValueMap mValueMap = null;
                    if (assetResource != null) {
                        mValueMap = assetResource.adaptTo(ModifiableValueMap.class);
                    }
                    if (mValueMap != null) {
                        String fieldType = fieldTypeAttr.getNodeValue();
                        switch (fieldType) {
                            case "image":
                                handleImageType(assetResource, childNode, resourceResolver, isArray);
                                break;
                            case "text":
                                handleTextType(assetResource, childNode, isArray);
                                break;
                            default:
                        }
                    }

                }
            }
        }
    }

    private void handleTextType(Resource assetResource, Node childNode, Boolean isArray) {
        Node propertyPathNode = childNode.getAttributes().getNamedItem(DynamicDeckDynamoConstants.XML_ATTR_PROPERTY_PATH);
        if (propertyPathNode != null) {
            String propertyPath = getAssetPropertyPath(propertyPathNode.getNodeValue(), assetResource.getValueMap());
            String textValue = childNode.getTextContent();
            setNewPropertyValue(isArray, propertyPath, assetResource, textValue);
        }
    }

    private void setNewPropertyValue(Boolean isArray, String propertyPath, Resource assetResource, String nodeContentValue) {
        String nodePath = StringUtils.substringBeforeLast(propertyPath, "/");
        ModifiableValueMap properties;
        if (StringUtils.isNotBlank(nodePath)) {
            Resource childResource = assetResource.getChild(nodePath);
            properties = childResource.adaptTo(ModifiableValueMap.class);
            propertyPath = StringUtils.substringAfterLast(propertyPath, "/");
        } else {
            properties = assetResource.adaptTo(ModifiableValueMap.class);
        }
        if (isArray) {
            String index = StringUtils.substringBetween(propertyPath, "[", "]");
            propertyPath = StringUtils.substringBeforeLast(propertyPath, "[");
            int indexVal = Integer.parseInt(index);
            String[] values = properties.get(propertyPath, String[].class);
            if (indexVal >= values.length) {
                values = Arrays.copyOf(values, indexVal + 1);
            }
            values[indexVal] = nodeContentValue;
            properties.put(propertyPath, values);

        } else {
            properties.put(propertyPath, nodeContentValue);
        }
    }

    private void handleImageType(Resource assetResource, Node childNode, ResourceResolver resourceResolver, Boolean isArray) throws DynamicDeckDynamoException {
        Node propertyPathNode = childNode.getAttributes().getNamedItem(DynamicDeckDynamoConstants.XML_ATTR_PROPERTY_PATH);
        if (propertyPathNode != null) {
            String propertyPath = getAssetPropertyPath(propertyPathNode.getNodeValue(), assetResource.getValueMap());
            Node hrefNode = childNode.getAttributes().getNamedItem(DavConstants.XML_HREF);
            try {
                if (hrefNode != null) {
                    String hrefValue = hrefNode.getNodeValue();
                    if (StringUtils.contains(hrefValue, "INDD-SERVER-DOCUMENTS/")) {
                        return;
                    }
                    String completeHrefValue = null;
                    if (hrefValue.contains(DamConstants.MOUNTPOINT_ASSETS)) {
                        String hrefEncodedValue = StringUtils.substringAfter(
                                URLDecoder.decode(hrefValue, StandardCharsets.UTF_8.toString()), DamConstants.MOUNTPOINT_ASSETS);
                        completeHrefValue = DamConstants.MOUNTPOINT_ASSETS + hrefEncodedValue;
                    }
                    if (null == completeHrefValue) {
                        LOGGER.error("Back track root path is not correct {}", hrefValue);
                        return;
                    }
                    Resource imageResource = resourceResolver.getResource(completeHrefValue);
                    if (DamUtil.isAsset(imageResource)) {
                        setNewPropertyValue(isArray, propertyPath, assetResource, completeHrefValue);

                    } else {
                        LOGGER.error("ERROR: DATA SYNC : Invalid asset embedded. Asset not found in repository {}", hrefValue);
                    }
                }
            } catch (UnsupportedEncodingException e) {
                throw new DynamicDeckDynamoException("Exception while handling the image type.", e);
            }
        }
    }

    private String getAssetPropertyPath(String nodeValue, ValueMap properties) {
        String propertyPath = nodeValue;
        if (!properties.containsKey(nodeValue)) {
            propertyPath = "jcr:content/metadata/" + propertyPath;
        }
        return propertyPath;
    }

    private String getAssetPath(NamedNodeMap nodeMap, String assetPath) {
        Node assetPathNode = nodeMap.getNamedItem(DynamicDeckDynamoConstants.XML_ATTR_ASSETPATH);
        if (assetPathNode != null) {
            return assetPathNode.getNodeValue();
        }

        return assetPath;
    }

    private boolean isFileEligibleToProcess(Resource assetResource) {

        Resource metadataResource = assetResource.getResourceResolver()
                .getResource(assetResource.getPath() + FileSystem.SEPARATOR + NameConstants.NN_CONTENT
                        + FileSystem.SEPARATOR + DamConstants.METADATA_FOLDER);
        if (null == metadataResource) {
            LOGGER.error("Metadata resource is null, hence returning false");
            return false;
        }
        ValueMap metadataValueMap = metadataResource.getValueMap();

        Resource jcrContentResource = assetResource.getResourceResolver()
                .getResource(assetResource.getPath() + FileSystem.SEPARATOR + NameConstants.NN_CONTENT);
        if (null == jcrContentResource) {
            LOGGER.error("JCR Content resource is null, hence returning false");
            return false;
        }
        ValueMap jcrContentValueMap = jcrContentResource.getValueMap();


        String assetMimeType = metadataValueMap.get(DamConstants.DC_FORMAT, String.class);
        String assetTemplateType = jcrContentValueMap.get(DynamicDeckDynamoConstants.PN_INDD_TEMPLATE_TYPE, String.class);

        String[] eligibleAssetMimeType = {DynamicDeckDynamoConstants.INDESIGN_MIME_TYPE};
        return StringUtils.isNotEmpty(assetMimeType) && ArrayUtils.contains(eligibleAssetMimeType, assetMimeType)
                && StringUtils.isNotBlank(assetTemplateType) && assetTemplateType.equals(DynamicDeckDynamoConstants.DECK_TYPE);
    }

}