All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.day.cq.dam.core.process.CommandLineProcess Maven / Gradle / Ivy

/*
 * Copyright 1997-2008 Day Management AG
 * Barfuesserplatz 6, 4001 Basel, Switzerland
 * All Rights Reserved.
 *
 * This software is the confidential and proprietary information of
 * Day Management AG, ("Confidential Information"). You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Day.
 */
package com.day.cq.dam.core.process;

import aQute.bnd.annotation.ProviderType;

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.renditions.RenditionMaker;
import com.day.cq.dam.api.renditions.RenditionTemplate;
import com.day.cq.dam.api.thumbnail.ThumbnailConfig;
import com.day.cq.dam.commons.process.AbstractAssetWorkflowProcess;
import com.day.cq.dam.commons.util.AssetUpdate;
import com.day.cq.dam.commons.util.AssetUpdateMonitor;
import com.day.cq.workflow.WorkflowException;
import com.day.cq.workflow.WorkflowSession;
import com.day.cq.workflow.exec.WorkItem;
import com.day.cq.workflow.metadata.MetaDataMap;

import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.apache.jackrabbit.util.Text;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jcr.RepositoryException;

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Workflow process that calls a command-line program and uses each output file
 * that it produces as an additional rendition. Optionally creates thumbnails
 * based on those renditions.
* The dimensions given in the thumbnail specifications are the maximal size the * result must have. Aspect ratio is maintained for image resizing. As a * precondition, the payload of this Workflow process has to be an * {@link com.day.cq.dam.api.Asset Asset} or part of an Asset. *

* Example with the following workflow step arguments: *

 *    mime:application/postscript,
 *    tn:140:100,tn:48:48,
 *    cmd:/bin/convert ${directory}/${filename} ${directory}/${basename}.jpg
 * 
*

* The process will call /bin/convert, pass it the full path of the asset being * processed (temporarily dumped to disk), and create thumbnails of size 140x100 * and 48x48 based on the output created by /bin/convert.
* This will only happen for assets having the * application/postscript mime-type, others are ignored. *

* Arguments: *

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
NamePrefixDescriptionRequiredMultipleExample
Commandcmd:Command as executed on the consle.
* The command can contain varibales which are replaced before execution. The * following variables are available:
* filename: name of the file as exported to disk.
* file: absolute path of the file exported.
* directory: absolute path of the directory the command is run and the * asset is exported to.
* basename: the assets name on the disk without possible file-extension
* extension: the assets file extension.
*
requiredmultiplecmd:/bin/convert ${directory}/${filename} ${directory}/${basename}.jpg
Mimetype Filtermime:Mimetype the Asset must have. If the Asset is of a diffrent type, this * process will not be executed.requiredmultiplemime:application/postscript
Thumbnail Specificationtn:Dimensions of the Thumbnails to be generated from the Asset.
* width:height[:false]
* width:Number the maximal width in Pixel the Thumbnail must not exceed * height:Number the maximal height in Pixel the Thumbnail must not * exceed. center:Boolean, optional flag to indicate that the resulting * Thumbnail must not be centered, true is default.
multipletn:140:100
* * @see AbstractAssetWorkflowProcess */ @Component(metatype = false) @Service @Property(name = "process.label", value = "Command Line") @ProviderType public class CommandLineProcess extends AbstractAssetWorkflowProcess { private final Logger log = LoggerFactory.getLogger(getClass()); /** * The available arguments to this process implementation. */ public enum Arguments { PROCESS_ARGS("PROCESS_ARGS"), /** Argument for the mime types to use */ MIME_TYPES("mime"), /** Argument for thumb nail sizes */ THUMBNAILS("tn"), /** Argument for command-line to execute */ COMMANDS("cmd"), /** Argument to generate web renditions **/ GENERATE_WEB_RENDITION("genWebRendition"), /** Argument to delete rendition generated by command prompt **/ DELETE_COMMAND_RENDITION("deleteCommandRendition"); private String argumentName; Arguments(String argumentName) { this.argumentName = argumentName; } public String getArgumentName() { return this.argumentName; } public String getArgumentPrefix() { return this.argumentName + ":"; } } @Reference protected RenditionMaker renditionMaker; @Reference private AssetUpdateMonitor monitor; private CreateWebEnabledImageProcess webEnabledImageCreator = new CreateWebEnabledImageProcess(); public void execute(final WorkItem workItem, WorkflowSession wfsession, final MetaDataMap args) throws WorkflowException { final AssetUpdate update = monitor.startUpdate(workItem, getResourceResolver(wfsession.getSession()), this); final String[] arguments = buildArguments(args); update.checkAndRun(new AssetUpdate.AssetCheck() { @Override public boolean isAcceptable(Asset asset) throws WorkflowException { // Process only specific mime types, based on arguments final List mimeTypes = new LinkedList(); final String assetMimeType = asset.getMimeType(); for (String str : arguments) { if (str.startsWith(Arguments.MIME_TYPES.getArgumentPrefix())) { final String mt = str.substring(Arguments.MIME_TYPES.getArgumentPrefix().length()).trim(); log.debug("execute: accepted mime type [{}] for asset [{}].", mt, 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.", assetMimeType, asset.getPath()); return false; } return true; } @Override public boolean isNullAcceptable() throws WorkflowException { String wfPayload = workItem.getWorkflowData().getPayload().toString(); String message = "execute: cannot extract metadata, create references and apply processing profile, asset [{" + wfPayload + "}] in payload doesn't exist for workflow [{" + workItem.getId() + "}]."; throw new WorkflowException(message); } }, new AssetUpdate.Runner() { @Override public void run(Asset asset, AssetUpdate update) throws WorkflowException, Exception { File tmpDir = null; InputStream is = null; OutputStream os = null; try { // creating temp directory tmpDir = File.createTempFile("cqdam", null); tmpDir.delete(); tmpDir.mkdir(); // make sure that tumbnails are not processed again, otherwise you // will end in a endless recursion final Rendition original = asset.getOriginal(); // streaming file to temp directory final File tmpFile = new File(tmpDir, Text.escape( asset.getName())); OutputStream fos = new FileOutputStream(tmpFile); InputStream in = null; try { in = original.getStream(); IOUtils.copy(in, fos); } finally { IOUtils.closeQuietly(is); IOUtils.closeQuietly(fos); } // building command line CommandLine commandLine; String lastLine = ""; Map parameters = new HashMap(); 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())) { // Execute command line final String cmd = argument.substring(Arguments.COMMANDS.getArgumentPrefix().length()) .trim(); commandLine = CommandLine.parse(cmd, parameters); lastLine = commandLine.toString(); DefaultExecutor exec = new DefaultExecutor(); exec.setWorkingDirectory(tmpDir); log.info("execute: executing command line [{}] for asset [{}].", lastLine, asset.getPath()); // No need to check the exit value, we get // an Exception from the executor if it's not 0 exec.execute(commandLine); } } } catch (Exception e) { log.error("execute: failed to execute command [{}] for asset [" + asset.getPath() + "]: ", lastLine, e); } // go over all result files. for (File result : tmpDir.listFiles(new FileFilter() { public boolean accept(File pathname) { // ignore the original file return !pathname.equals(tmpFile); } })) { // Stream output to rendition node final Rendition rendition = asset.addRendition(result.getName(), new FileInputStream(result), recheck(result.getName())); // Extract thumbnail dimensions from args final Set thumbnailConfigs = new HashSet(); for (final String str : arguments) { int indexOf = str.indexOf(Arguments.THUMBNAILS.getArgumentPrefix()); if (indexOf > -1) { final ThumbnailConfig config = CreateThumbnailProcess.parseThumbnailArguments(str.substring(indexOf + Arguments.THUMBNAILS.getArgumentPrefix().length())); if (null != config) { thumbnailConfigs.add(config); log.debug("execute: thumbnail dimensions [{}] for asset [{}].", str, asset.getPath()); } else { log.error("execute: cannot add invalid thumbnail config [{}] for asset [{}].", str, asset.getPath()); } } } List templates = createRenditionTemplates(rendition, thumbnailConfigs.toArray(new ThumbnailConfig[] {})); log.debug("thumbnail template created at [{}] with [{}] thumbnails for [" + asset.getPath() + "].", rendition.getPath(), templates.size()); Boolean createWebRend = args.get(Arguments.GENERATE_WEB_RENDITION.name(), Boolean.class); // create web-enabled rendition if flag is enabled if (createWebRend != null && createWebRend) { CreateWebEnabledImageProcess.Config config = webEnabledImageCreator.parseConfig(args); RenditionTemplate webRendTemp = renditionMaker.createWebRenditionTemplate(rendition, config.width, config.height, config.quality, config.mimeType, config.mimeTypesToKeep); templates.add(webRendTemp); log.debug("Web rendition template created at [{}] with [{}] thumbnails for [" + asset.getPath() + "].", rendition.getPath()); } // generate all thumbnail & web enabled renditions renditionMaker.generateRenditions(asset, templates.toArray(new RenditionTemplate[] {})); // if flag is enabled remove rendition generated by // command. Boolean delCommRend = args.get(Arguments.DELETE_COMMAND_RENDITION.name(), Boolean.class); if (delCommRend != null && delCommRend) { asset.removeRendition(rendition.getName()); } } } finally { // cleaning up temp directory IOUtils.closeQuietly(is); IOUtils.closeQuietly(os); if (tmpDir != null) { try { FileUtils.deleteDirectory(tmpDir); } catch (IOException e) { throw new WorkflowException(e); } } } } }); } protected void createThumbnails(final Asset asset, final Rendition rendition, final Collection configs) throws Exception { final String mimeType = rendition.getMimeType(); final AssetHandler handler = getAssetHandler(mimeType); if (handler == null) { throw new IOException("No AssetHandler found for mimetype " + mimeType); } log.debug("createThumbnails: generating thumbnails for rendition [{}] with mime type [{}]...", asset.getPath(), mimeType); handler.createThumbnails(asset, rendition, configs); } protected String recheck(String fileName) throws RepositoryException { if (mimeTypeService.getMimeType(fileName.toLowerCase()) != null) { return mimeTypeService.getMimeType(fileName.toLowerCase()); } return "application/octet-stream"; } public String[] buildArguments(MetaDataMap metaData) { // the 'old' way, ensures backward compatibility String processArgs = metaData.get(Arguments.PROCESS_ARGS.name(), String.class); if (processArgs != null && !processArgs.equals("")) { return processArgs.split(","); } else { List arguments = new ArrayList(); String[] commands = 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()); } } String[] mimetypes = metaData.get(Arguments.MIME_TYPES.name(), String[].class); if (mimetypes != null) { for (String mimetype : mimetypes) { StringBuilder builder = new StringBuilder(); builder.append(Arguments.MIME_TYPES.getArgumentPrefix()).append(mimetype); arguments.add(builder.toString()); } } String[] thumbnails = metaData.get(Arguments.THUMBNAILS.name(), String[].class); if (thumbnails != 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()]); } } private List createRenditionTemplates(Rendition rendition, ThumbnailConfig[] thumbnails) { List templates = new ArrayList(thumbnails.length); for (int i = 0; i < thumbnails.length; i++) { ThumbnailConfig thumb = thumbnails[i]; templates.add(renditionMaker.createThumbnailTemplate(rendition, thumb.getWidth(), thumb.getHeight(), thumb.doCenter())); } return templates; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy