XMPWritebackProcess.java 13.9 KB
/*
 * Decompiled with CFR 0_118.
 * 
 * Could not load the following classes:
 *  com.adobe.granite.asset.api.Asset
 *  com.adobe.granite.asset.api.AssetMetadata
 *  com.adobe.xmp.core.XMPMetadata
 *  com.adobe.xmp.core.XMPNode
 *  com.adobe.xmp.core.XMPSimple
 *  com.day.cq.dam.api.Asset
 *  com.day.cq.dam.api.Rendition
 *  com.day.cq.dam.api.handler.xmp.XMPHandler
 *  com.day.cq.dam.api.handler.xmp.XMPWriteBackOptions
 *  com.day.cq.dam.commons.process.AbstractAssetWorkflowProcess
 *  com.day.cq.workflow.WorkflowException
 *  com.day.cq.workflow.WorkflowSession
 *  com.day.cq.workflow.exec.WorkItem
 *  com.day.cq.workflow.exec.Workflow
 *  com.day.cq.workflow.exec.WorkflowData
 *  com.day.cq.workflow.metadata.MetaDataMap
 *  javax.jcr.Node
 *  javax.jcr.Property
 *  javax.jcr.RepositoryException
 *  javax.jcr.Session
 *  org.apache.commons.lang.StringUtils
 *  org.apache.felix.scr.annotations.Component
 *  org.apache.felix.scr.annotations.Properties
 *  org.apache.felix.scr.annotations.Property
 *  org.apache.felix.scr.annotations.Reference
 *  org.apache.felix.scr.annotations.ReferenceCardinality
 *  org.apache.felix.scr.annotations.ReferencePolicy
 *  org.apache.felix.scr.annotations.Service
 *  org.apache.sling.api.resource.Resource
 *  org.slf4j.Logger
 *  org.slf4j.LoggerFactory
 */
package com.day.cq.dam.core.process;

import com.adobe.granite.asset.api.AssetMetadata;
import com.adobe.xmp.core.XMPMetadata;
import com.adobe.xmp.core.XMPNode;
import com.adobe.xmp.core.XMPSimple;
import com.day.cq.dam.api.Asset;
import com.day.cq.dam.api.Rendition;
import com.day.cq.dam.api.handler.xmp.XMPHandler;
import com.day.cq.dam.api.handler.xmp.XMPWriteBackOptions;
import com.day.cq.dam.commons.process.AbstractAssetWorkflowProcess;
import com.day.cq.dam.core.impl.handler.xmp.XMPWriteBackOptionsImpl;
import com.day.cq.workflow.WorkflowException;
import com.day.cq.workflow.WorkflowSession;
import com.day.cq.workflow.exec.WorkItem;
import com.day.cq.workflow.exec.Workflow;
import com.day.cq.workflow.exec.WorkflowData;
import com.day.cq.workflow.metadata.MetaDataMap;
import java.io.InputStream;
import java.io.PushbackInputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.jcr.Node;
import javax.jcr.Property;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import org.apache.commons.lang.StringUtils;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.ReferencePolicy;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.api.resource.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component(metatype=0)
@Service
@Properties(value={@org.apache.felix.scr.annotations.Property(name="process.label", value={"XMP Writeback"})})
public class XMPWritebackProcess
extends AbstractAssetWorkflowProcess {
    private static final String PS_AUX_ISO = "psAux:ISO";
    private static final String EXIF_FLASH = "Flash";
    private static final int MAGIC_SIZE = 1024;
    private static final String EPS_MIMETYPE = "application/postscript";
    private static final byte[] PS_START = "%!".getBytes();
    private static final byte[] PS_ADOBE = "PS-Adobe-".getBytes();
    private static final byte[] EPS_TYPE = "EPS".getBytes();
    private static final Logger log = LoggerFactory.getLogger(XMPWritebackProcess.class);
    @Reference(policy=ReferencePolicy.DYNAMIC, cardinality=ReferenceCardinality.OPTIONAL_UNARY)
    protected XMPHandler xmpHandler;
    private final boolean debug = log.isDebugEnabled();

    public void execute(WorkItem workItem, WorkflowSession workflowSession, MetaDataMap metaDataMap) throws WorkflowException {
        if (null == this.xmpHandler) {
            log.warn("XMP Writeback is not supported on this platform or required libraries are missing !!!");
            workflowSession.terminateWorkflow(workItem.getWorkflow());
            return;
        }
        String[] args = this.buildArguments(metaDataMap);
        String payloadPath = null;
        if (workItem.getWorkflowData().getPayloadType().equals("JCR_PATH")) {
            payloadPath = workItem.getWorkflowData().getPayload().toString();
        }
        log.info("payload path :" + payloadPath);
        Session session = workflowSession.getSession();
        Asset asset = this.getAssetFromPayload(workItem, session);
        String userId = (String)workItem.getWorkflowData().getMetaDataMap().get("userId", String.class);
        if (null != asset && null != payloadPath) {
            try {
                Node assetNode = (Node)asset.adaptTo(Node.class);
                Node content = assetNode.getNode("jcr:content");
                Node metadata = assetNode.getNode("jcr:content/metadata");
                Node payloadNode = session.getNode(payloadPath);
                if (payloadNode.hasProperty("newRendition")) {
                    if (log.isDebugEnabled()) {
                        log.debug("Skipping the {} execution.", (Object)XMPWritebackProcess.class.getName());
                    }
                    payloadNode.getProperty("newRendition").remove();
                    session.save();
                    workflowSession.terminateWorkflow(workItem.getWorkflow());
                    return;
                }
                if (content.hasProperty("newRendition")) {
                    if (log.isDebugEnabled()) {
                        log.debug("Skipping the {} execution.", (Object)XMPWritebackProcess.class.getName());
                    }
                    content.getProperty("newRendition").remove();
                    session.save();
                    workflowSession.terminateWorkflow(workItem.getWorkflow());
                } else {
                    String mime = asset.getMimeType();
                    boolean createVersion = true;
                    if (args.length > 0) {
                        boolean bl = createVersion = "true".equals(this.getValuesFromArgs("createversion", args).get(0));
                    }
                    if (this.xmpHandler.isSupported(mime) && this.isWritebackSupported(asset)) {
                        XMPWriteBackOptionsImpl writeBackOptions = new XMPWriteBackOptionsImpl();
                        writeBackOptions.createVersion(createVersion);
                        List renditions = this.getValuesFromArgs("rendition", args);
                        HashSet<Rendition> XMPWriteBackRenditions = new HashSet<Rendition>();
                        for (String rendition : renditions) {
                            Rendition currentRendition = asset.getRendition(rendition);
                            if (null == currentRendition) continue;
                            XMPWriteBackRenditions.add(currentRendition);
                        }
                        writeBackOptions.setRenditions(XMPWriteBackRenditions);
                        this.writeXmp(asset, workItem, workflowSession, writeBackOptions);
                        Rendition orgRen = asset.getOriginal();
                        Node orgRendNode = (Node)orgRen.adaptTo(Node.class);
                        Node orgRendContNode = orgRendNode.getNode("jcr:content");
                        orgRendContNode.setProperty("jcr:lastModifiedBy", userId);
                        content.setProperty("jcr:lastModifiedBy", userId);
                        session.save();
                    } else if (this.debug) {
                        log.debug("XMP Writeback is not supported for type {} using implementation {}", (Object)mime, (Object)this.xmpHandler.getClass().getName());
                    }
                }
            }
            catch (Throwable e) {
                log.error(e.getMessage());
                log.debug("Stack Trace", e);
                try {
                    if (session.hasPendingChanges()) {
                        session.refresh(false);
                    }
                }
                catch (RepositoryException re) {
                    log.error("Failed to refresh workflow session", (Throwable)re);
                }
            }
        } else {
            String wfPayload = workItem.getWorkflowData().getPayload().toString();
            String message = "execute: cannot writeback xmp, asset [{" + wfPayload + "}] in payload doesn't exist for workflow [{" + workItem.getId() + "}].";
            throw new WorkflowException(message);
        }
    }

    private boolean isWritebackSupported(Asset asset) {
        String mimeType = asset.getMimeType();
        if ("application/postscript".equals(mimeType)) {
            try {
                PushbackInputStream pin = new PushbackInputStream(asset.getOriginal().getStream(), 1024);
                byte[] data = new byte[1024];
                int len = pin.read(data);
                if (len <= 0) {
                    return false;
                }
                double adobeMarker = this.getAdobeMarkerFromEPS(data, len);
                if (adobeMarker > 3.0) {
                    return true;
                }
                return false;
            }
            catch (Exception e) {
                log.warn("error while reading AI/EPS file:  [{}]: ", (Throwable)e);
            }
        }
        return true;
    }

    private double getAdobeMarkerFromEPS(byte[] data, int len) {
        double adobeMarker = 0.0;
        int off = this.locate(PS_START, data, 0, len);
        if (off == -1) {
            return adobeMarker;
        }
        if ((off = this.locate(PS_ADOBE, data, off, len)) == -1) {
            return adobeMarker;
        }
        int epsTypeLocation = this.locate(EPS_TYPE, data, off, len);
        if (epsTypeLocation == -1) {
            return adobeMarker;
        }
        String adobeMarkerString = new String(data, off, epsTypeLocation - off);
        try {
            adobeMarker = new Double(adobeMarkerString.replace(new String(EPS_TYPE), "").trim());
        }
        catch (NumberFormatException ne) {
            log.warn("Exception occured while reading PS_ADOBE marker from eps file: " + ne.getMessage());
        }
        return adobeMarker;
    }

    private int locate(byte[] pattern, byte[] data, int off, int len) {
        int i = 0;
        while (i < pattern.length && off < len) {
            i = pattern[i] == data[off] ? ++i : 0;
            ++off;
        }
        return i == pattern.length ? off : -1;
    }

    private void writeXmp(Asset asset, WorkItem workItem, WorkflowSession workflowSession, XMPWriteBackOptions writeBackOptions) {
        Session session = workflowSession.getSession();
        try {
            Node assetNode = (Node)asset.adaptTo(Node.class);
            Node content = assetNode.getNode("jcr:content");
            XMPMetadata metadata = ((com.adobe.granite.asset.api.Asset)((Resource)asset.adaptTo(Resource.class)).adaptTo(com.adobe.granite.asset.api.Asset.class)).getAssetMetadata().getXMP();
            if (metadata.get("http://ns.adobe.com/exif/1.0/aux/", "psAux:ISO") != null) {
                metadata.setSimple("http://ns.adobe.com/exif/1.0/aux/", "psAux:ISO", "");
            }
            if (metadata.get("http://ns.adobe.com/exif/1.0/", "Flash") != null) {
                metadata.remove("http://ns.adobe.com/exif/1.0/", "Flash");
            }
            String userId = (String)workItem.getWorkflowData().getMetaDataMap().get("userId", String.class);
            content.setProperty("cq:versionCreator", userId);
            this.xmpHandler.writeXmpMetadata(asset, metadata, writeBackOptions);
        }
        catch (Throwable e) {
            log.warn("XMP Writeback is not supported on this platform or required libraries are missing !!!");
            log.debug("Stack Trace", e);
            try {
                if (session.hasPendingChanges()) {
                    session.refresh(false);
                }
            }
            catch (RepositoryException re) {
                log.error("Failed to refresh workflow session", (Throwable)re);
            }
        }
    }

    synchronized void bindXmpHandler(XMPHandler handler) {
        this.xmpHandler = handler;
        log.debug("binding xmp handler");
    }

    synchronized void unbindXmpHandler(XMPHandler handler) {
        this.xmpHandler = null;
        log.debug("un-binding xmp handler");
    }

    public String[] buildArguments(MetaDataMap metaData) {
        String[] renditions;
        String processArgs = (String)metaData.get(Arguments.PROCESS_ARGS.name(), String.class);
        if (processArgs != null && !processArgs.equals("")) {
            return processArgs.split(",");
        }
        ArrayList<String> arguments = new ArrayList<String>();
        String createVersion = (String)metaData.get(Arguments.CREATE_VERSION.name(), String.class);
        if (StringUtils.isNotBlank((String)createVersion)) {
            StringBuilder builder = new StringBuilder();
            builder.append(Arguments.CREATE_VERSION.getArgumentPrefix()).append(createVersion);
            arguments.add(builder.toString());
        }
        if ((renditions = (String[])metaData.get(Arguments.RENDITION.name(), String[].class)) != null) {
            for (String rendition : renditions) {
                StringBuilder builder = new StringBuilder();
                builder.append(Arguments.RENDITION.getArgumentPrefix()).append(rendition);
                arguments.add(builder.toString());
            }
        }
        return arguments.toArray(new String[arguments.size()]);
    }

    public static enum Arguments {
        PROCESS_ARGS("PROCESS_ARGS"),
        CREATE_VERSION("createversion"),
        RENDITION("rendition");
        
        private String argumentName;

        private Arguments(String argumentName) {
            this.argumentName = argumentName;
        }

        public String getArgumentName() {
            return this.argumentName;
        }

        public String getArgumentPrefix() {
            return this.argumentName + ":";
        }
    }

}