ExifToolExtractMetadataProcess.java 19.3 KB
/*
 * Decompiled with CFR 0_118.
 * 
 * Could not load the following classes:
 *  com.adobe.xmp.XMPMeta
 *  com.adobe.xmp.XMPMetaFactory
 *  com.day.cq.dam.api.Asset
 *  com.day.cq.dam.api.Rendition
 *  com.day.cq.dam.api.handler.AssetHandler
 *  com.day.cq.dam.api.metadata.ExtractedMetadata
 *  com.day.cq.dam.commons.metadata.SimpleXmpToJcrMetadataBuilder
 *  com.day.cq.dam.commons.process.AbstractAssetWorkflowProcess
 *  com.day.cq.tagging.JcrTagManagerFactory
 *  com.day.cq.tagging.Tag
 *  com.day.cq.tagging.TagManager
 *  com.day.cq.workflow.WorkflowException
 *  com.day.cq.workflow.WorkflowSession
 *  com.day.cq.workflow.exec.WorkItem
 *  com.day.cq.workflow.exec.WorkflowData
 *  com.day.cq.workflow.metadata.MetaDataMap
 *  javax.jcr.Binary
 *  javax.jcr.Node
 *  javax.jcr.Property
 *  javax.jcr.RepositoryException
 *  javax.jcr.Session
 *  javax.jcr.Value
 *  org.apache.commons.codec.digest.DigestUtils
 *  org.apache.commons.exec.CommandLine
 *  org.apache.commons.exec.DefaultExecutor
 *  org.apache.commons.exec.ExecuteStreamHandler
 *  org.apache.commons.exec.PumpStreamHandler
 *  org.apache.commons.io.IOUtils
 *  org.apache.commons.lang.StringUtils
 *  org.apache.felix.scr.annotations.Activate
 *  org.apache.felix.scr.annotations.Component
 *  org.apache.felix.scr.annotations.Property
 *  org.apache.felix.scr.annotations.Reference
 *  org.apache.felix.scr.annotations.Service
 *  org.apache.sling.api.resource.Resource
 *  org.apache.sling.api.resource.ResourceResolver
 *  org.apache.sling.api.resource.ValueMap
 *  org.apache.sling.commons.mime.MimeTypeService
 *  org.apache.sling.commons.osgi.OsgiUtil
 *  org.osgi.service.component.ComponentContext
 *  org.slf4j.Logger
 *  org.slf4j.LoggerFactory
 */
package com.day.cq.dam.core.process;

import com.adobe.xmp.XMPMeta;
import com.adobe.xmp.XMPMetaFactory;
import com.day.cq.dam.api.Asset;
import com.day.cq.dam.api.Rendition;
import com.day.cq.dam.api.handler.AssetHandler;
import com.day.cq.dam.api.metadata.ExtractedMetadata;
import com.day.cq.dam.commons.metadata.SimpleXmpToJcrMetadataBuilder;
import com.day.cq.dam.commons.process.AbstractAssetWorkflowProcess;
import com.day.cq.tagging.JcrTagManagerFactory;
import com.day.cq.tagging.Tag;
import com.day.cq.tagging.TagManager;
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.WorkflowData;
import com.day.cq.workflow.metadata.MetaDataMap;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.LinkedList;
import javax.jcr.Binary;
import javax.jcr.Node;
import javax.jcr.Property;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Value;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.ExecuteStreamHandler;
import org.apache.commons.exec.PumpStreamHandler;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.commons.mime.MimeTypeService;
import org.apache.sling.commons.osgi.OsgiUtil;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component(metatype=1)
@Service
@org.apache.felix.scr.annotations.Property(name="process.label", value={"Exif ToolExtract Meta Data"})
public class ExifToolExtractMetadataProcess
extends AbstractAssetWorkflowProcess {
    private static final String JCR_CONTENT_JCR_DATA = "jcr:content/jcr:data";
    private static final String METADATA_PROPERTY_NAME_ADOBE_KEYWORDS = "lr:hierarchicalSubject";
    private static final String SYNC_FLAG = "newRendition";
    @Reference
    private JcrTagManagerFactory tagManagerFactory = null;
    private static final Logger log = LoggerFactory.getLogger(ExifToolExtractMetadataProcess.class);
    private String[] defaultFormats = new String[]{"application/octet-stream"};
    private boolean sha1Enabled = false;
    @org.apache.felix.scr.annotations.Property(boolValue={1})
    public static final String ENABLE_SHA1_GEN = "cq.dam.enable.sha1";

    public void execute(WorkItem workItem, WorkflowSession workflowSession, MetaDataMap args) throws WorkflowException {
        block16 : {
            String[] arguments = this.buildArguments(args);
            try {
                Session session = workflowSession.getSession();
                Asset asset = this.getAssetFromPayload(workItem, session);
                if (null != asset) {
                    asset.setBatchMode(true);
                    AssetHandler handler = this.getAssetHandler(asset.getMimeType());
                    if (null != handler) {
                        Node originalBinary;
                        ExtractedMetadata metadata;
                        block15 : {
                            Node assetNode = (Node)asset.adaptTo(Node.class);
                            Node content = assetNode.getNode("jcr:content");
                            if (!content.hasProperty("newRendition")) {
                                content.setProperty("newRendition", true);
                            }
                            metadata = new ExtractedMetadata();
                            File tmpDir = null;
                            Object is = null;
                            Object os = null;
                            try {
                                LinkedList<String> mimeTypes = new LinkedList<String>();
                                String assetMimeType = asset.getMimeType();
                                for (String str : arguments) {
                                    if (!str.startsWith(Arguments.MIME_TYPES.getArgumentPrefix())) continue;
                                    String mt = str.substring(Arguments.MIME_TYPES.getArgumentPrefix().length()).trim();
                                    log.debug("execute: accepted mime type [{}] for asset [{}].", (Object)mt, (Object)asset.getPath());
                                    mimeTypes.add(mt);
                                }
                                if (!mimeTypes.contains(assetMimeType)) {
                                    log.info("execute: mime type [{}] of asset [{}] is not in list of accepted mime types [" + mimeTypes + "], ignoring.", (Object)assetMimeType, (Object)asset.getPath());
                                    return;
                                }
                                tmpDir = File.createTempFile("cqdam", null);
                                tmpDir.delete();
                                tmpDir.mkdir();
                                if (null == asset) break block15;
                                Rendition original = asset.getOriginal();
                                File tmpFile = new File(tmpDir, asset.getName());
                                FileOutputStream fos = new FileOutputStream(tmpFile);
                                IOUtils.copy((InputStream)original.getStream(), (OutputStream)fos);
                                IOUtils.closeQuietly((OutputStream)fos);
                                String lastLine = "";
                                HashMap<String, String> parameters = new HashMap<String, String>();
                                parameters.put("filename", tmpFile.getName());
                                parameters.put("file", tmpFile.getAbsolutePath());
                                parameters.put("directory", tmpDir.getAbsolutePath());
                                parameters.put("basename", tmpFile.getName().replaceFirst("\\..*$", ""));
                                parameters.put("extension", tmpFile.getName().replaceFirst("^.*\\.", ""));
                                try {
                                    for (String argument : arguments) {
                                        if (!argument.startsWith(Arguments.COMMANDS.getArgumentPrefix())) continue;
                                        String cmd = argument.substring(Arguments.COMMANDS.getArgumentPrefix().length()).trim();
                                        CommandLine commandLine = CommandLine.parse((String)cmd, parameters);
                                        lastLine = commandLine.toString();
                                        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                                        DefaultExecutor exec = new DefaultExecutor();
                                        exec.setWorkingDirectory(tmpDir);
                                        PumpStreamHandler streamHandler = new PumpStreamHandler((OutputStream)outputStream);
                                        exec.setStreamHandler((ExecuteStreamHandler)streamHandler);
                                        log.info("execute: executing command line [{}] for asset [{}].", (Object)lastLine, (Object)asset.getPath());
                                        exec.execute(commandLine);
                                        String[] commands = new String[]{"exiftool", (String)parameters.get("file"), ">", "output.txt"};
                                        Runtime rt = Runtime.getRuntime();
                                        String[] metalines = outputStream.toString().split("\\r?\\n");
                                        System.out.println("Here is the standard output of the command:\n");
                                        Object s = null;
                                        for (String st : metalines) {
                                            String[] items = st.split(":");
                                            metadata.setMetaDataProperty(items[0].trim(), (Object)items[1].trim());
                                        }
                                    }
                                }
                                catch (Exception e) {
                                    log.error("execute: failed to execute command [{}] for asset [" + asset.getPath() + "]: ", (Object)lastLine, (Object)e);
                                }
                            }
                            catch (Exception e) {
                                // empty catch block
                            }
                        }
                        metadata.setMetaDataProperty("dam:extracted", (Object)Calendar.getInstance().getTime());
                        Rendition original = asset.getOriginal();
                        if (this.sha1Enabled) {
                            String sha1 = DigestUtils.shaHex((InputStream)original.getStream());
                            metadata.setMetaDataProperty("dam:sha1", (Object)sha1);
                        }
                        if ((originalBinary = (Node)original.adaptTo(Node.class)).hasProperty("jcr:content/jcr:data")) {
                            metadata.setMetaDataProperty("dam:size", (Object)originalBinary.getProperty("jcr:content/jcr:data").getBinary().getSize());
                        }
                        this.resetMimetype(asset, metadata);
                        this.saveMetadata(asset, metadata);
                        this.extractHierarchicalSubjects(asset, session);
                        break block16;
                    }
                    log.error("execute: cannot extract metadata, no handler found for asset [{}] with mime type [{}]", (Object)asset.getPath(), (Object)asset.getMimeType());
                    break block16;
                }
                String wfPayload = workItem.getWorkflowData().getPayload().toString();
                String message = "execute: cannot extract metadata, asset [{" + wfPayload + "}] in payload doesn't exist for workflow [{" + workItem.getId() + "}].";
                throw new WorkflowException(message);
            }
            catch (Exception e) {
                log.warn("unexpected error occurred during metadata extraction. Cause: {}", (Object)e.getMessage(), (Object)e);
            }
        }
    }

    protected void saveMetadata(Asset asset, ExtractedMetadata metadata) {
        Resource resource = (Resource)asset.adaptTo(Resource.class);
        ResourceResolver resolver = resource.getResourceResolver();
        Resource metadataResource = resolver.getResource(resource, "jcr:content/metadata");
        if (null != metadataResource) {
            Node metadataNode = (Node)metadataResource.adaptTo(Node.class);
            SimpleXmpToJcrMetadataBuilder metadataMetadataBuilderXmpTo = new SimpleXmpToJcrMetadataBuilder();
            try {
                metadataMetadataBuilderXmpTo.storeAsXmp(metadata, metadataNode, false);
                InputStream is = metadata.getXmp();
                if (is != null) {
                    XMPMeta meta = XMPMetaFactory.parse((InputStream)is);
                    if (metadataNode.hasProperty("dc:format") && !this.isDefaultFormat(metadataNode.getProperty("dc:format").getValue().getString()) && meta.getPropertyString("http://purl.org/dc/elements/1.1/", "dc:format") != null) {
                        meta.deleteProperty("http://purl.org/dc/elements/1.1/", "dc:format");
                    }
                    metadataMetadataBuilderXmpTo.storeXmp(metadataNode, meta, false);
                }
            }
            catch (Exception e) {
                log.error("saveMetadata: error while saving metdata for asset [{}]: ", (Object)asset.getPath(), (Object)e);
            }
        } else {
            log.error("execute: cannot save metdata for asset [{}], doesn't have metdata node.", (Object)asset.getPath());
        }
    }

    private boolean isDefaultFormat(String format) {
        for (int i = 0; i < this.defaultFormats.length; ++i) {
            if (!this.defaultFormats[i].equals(format)) continue;
            return true;
        }
        return false;
    }

    private void resetMimetype(Asset asset, ExtractedMetadata metadata) {
        if (this.mimeTypeService.getMimeType(asset.getName()) != null) {
            String mimeType = this.mimeTypeService.getMimeType(asset.getName());
            metadata.setMetaDataProperty("dc:format", (Object)mimeType);
        }
    }

    private void extractHierarchicalSubjects(Asset asset, Session session) {
        Resource resource = (Resource)asset.adaptTo(Resource.class);
        ResourceResolver resolver = resource.getResourceResolver();
        Resource metadataResource = resolver.getResource(resource, "jcr:content/metadata");
        if (null != metadataResource) {
            ValueMap props = (ValueMap)metadataResource.adaptTo(ValueMap.class);
            Object[] subjects = (String[])props.get("lr:hierarchicalSubject", (Object)new String[0]);
            log.debug("got hierarchical subjects [{}] with content [{}].", (Object)"lr:hierarchicalSubject", (Object)StringUtils.join((Object[])subjects, (String)", "));
            if (subjects.length > 0) {
                TagManager tagManager = this.tagManagerFactory.getTagManager(session);
                ArrayList<Tag> tags = new ArrayList<Tag>();
                for (Object subject : subjects) {
                    String titlePath = StringUtils.replaceOnce((String)subject, (String)":|", (String)":");
                    Tag tag = tagManager.resolveByTitle(titlePath = StringUtils.replace((String)titlePath, (String)"|", (String)"/"));
                    if (null != tag) {
                        log.debug("got tag [{}] from title path [{}].", (Object)tag.getTagID(), (Object)titlePath);
                        tags.add(tag);
                        continue;
                    }
                    log.warn("could not find tag from title path [{}].", (Object)titlePath);
                }
                if (tags.size() > 0) {
                    try {
                        log.debug("tagging [{}] with [{}] tags.", (Object)asset.getPath(), (Object)tags.size());
                        tagManager.setTags(metadataResource, tags.toArray((T[])new Tag[tags.size()]), asset.isBatchMode());
                    }
                    catch (Exception e) {
                        log.error("cannot save hierarchical subjects for asset [{}]: ", (Object)asset.getPath(), (Object)e);
                    }
                }
            }
        } else {
            log.error("cannot save hierarchical subjects for asset [{}], doesn't have metdata node.", (Object)asset.getPath());
        }
    }

    @Activate
    protected void Actiate(ComponentContext context) throws RepositoryException {
        this.sha1Enabled = OsgiUtil.toBoolean(context.getProperties().get("cq.dam.enable.sha1"), (boolean)true);
    }

    public String[] buildArguments(MetaDataMap metaData) {
        String[] mimetypes;
        String[] thumbnails;
        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[] commands = (String[])metaData.get(Arguments.COMMANDS.name(), String[].class);
        if (commands != null) {
            for (String command : commands) {
                StringBuilder builder = new StringBuilder();
                builder.append(Arguments.COMMANDS.getArgumentPrefix()).append(command);
                arguments.add(builder.toString());
            }
        }
        if ((mimetypes = (String[])metaData.get(Arguments.MIME_TYPES.name(), String[].class)) != null) {
            for (String mimetype : mimetypes) {
                StringBuilder builder = new StringBuilder();
                builder.append(Arguments.MIME_TYPES.getArgumentPrefix()).append(mimetype);
                arguments.add(builder.toString());
            }
        }
        if ((thumbnails = (String[])metaData.get(Arguments.THUMBNAILS.name(), String[].class)) != null) {
            for (String thumbnail : thumbnails) {
                StringBuilder builder = new StringBuilder();
                builder.append(Arguments.THUMBNAILS.getArgumentPrefix()).append(thumbnail);
                arguments.add(builder.toString());
            }
        }
        return arguments.toArray(new String[arguments.size()]);
    }

    protected void bindTagManagerFactory(JcrTagManagerFactory jcrTagManagerFactory) {
        this.tagManagerFactory = jcrTagManagerFactory;
    }

    protected void unbindTagManagerFactory(JcrTagManagerFactory jcrTagManagerFactory) {
        if (this.tagManagerFactory == jcrTagManagerFactory) {
            this.tagManagerFactory = null;
        }
    }

    public static enum Arguments {
        PROCESS_ARGS("PROCESS_ARGS"),
        MIME_TYPES("mime"),
        THUMBNAILS("tn"),
        COMMANDS("cmd");
        
        private String argumentName;

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

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

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

}