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