001/*
002 * (C) Copyright 2002-2015 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl-2.1.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Nuxeo - initial API and implementation
016 *
017 */
018
019package org.nuxeo.ecm.platform.convert.plugins;
020
021import java.io.Closeable;
022import java.io.IOException;
023import java.io.Serializable;
024import java.util.ArrayList;
025import java.util.HashMap;
026import java.util.List;
027import java.util.Map;
028
029import org.apache.commons.io.FilenameUtils;
030import org.apache.commons.io.IOUtils;
031import org.nuxeo.ecm.core.api.Blob;
032import org.nuxeo.ecm.core.api.CloseableFile;
033import org.nuxeo.ecm.core.api.blobholder.BlobHolder;
034import org.nuxeo.ecm.core.convert.api.ConversionException;
035import org.nuxeo.ecm.core.convert.api.ConverterCheckResult;
036import org.nuxeo.ecm.core.convert.extension.Converter;
037import org.nuxeo.ecm.core.convert.extension.ConverterDescriptor;
038import org.nuxeo.ecm.core.convert.extension.ExternalConverter;
039import org.nuxeo.ecm.platform.commandline.executor.api.CmdParameters;
040import org.nuxeo.ecm.platform.commandline.executor.api.CommandAvailability;
041import org.nuxeo.ecm.platform.commandline.executor.api.CommandException;
042import org.nuxeo.ecm.platform.commandline.executor.api.CommandLineExecutorService;
043import org.nuxeo.ecm.platform.commandline.executor.api.CommandNotAvailable;
044import org.nuxeo.ecm.platform.commandline.executor.api.ExecResult;
045import org.nuxeo.runtime.api.Framework;
046
047/**
048 * Base class to implement {@link Converter} based on {@link CommandLineExecutorService}.
049 *
050 * @author tiry
051 */
052public abstract class CommandLineBasedConverter implements ExternalConverter {
053
054    protected static final String CMD_NAME_PARAMETER = "CommandLineName";
055
056    protected static final String TMP_PATH_PARAMETER = "TmpDirectory";
057
058    protected Map<String, String> initParameters;
059
060    /**
061     * @deprecated Since 7.4. Useless.
062     */
063    @Deprecated
064    protected CommandLineExecutorService getCommandLineService() {
065        return Framework.getService(CommandLineExecutorService.class);
066    }
067
068    public String getTmpDirectory(Map<String, Serializable> parameters) {
069        String tmp = initParameters.get(TMP_PATH_PARAMETER);
070        if (parameters != null && parameters.containsKey(TMP_PATH_PARAMETER)) {
071            tmp = (String) parameters.get(TMP_PATH_PARAMETER);
072        }
073        if (tmp == null) {
074            tmp = System.getProperty("java.io.tmpdir");
075        }
076        return tmp;
077    }
078
079    @Override
080    public BlobHolder convert(BlobHolder blobHolder, Map<String, Serializable> parameters) throws ConversionException {
081        String commandName = getCommandName(blobHolder, parameters);
082        if (commandName == null) {
083            throw new ConversionException("Unable to determine target CommandLine name");
084        }
085
086        Map<String, Blob> blobParams = getCmdBlobParameters(blobHolder, parameters);
087        Map<String, String> strParams = getCmdStringParameters(blobHolder, parameters);
088
089        CmdReturn result = execOnBlob(commandName, blobParams, strParams);
090        return buildResult(result.output, result.params);
091    }
092
093    protected String getCommandName(BlobHolder blobHolder, Map<String, Serializable> parameters) {
094        String commandName = initParameters.get(CMD_NAME_PARAMETER);
095        if (parameters != null && parameters.containsKey(CMD_NAME_PARAMETER)) {
096            commandName = (String) parameters.get(CMD_NAME_PARAMETER);
097        }
098        return commandName;
099    }
100
101    /**
102     * Extracts BlobParameters.
103     */
104    protected abstract Map<String, Blob> getCmdBlobParameters(BlobHolder blobHolder,
105            Map<String, Serializable> parameters) throws ConversionException;
106
107    /**
108     * Extracts String parameters.
109     */
110    protected abstract Map<String, String> getCmdStringParameters(BlobHolder blobHolder,
111            Map<String, Serializable> parameters) throws ConversionException;
112
113    /**
114     * Builds result from commandLine output buffer.
115     */
116    protected abstract BlobHolder buildResult(List<String> cmdOutput, CmdParameters cmdParams)
117            throws ConversionException;
118
119    protected class CmdReturn {
120        protected final CmdParameters params;
121
122        protected final List<String> output;
123
124        protected CmdReturn(CmdParameters params, List<String> output) {
125            this.params = params;
126            this.output = output;
127        }
128    }
129
130    protected CmdReturn execOnBlob(String commandName, Map<String, Blob> blobParameters, Map<String, String> parameters)
131            throws ConversionException {
132        CommandLineExecutorService cles = Framework.getService(CommandLineExecutorService.class);
133        CmdParameters params = cles.getDefaultCmdParameters();
134        List<Closeable> toClose = new ArrayList<>();
135        try {
136            if (blobParameters != null) {
137                for (String blobParamName : blobParameters.keySet()) {
138                    Blob blob = blobParameters.get(blobParamName);
139                    // closed in finally block
140                    CloseableFile closeable = blob.getCloseableFile("."
141                            + FilenameUtils.getExtension(blob.getFilename()));
142                    params.addNamedParameter(blobParamName, closeable.getFile());
143                    toClose.add(closeable);
144                }
145            }
146
147            if (parameters != null) {
148                for (String paramName : parameters.keySet()) {
149                    params.addNamedParameter(paramName, parameters.get(paramName));
150                }
151            }
152
153            ExecResult result = Framework.getService(CommandLineExecutorService.class).execCommand(commandName, params);
154            if (!result.isSuccessful()) {
155                throw result.getError();
156            }
157            return new CmdReturn(params, result.getOutput());
158        } catch (CommandNotAvailable e) {
159            // XXX bubble installation instructions
160            throw new ConversionException("Unable to find targetCommand", e);
161        } catch (IOException | CommandException e) {
162            throw new ConversionException("Error while converting via CommandLineService", e);
163        } finally {
164            for (Closeable closeable : toClose) {
165                IOUtils.closeQuietly(closeable);
166            }
167        }
168    }
169
170    @Override
171    public void init(ConverterDescriptor descriptor) {
172        initParameters = descriptor.getParameters();
173        if (initParameters == null) {
174            initParameters = new HashMap<>();
175        }
176    }
177
178    @Override
179    public ConverterCheckResult isConverterAvailable() {
180        String commandName = getCommandName(null, null);
181        if (commandName == null) {
182            // can not check
183            return new ConverterCheckResult();
184        }
185
186        CommandAvailability ca = Framework.getService(CommandLineExecutorService.class).getCommandAvailability(
187                commandName);
188
189        if (ca.isAvailable()) {
190            return new ConverterCheckResult();
191        } else {
192            return new ConverterCheckResult(ca.getInstallMessage(), ca.getErrorMessage());
193        }
194    }
195
196}